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 | # aux files | ||||||
| *.pyc | *.pyc | ||||||
| *.swp | *.sw[mnop] | ||||||
| 
 | 
 | ||||||
| # secrets | # secrets | ||||||
| /django.secret | /django.secret | ||||||
|  |  | ||||||
|  | @ -13,11 +13,13 @@ Use git :-) | ||||||
| 
 | 
 | ||||||
| Quickstart | Quickstart | ||||||
| ---------- | ---------- | ||||||
| 
 | Run the following commands: | ||||||
| 	make install_venv | 	make install_venv | ||||||
| 	. env/bin/activate | 	. env/bin/activate | ||||||
| 	make install_web | 	make install_web | ||||||
| 
 | 
 | ||||||
|  | After finishing development, run "deactivate". | ||||||
|  | 
 | ||||||
| Make commands | Make commands | ||||||
| ------------- | ------------- | ||||||
| 
 | 
 | ||||||
|  | @ -56,6 +58,8 @@ Make commands | ||||||
| 
 | 
 | ||||||
| * `./manage.py test` - run the tests. | * `./manage.py test` - run the tests. | ||||||
| 
 | 
 | ||||||
|  | * `./manage.py shell` - run commands, list elemements of database, check syntax | ||||||
|  |   by importing files, etc. | ||||||
| 
 | 
 | ||||||
| Configurations | Configurations | ||||||
| -------------- | -------------- | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ from django.contrib import admin | ||||||
| from django.http import HttpResponseRedirect | from django.http import HttpResponseRedirect | ||||||
| from django import forms | from django import forms | ||||||
| from django.db import models | from django.db import models | ||||||
| from autocomplete_light import shortcuts as autocomplete_light |  | ||||||
| 
 | 
 | ||||||
| # akction | # akction | ||||||
| 
 | 
 | ||||||
|  | @ -39,11 +38,12 @@ class GalerieInline(admin.TabularInline): | ||||||
| 
 | 
 | ||||||
| class ObrazekAdmin(admin.ModelAdmin): | class ObrazekAdmin(admin.ModelAdmin): | ||||||
| 	list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag') | 	list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag') | ||||||
|  | 	search_fields = ['nazev','popis'] | ||||||
| 
 | 
 | ||||||
| class GalerieAdmin(admin.ModelAdmin): | class GalerieAdmin(admin.ModelAdmin): | ||||||
| 	form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek']) |  | ||||||
| 	model = Galerie | 	model = Galerie | ||||||
| 	fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') | 	fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') | ||||||
|  | 	autocomplete_fields = ['titulni_obrazek'] | ||||||
| 	list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny') | 	list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny') | ||||||
| 	inlines = [GalerieInline] | 	inlines = [GalerieInline] | ||||||
| 	actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] | 	actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ AUTHENTICATION_BACKENDS = ( | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| MIDDLEWARE = ( | MIDDLEWARE = ( | ||||||
|     'reversion.middleware.RevisionMiddleware', | #    'reversion.middleware.RevisionMiddleware', | ||||||
|     'django.contrib.sessions.middleware.SessionMiddleware', |     'django.contrib.sessions.middleware.SessionMiddleware', | ||||||
|     'django.middleware.common.CommonMiddleware', |     'django.middleware.common.CommonMiddleware', | ||||||
|     'django.middleware.csrf.CsrfViewMiddleware', |     'django.middleware.csrf.CsrfViewMiddleware', | ||||||
|  | @ -98,13 +98,14 @@ INSTALLED_APPS = ( | ||||||
| 
 | 
 | ||||||
|     # Utilities |     # Utilities | ||||||
|     'sekizai', |     'sekizai', | ||||||
|     'reversion', | #    'reversion', | ||||||
|     'django_countries', |     'django_countries', | ||||||
|     'solo', |     'solo', | ||||||
|     'ckeditor', |     'ckeditor', | ||||||
|     'ckeditor_uploader', |     'ckeditor_uploader', | ||||||
|     'taggit', |     'taggit', | ||||||
|     'autocomplete_light', |     'dal', | ||||||
|  |     'dal_select2', | ||||||
| 
 | 
 | ||||||
|     'fluent_comments', |     'fluent_comments', | ||||||
|     'crispy_forms', |     'crispy_forms', | ||||||
|  | @ -118,6 +119,8 @@ INSTALLED_APPS = ( | ||||||
| 
 | 
 | ||||||
|     'imagekit', |     'imagekit', | ||||||
| 
 | 
 | ||||||
|  |     'polymorphic', | ||||||
|  | 
 | ||||||
|     # MaMweb |     # MaMweb | ||||||
|     'mamweb', |     'mamweb', | ||||||
|     'seminar', |     'seminar', | ||||||
|  | @ -215,6 +218,14 @@ LOGGING = { | ||||||
|                 'handlers': ['console'], |                 'handlers': ['console'], | ||||||
|                 'level': 'DEBUG', |                 '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 |             # Catch-all logger | ||||||
|             '': { |             '': { | ||||||
|  | @ -237,6 +248,24 @@ LOGGING = { | ||||||
|                 'class': 'django.utils.log.AdminEmailHandler', |                 'class': 'django.utils.log.AdminEmailHandler', | ||||||
|                 'formatter': 'verbose',  |                 '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'], |          #    'handlers': ['console'], | ||||||
|          #    'propagate': False, |          #    'propagate': False, | ||||||
|          #}, |          #}, | ||||||
|  |         'werkzeug': { | ||||||
|  |         	'handlers': ['console'], | ||||||
|  |         	'level': 'DEBUG', | ||||||
|  |         	'propagate': True, | ||||||
|  |         }, | ||||||
|         '': { |         '': { | ||||||
|             'handlers': ['console'], |             'handlers': ['console'], | ||||||
|             'level': 'DEBUG', |             'level': 'DEBUG', | ||||||
|  | @ -88,3 +93,5 @@ LOGGING = { | ||||||
| 
 | 
 | ||||||
| # set to 'DEBUG' for EXTRA verbose output | # set to 'DEBUG' for EXTRA verbose output | ||||||
| # LOGGING['handlers']['console']['level'] = 'INFO' | # 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']['']['handlers'] = ['console', 'mail_admins'] | ||||||
| LOGGING['loggers']['django']['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 | # E-MAIL NOTIFICATIONS | ||||||
|  |  | ||||||
|  | @ -65,3 +65,5 @@ CSRF_COOKIE_SECURE = True | ||||||
| 
 | 
 | ||||||
| LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins'] | LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins'] | ||||||
| LOGGING['loggers']['django']['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 { | div.nahledy_cisel div, div.nahledy_cisel img { | ||||||
|     position: absolute; |     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 %} | {% block extrahead %} | ||||||
| <link rel="shortcut icon" href="{% static 'favicon.ico' %}" type="image/x-icon"> | <link rel="shortcut icon" href="{% static 'favicon.ico' %}" type="image/x-icon"> | ||||||
| <script src="{% static 'js/jquery-1.11.1.js' %}"></script> | <script src="{% static 'js/jquery-1.11.1.js' %}"></script> | ||||||
| {% include 'autocomplete_light/static.html' %} |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
| 
 | 
 | ||||||
| {% block branding %} | {% block branding %} | ||||||
|  |  | ||||||
|  | @ -27,8 +27,8 @@ | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|     </script> |     </script> | ||||||
|     <script type="text/javascript" |     <script type="text/javascript" async | ||||||
|       src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> |       src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> | ||||||
|     </script> |     </script> | ||||||
| 
 | 
 | ||||||
|     {# script specifický pro stránku #} |     {# script specifický pro stránku #} | ||||||
|  | @ -97,7 +97,27 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	<div class='row content'> | 	<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 %} | 	    {% block content %} | ||||||
| 	    {% endblock content %} | 	    {% endblock content %} | ||||||
| 	  </div> | 	  </div> | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ urlpatterns = [ | ||||||
| 	# Admin a nastroje | 	# Admin a nastroje | ||||||
| 	path('admin/', admin.site.urls),  # NOQA | 	path('admin/', admin.site.urls),  # NOQA | ||||||
| 	path('ckeditor/', include('ckeditor_uploader.urls')), | 	path('ckeditor/', include('ckeditor_uploader.urls')), | ||||||
| 	path('autocomplete/', include('autocomplete_light.urls')), |  | ||||||
| 
 | 
 | ||||||
| 	# Seminarova aplikace (ma vlastni podadresare) | 	# Seminarova aplikace (ma vlastni podadresare) | ||||||
| 	path('', include('seminar.urls')), | 	path('', include('seminar.urls')), | ||||||
|  |  | ||||||
|  | @ -22,11 +22,12 @@ django-solo | ||||||
| django-ckeditor | django-ckeditor | ||||||
| django-flat-theme | django-flat-theme | ||||||
| django-taggit | django-taggit | ||||||
| django-autocomplete-light==2.3.6 | django-autocomplete-light | ||||||
| django-crispy-forms | django-crispy-forms | ||||||
| django-imagekit | django-imagekit | ||||||
| django-polymorphic | django-polymorphic | ||||||
| django-sitetree | django-sitetree | ||||||
|  | django_reverse_admin | ||||||
| 
 | 
 | ||||||
| # Comments | # Comments | ||||||
| akismet==1.0.1 | akismet==1.0.1 | ||||||
|  |  | ||||||
							
								
								
									
										173
									
								
								seminar/admin.py
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								seminar/admin.py
									
									
									
									
									
								
							|  | @ -1,36 +1,171 @@ | ||||||
| from django.contrib import admin | 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 | import seminar.models as m | ||||||
| 
 | 
 | ||||||
| admin.site.register(m.Osoba) |  | ||||||
| admin.site.register(m.Skola) | admin.site.register(m.Skola) | ||||||
| admin.site.register(m.Prijemce) | admin.site.register(m.Prijemce) | ||||||
| admin.site.register(m.Resitel) |  | ||||||
| admin.site.register(m.Rocnik) | admin.site.register(m.Rocnik) | ||||||
| admin.site.register(m.Cislo) | admin.site.register(m.Cislo) | ||||||
| admin.site.register(m.Organizator) | admin.site.register(m.Organizator) | ||||||
| admin.site.register(m.Soustredeni) | admin.site.register(m.Soustredeni) | ||||||
| admin.site.register(m.Problem) | 
 | ||||||
| admin.site.register(m.Tema) | @admin.register(m.Osoba) | ||||||
| admin.site.register(m.Clanek) | 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.Text) | ||||||
| admin.site.register(m.Uloha) | 
 | ||||||
| admin.site.register(m.Reseni) | class ResitelInline(admin.TabularInline): | ||||||
| admin.site.register(m.Hodnoceni) | 	model = m.Resitel | ||||||
|  | 	extra = 1 | ||||||
|  | admin.site.register(m.Resitel) | ||||||
|  | 
 | ||||||
|  | class PrilohaReseniInline(admin.TabularInline): | ||||||
|  | 	model = m.PrilohaReseni | ||||||
|  | 	extra = 1 | ||||||
| admin.site.register(m.PrilohaReseni) | 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.Pohadka) | ||||||
| admin.site.register(m.Konfera) | admin.site.register(m.Konfera) | ||||||
| admin.site.register(m.Obrazek) | admin.site.register(m.Obrazek) | ||||||
| admin.site.register(m.TreeNode) | 
 | ||||||
| admin.site.register(m.RocnikNode) | 
 | ||||||
| admin.site.register(m.CisloNode) | # Polymorfismus pro stromy | ||||||
| admin.site.register(m.MezicisloNode) | # TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html | ||||||
| admin.site.register(m.TemaVCisleNode) | 
 | ||||||
| admin.site.register(m.KonferaNode) | @admin.register(m.TreeNode) | ||||||
| admin.site.register(m.ClanekNode) | class TreeNodeAdmin(PolymorphicParentModelAdmin): | ||||||
| admin.site.register(m.UlohaZadaniNode) | 	base_model = m.TreeNode | ||||||
| admin.site.register(m.PohadkaNode) | 	child_models = [ | ||||||
| admin.site.register(m.UlohaVzorakNode) | 		m.RocnikNode, | ||||||
| admin.site.register(m.TextNode) | 		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.Nastaveni) | ||||||
| admin.site.register(m.Novinky) | admin.site.register(m.Novinky) | ||||||
|  |  | ||||||
							
								
								
									
										255
									
								
								seminar/forms.py
									
									
									
									
									
								
							
							
						
						
									
										255
									
								
								seminar/forms.py
									
									
									
									
									
								
							|  | @ -1,6 +1,257 @@ | ||||||
| from django import forms | 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): | class Command(BaseCommand): | ||||||
|     help = "Clear database and load testing data." | 	help = "Clear database and load testing data." | ||||||
| 
 | 
 | ||||||
|     def handle(self, *args, **options): | 	def add_arguments(self, parser): | ||||||
|         assert settings.DEBUG == True | 		parser.add_argument( | ||||||
|         dbfile = settings.DATABASES['default']['NAME'] | 			'--no-clean', | ||||||
|         if os.path.exists(dbfile): | 			action='store_true', | ||||||
|             os.rename(dbfile, dbfile + '.old') | 			help='Změny se provedou v aktuální DB, ne v čisté. Aktuální DB se nezachová. (jen k debugování)', | ||||||
|             self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old')) | 			) | ||||||
|         call_command('migrate', no_input=True) | 		parser.add_argument( | ||||||
|         self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') | 			'--no-migrate', | ||||||
|         create_test_data(size=8) | 			action='store_true', | ||||||
|         self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'  | 			help='Neprovádět migrace před generováním testovacích dat (jen k debugování)', | ||||||
|                ' {} 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 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 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): | class SeminarModelBase(models.Model): | ||||||
| 
 | 
 | ||||||
|  | @ -129,6 +130,17 @@ class Osoba(SeminarModelBase): | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return self.plne_jmeno() | 		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 | # Mělo by být částečně vytaženo z Aesopa | ||||||
| # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. | # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. | ||||||
|  | @ -352,7 +364,7 @@ class Rocnik(SeminarModelBase): | ||||||
| 	 | 	 | ||||||
| 	def verejna_cisla(self): | 	def verejna_cisla(self): | ||||||
| 		vc = [c for c in self.cisla.all() if c.verejne()] | 		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 | 		return vc | ||||||
| 
 | 
 | ||||||
| 	def posledni_verejne_cislo(self): | 	def posledni_verejne_cislo(self): | ||||||
|  | @ -361,7 +373,7 @@ class Rocnik(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 	def verejne_vysledkovky_cisla(self): | 	def verejne_vysledkovky_cisla(self): | ||||||
| 		vc = list(self.cisla.filter(verejna_vysledkovka=True)) | 		vc = list(self.cisla.filter(verejna_vysledkovka=True)) | ||||||
| 		vc.sort(key=lambda c: c.cislo) | 		vc.sort(key=lambda c: c.poradi) | ||||||
| 		return vc | 		return vc | ||||||
| 
 | 
 | ||||||
| 	def posledni_zverejnena_vysledkovka_cislo(self): | 	def posledni_zverejnena_vysledkovka_cislo(self): | ||||||
|  | @ -383,10 +395,18 @@ class Rocnik(SeminarModelBase): | ||||||
| 			cache.set(name, c, 300) | 			cache.set(name, c, 300) | ||||||
| 		return c | 		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): | def cislo_pdf_filename(self, filename): | ||||||
| 	rocnik = str(self.rocnik.rocnik) | 	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) | @reversion.register(ignore_duplicates=True) | ||||||
| class Cislo(SeminarModelBase): | class Cislo(SeminarModelBase): | ||||||
|  | @ -395,7 +415,7 @@ class Cislo(SeminarModelBase): | ||||||
| 		db_table = 'seminar_cisla' | 		db_table = 'seminar_cisla' | ||||||
| 		verbose_name = 'Číslo' | 		verbose_name = 'Číslo' | ||||||
| 		verbose_name_plural = 'Čísla' | 		verbose_name_plural = 'Čísla' | ||||||
| 		ordering = ['-rocnik__rocnik', '-cislo'] | 		ordering = ['-rocnik__rocnik', '-poradi'] | ||||||
| 
 | 
 | ||||||
| 	# Interní ID | 	# Interní ID | ||||||
| 	id = models.AutoField(primary_key = True) | 	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', | 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla', | ||||||
| 		db_index=True,on_delete=models.PROTECT) | 		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!') | 		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, | 	datum_vydani = models.DateField('datum vydání', blank=True, null=True, | ||||||
|  | @ -436,20 +456,20 @@ class Cislo(SeminarModelBase): | ||||||
| 	# CisloNode | 	# CisloNode | ||||||
| 
 | 
 | ||||||
| 	def kod(self): | 	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' | 	kod.short_description = 'Kód čísla' | ||||||
| 
 | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		# Potenciální DB HOG, pokud by se ročník necachoval | 		# Potenciální DB HOG, pokud by se ročník necachoval | ||||||
| 		r = Rocnik.cached_rocnik(self.rocnik_id) | 		r = Rocnik.cached_rocnik(self.rocnik_id) | ||||||
| 		return '{}.{}'.format(r.rocnik, self.cislo) | 		return '{}.{}'.format(r.rocnik, self.poradi) | ||||||
| 
 | 
 | ||||||
| 	def verejne(self): | 	def verejne(self): | ||||||
| 		return self.verejne_db | 		return self.verejne_db | ||||||
| 	verejne.boolean = True | 	verejne.boolean = True | ||||||
| 
 | 
 | ||||||
| 	def verejne_url(self): | 	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): | 	def nasledujici(self): | ||||||
| 		"Vrací None, pokud je toto poslední" | 		"Vrací None, pokud je toto poslední" | ||||||
|  | @ -471,11 +491,20 @@ class Cislo(SeminarModelBase): | ||||||
| 	def get(cls, rocnik, cislo): | 	def get(cls, rocnik, cislo): | ||||||
| 		try: | 		try: | ||||||
| 			r = Rocnik.objects.get(rocnik=rocnik) | 			r = Rocnik.objects.get(rocnik=rocnik) | ||||||
| 			c = r.cisla.get(cislo=cislo) | 			c = r.cisla.get(poradi=cislo) | ||||||
| 		except ObjectDoesNotExist: | 		except ObjectDoesNotExist: | ||||||
| 			return None | 			return None | ||||||
| 		return c | 		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) | @reversion.register(ignore_duplicates=True) | ||||||
| class Organizator(SeminarModelBase): | class Organizator(SeminarModelBase): | ||||||
| # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu | # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu | ||||||
|  | @ -583,7 +612,8 @@ class Soustredeni(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @reversion.register(ignore_duplicates=True) | @reversion.register(ignore_duplicates=True) | ||||||
| class Problem(SeminarModelBase): | # Pozor na následující řádek. *Nekrmit, asi kouše!* | ||||||
|  | class Problem(SeminarModelBase,PolymorphicModel): | ||||||
| 
 | 
 | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		# Není abstraktní, protože se na něj jinak nedají dělat ForeignKeys. | 		# 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) | 	id = models.AutoField(primary_key = True) | ||||||
| 
 | 
 | ||||||
| 	# Název | 	# 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 | 	# Problém má podproblémy | ||||||
| 	nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém', | 	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) | 		on_delete=models.SET_NULL) | ||||||
| 
 | 
 | ||||||
| 	STAV_NAVRH = 'navrh' | 	STAV_NAVRH = 'navrh' | ||||||
|  | @ -698,6 +728,9 @@ class Tema(Problem): | ||||||
| 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',blank=True, null=True, | 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',blank=True, null=True, | ||||||
| 		on_delete=models.PROTECT) | 		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): | 	def kod_v_rocniku(self): | ||||||
| 		if self.stav == 'zadany': | 		if self.stav == 'zadany': | ||||||
| 			if self.nadproblem: | 			if self.nadproblem: | ||||||
|  | @ -705,6 +738,12 @@ class Tema(Problem): | ||||||
| 			return "t{}".format(self.kod) | 			return "t{}".format(self.kod) | ||||||
| 		return '<Není zadaný>' | 		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 Clanek(Problem): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_clanky' | 		db_table = 'seminar_clanky' | ||||||
|  | @ -725,6 +764,15 @@ class Clanek(Problem): | ||||||
| 			return "c{}".format(self.kod) | 			return "c{}".format(self.kod) | ||||||
| 		return '<Není zadaný>' | 		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 Text(SeminarModelBase): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_texty' | 		db_table = 'seminar_texty' | ||||||
|  | @ -742,8 +790,17 @@ class Text(SeminarModelBase): | ||||||
| 	 | 	 | ||||||
| 	# obrázky mají návaznost opačným směrem (vazba z druhé strany) | 	# 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 Uloha(Problem): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_ulohy' | 		db_table = 'seminar_ulohy' | ||||||
|  | @ -770,12 +827,26 @@ class Uloha(Problem): | ||||||
| 
 | 
 | ||||||
| 	def kod_v_rocniku(self): | 	def kod_v_rocniku(self): | ||||||
| 		if self.stav == 'zadany': | 		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: | 			if self.nadproblem: | ||||||
| 				return self.nadproblem.kod_v_rocniku()+name | 				return self.nadproblem.kod_v_rocniku()+name | ||||||
| 			return name | 			return name | ||||||
| 		return '<Není zadaný>' | 		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) | @reversion.register(ignore_duplicates=True) | ||||||
| class Reseni(SeminarModelBase): | class Reseni(SeminarModelBase): | ||||||
|  | @ -828,7 +899,7 @@ class Reseni(SeminarModelBase): | ||||||
| 	# Konfera | 	# Konfera | ||||||
| 
 | 
 | ||||||
| 	def __str__(self): | 	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) | 		# NOTE: Potenciální DB HOG (bez select_related) | ||||||
| 
 | 
 | ||||||
| ## Pravdepodobne uz nebude potreba: | ## Pravdepodobne uz nebude potreba: | ||||||
|  | @ -856,7 +927,8 @@ class Hodnoceni(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | 	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): | 	def __str__(self): | ||||||
| 		return "{}, {}, {}".format(self.problem, self.reseni, self.body) | 		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)]+"..." | 		uryvek = self.text if len(self.text) < 50 else self.text[:(50-3)]+"..." | ||||||
| 		return uryvek | 		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) | @reversion.register(ignore_duplicates=True) | ||||||
| class Soustredeni_Ucastnici(SeminarModelBase): | class Soustredeni_Ucastnici(SeminarModelBase): | ||||||
|  | @ -1020,7 +1100,7 @@ class Konfera(models.Model): | ||||||
| 	# Interní ID | 	# Interní ID | ||||||
| 	id = models.AutoField(primary_key = True) | 	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, | 	anotace = models.TextField('anotace', blank=True, | ||||||
| 		help_text='Popis, o čem bude konfera.') | 		help_text='Popis, o čem bude konfera.') | ||||||
|  | @ -1067,6 +1147,15 @@ class Konfera(models.Model): | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return "{}: ({})".format(self.nazev, self.soustredeni) | 		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. | # Vazebna tabulka. Mozna se generuje automaticky. | ||||||
| @reversion.register(ignore_duplicates=True) | @reversion.register(ignore_duplicates=True) | ||||||
|  | @ -1139,12 +1228,13 @@ class Obrazek(SeminarModelBase): | ||||||
| 			help_text = 'Černobílá verze obrázku do čísla',  | 			help_text = 'Černobílá verze obrázku do čísla',  | ||||||
| 			upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True) | 			upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True) | ||||||
| 
 | 
 | ||||||
| class TreeNode(models.Model): | class TreeNode(PolymorphicModel): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = "seminar_nodes_treenode" | 		db_table = "seminar_nodes_treenode" | ||||||
| 		verbose_name = "TreeNode" | 		verbose_name = "TreeNode" | ||||||
| 		verbose_name_plural = "TreeNody" | 		verbose_name_plural = "TreeNody" | ||||||
| 
 | 
 | ||||||
|  | 	# TODO: Nechceme radši jako root vyžadovat přímo RocnikNode? | ||||||
| 	root = models.ForeignKey('TreeNode', | 	root = models.ForeignKey('TreeNode', | ||||||
| 		related_name="potomci_set", | 		related_name="potomci_set", | ||||||
| 		null = True, | 		null = True, | ||||||
|  | @ -1162,14 +1252,59 @@ class TreeNode(models.Model): | ||||||
| 		blank = True, | 		blank = True, | ||||||
| 		on_delete=models.SET_NULL, | 		on_delete=models.SET_NULL, | ||||||
| 		verbose_name="další element na stejné úrovni") | 		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): | 	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: | 		if self.first_child: | ||||||
| 			self.first_child.print_tree(indent=indent+2) | 			self.first_child.print_tree(indent=indent+2) | ||||||
| 		if self.succ: | 		if self.succ: | ||||||
| 			self.succ.print_tree(indent=indent) | 			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 RocnikNode(TreeNode): | ||||||
| 	class Meta: | 	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ě | 		on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně | ||||||
| 		verbose_name = "ročník") | 		verbose_name = "ročník") | ||||||
| 
 | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "RocnikNode: "+str(self.rocnik) | ||||||
|  | 
 | ||||||
| class CisloNode(TreeNode): | class CisloNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_cislo' | 		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ě | 		on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně | ||||||
| 		verbose_name = "číslo") | 		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 MezicisloNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_mezicislo' | 		db_table = 'seminar_nodes_mezicislo' | ||||||
| 		verbose_name = 'Mezičíslo (Node)' | 		verbose_name = 'Mezičíslo (Node)' | ||||||
| 		verbose_name_plural = 'Mezičísla (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): | class TemaVCisleNode(TreeNode): | ||||||
| 	""" Obsahuje příspěvky k tématu v daném čísle """ | 	""" Obsahuje příspěvky k tématu v daném čísle """ | ||||||
| 	class Meta: | 	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ě | 		on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně | ||||||
| 		verbose_name = "téma v čísle") | 		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 KonferaNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_konfera' | 		db_table = 'seminar_nodes_konfera' | ||||||
|  | @ -1216,6 +1384,9 @@ class KonferaNode(TreeNode): | ||||||
| 		null=True, | 		null=True, | ||||||
| 		blank=False) | 		blank=False) | ||||||
| 
 | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "KonferaNode: "+str(self.konfera) | ||||||
|  | 
 | ||||||
| class ClanekNode(TreeNode): | class ClanekNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_clanek' | 		db_table = 'seminar_nodes_clanek' | ||||||
|  | @ -1227,6 +1398,13 @@ class ClanekNode(TreeNode): | ||||||
| 		null=True, | 		null=True, | ||||||
| 		blank=False) | 		blank=False) | ||||||
| 
 | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "ClanekNode: "+str(self.clanek) | ||||||
|  | 
 | ||||||
|  | 	def getOdkazStr(self): | ||||||
|  | 		return str(self.clanek) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class UlohaZadaniNode(TreeNode): | class UlohaZadaniNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_uloha_zadani' | 		db_table = 'seminar_nodes_uloha_zadani' | ||||||
|  | @ -1238,6 +1416,13 @@ class UlohaZadaniNode(TreeNode): | ||||||
| 		null=True, | 		null=True, | ||||||
| 		blank=False) | 		blank=False) | ||||||
| 
 | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "UlohaZadaniNode: "+str(self.uloha) | ||||||
|  | 
 | ||||||
|  | 	def getOdkazStr(self): | ||||||
|  | 		return str(self.uloha) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class PohadkaNode(TreeNode): | class PohadkaNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_pohadka' | 		db_table = 'seminar_nodes_pohadka' | ||||||
|  | @ -1248,6 +1433,9 @@ class PohadkaNode(TreeNode): | ||||||
| 		verbose_name = "pohádka", | 		verbose_name = "pohádka", | ||||||
| 		) | 		) | ||||||
| 
 | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "PohadkaNode: "+str(self.pohadka) | ||||||
|  | 
 | ||||||
| class UlohaVzorakNode(TreeNode): | class UlohaVzorakNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_uloha_vzorak' | 		db_table = 'seminar_nodes_uloha_vzorak' | ||||||
|  | @ -1259,6 +1447,13 @@ class UlohaVzorakNode(TreeNode): | ||||||
| 		null=True, | 		null=True, | ||||||
| 		blank=False) | 		blank=False) | ||||||
| 
 | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "UlohaVzorakNode: "+str(self.uloha) | ||||||
|  | 
 | ||||||
|  | 	def getOdkazStr(self): | ||||||
|  | 		return str(self.uloha) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class TextNode(TreeNode): | class TextNode(TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = 'seminar_nodes_obsah' | 		db_table = 'seminar_nodes_obsah' | ||||||
|  | @ -1267,6 +1462,13 @@ class TextNode(TreeNode): | ||||||
| 	text = models.ForeignKey(Text, | 	text = models.ForeignKey(Text, | ||||||
| 		on_delete=models.PROTECT, | 		on_delete=models.PROTECT, | ||||||
| 		verbose_name = 'text') | 		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. | ## FIXME: Logiku přesunout do views. | ||||||
| #class VysledkyBase(SeminarModelBase): | #class VysledkyBase(SeminarModelBase): | ||||||
|  | @ -1292,7 +1494,7 @@ class TextNode(TreeNode): | ||||||
| # | # | ||||||
| #	def __str__(self): | #	def __str__(self): | ||||||
| #		return "%s: %sb (%s)".format(self.resitel.plne_jmeno(), self.body,  | #		return "%s: %sb (%s)".format(self.resitel.plne_jmeno(), self.body,  | ||||||
| #		str(self.cislo)) | #		str(self.poradi)) | ||||||
| #		# NOTE: DB zatez pri vypisu (ale nepouzivany) | #		# NOTE: DB zatez pri vypisu (ale nepouzivany) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1342,7 +1544,7 @@ class TextNode(TreeNode): | ||||||
| # | # | ||||||
| #	def __str__(self): | #	def __str__(self): | ||||||
| #		# NOTE: DB HOG (ale nepouzivany) | #		# 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 | ##mozna potreba upravit | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1353,12 +1555,16 @@ class Nastaveni(SingletonModel): | ||||||
| 		db_table = 'seminar_nastaveni' | 		db_table = 'seminar_nastaveni' | ||||||
| 		verbose_name = 'Nastavení semináře' | 		verbose_name = 'Nastavení semináře' | ||||||
| 
 | 
 | ||||||
| 	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník', | #	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník', | ||||||
| 		null=False, on_delete=models.PROTECT) | #		null=False, on_delete=models.PROTECT) | ||||||
| 
 | 
 | ||||||
| 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='poslední vydané číslo',  | 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='poslední vydané číslo',  | ||||||
| 		null=False, on_delete=models.PROTECT) | 		null=False, on_delete=models.PROTECT) | ||||||
| 
 | 
 | ||||||
|  | 	@property | ||||||
|  | 	def aktualni_rocnik(self): | ||||||
|  | 		return self.aktualni_cislo.rocnik | ||||||
|  | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return 'Nastavení semináře' | 		return 'Nastavení semináře' | ||||||
| 
 | 
 | ||||||
|  | @ -1399,3 +1605,35 @@ class Novinky(models.Model): | ||||||
| 			return '[' + str(self.datum) + '] ' + self.text[0:50] | 			return '[' + str(self.datum) + '] ' + self.text[0:50] | ||||||
| 		else: | 		else: | ||||||
| 			return '[' + str(self.datum) + '] ' | 			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 | % Tohle makro vysází samotnou obálku | ||||||
| \def\obalka#1#2#3#4#5#6#7{ | \def\obalka#1#2#3#4#5#6#7{ | ||||||
| % Horní a pravý okraj je zároveň okraj stránky, resetujeme odsazení | % 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} | \adresaMaM} | ||||||
| \vskip 7.3 cm % Od oka | \vskip 7.3 cm % Od oka | ||||||
| \hskip\toskip% | \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"> | {% extends "seminar/zadani/base.html" %} | ||||||
|     {% csrf_token %} | {% load staticfiles %} | ||||||
|     {{ form }} | 
 | ||||||
|     <input type="submit" value="Submit"> | {% 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> | </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 -*- | # -*- coding: utf-8 -*- | ||||||
| 
 | 
 | ||||||
| import datetime | import datetime | ||||||
|  | from pytz import timezone | ||||||
| import random | import random | ||||||
| import lorem | import lorem | ||||||
| import django.contrib.auth | import django.contrib.auth | ||||||
| from django.db import transaction | from django.db import transaction | ||||||
| import unidecode | 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 | 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() | User = django.contrib.auth.get_user_model() | ||||||
| zlinska = None # tohle bude speciální škola, které později dodáme kontaktní osobu | zlinska = None # tohle bude speciální škola, které později dodáme kontaktní osobu | ||||||
| 
 | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
| def gen_osoby(rnd, size): | def gen_osoby(rnd, size): | ||||||
|  | 	logger.info('Generuji osoby (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
| 	jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel'] | 	jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel'] | ||||||
| 	jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', | 	jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', | ||||||
| 		'Marta Iva', 'Shu Shan'] | 		'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 | def gen_skoly(): #TODO někdy to přepsat, aby jich bylo více | ||||||
|  | 	logger.info('Generuji školy...') | ||||||
|  | 
 | ||||||
| 	skoly = [] | 	skoly = [] | ||||||
| 	prvnizs = Skola.objects.create(mesto='Praha', stat='CZ', psc='101 00', | 	prvnizs = Skola.objects.create(mesto='Praha', stat='CZ', psc='101 00', | ||||||
| 		ulice='Krátká 5', nazev='První ZŠ', je_zs=True, je_ss=False) | 		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 | 	return skoly | ||||||
| 
 | 
 | ||||||
| def gen_resitele(rnd, osoby, skoly): | def gen_resitele(rnd, osoby, skoly): | ||||||
|  | 	logger.info('Generuji řešitele...') | ||||||
|  | 
 | ||||||
| 	resitele = [] | 	resitele = [] | ||||||
| 	for os in osoby: | 	for os in osoby: | ||||||
| 		rand = rnd.randint(0, 8) | 		rand = rnd.randint(0, 8) | ||||||
| 		if not (rand % 8 == 0): | 		if not (rand % 8 == 0): | ||||||
| 			resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly), | 			resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly), | ||||||
| 				rok_maturity=rnd.randint(2019, 2029), | 				rok_maturity=rnd.randint(2019, 2029), | ||||||
| 				zasilat=rnd.choice(Resitel.ZASILAT_CHOICES))) | 				zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0])) | ||||||
| 	return resitele | 	return resitele | ||||||
| 
 | 
 | ||||||
| def gen_prijemci(rnd, osoby, kolik=10): | def gen_prijemci(rnd, osoby, kolik=10): | ||||||
|  | 	logger.info('Generuji příjemce (kolik={})...'.format(kolik)) | ||||||
| 	prijemci = [] | 	prijemci = [] | ||||||
| 	for i in rnd.sample(osoby, kolik): | 	for i in rnd.sample(osoby, kolik): | ||||||
| 		prijemci.append(Prijemce.objects.create(osoba=i)) | 		prijemci.append(Prijemce.objects.create(osoba=i)) | ||||||
| 	return prijemci | 	return prijemci | ||||||
| 
 | 
 | ||||||
| def gen_organizatori(rnd, osoby, last_rocnik, users): | def gen_organizatori(rnd, osoby, last_rocnik, users): | ||||||
|  | 	logger.info('Generuji organizátory...') | ||||||
| 	organizatori = [] | 	organizatori = [] | ||||||
| 
 | 
 | ||||||
| 	 | 	 | ||||||
|  | @ -118,9 +130,18 @@ def gen_organizatori(rnd, osoby, last_rocnik, users): | ||||||
| 		rand = rnd.randint(0, 8) | 		rand = rnd.randint(0, 8) | ||||||
| 		if (rand % 8 == 0): | 		if (rand % 8 == 0): | ||||||
| 			pusobnost = rnd.randint(1, last_rocnik) | 			pusobnost = rnd.randint(1, last_rocnik) | ||||||
| 			od = datetime.date(1993 + pusobnost, rnd.randint(1, 12), rnd.randint(1, 28)) | 			od = datetime.datetime( | ||||||
| 			do = datetime.date(od.year + rnd.randint(1, 6), rnd.randint(1, 12), | 				year=1993 + pusobnost, | ||||||
| 				rnd.randint(1, 28)) | 				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 | 			#aktualni organizatori jeste nemaji vyplnene organizuje_do | ||||||
| 
 | 
 | ||||||
| 			#popis orga | 			#popis orga | ||||||
|  | @ -136,6 +157,8 @@ def gen_organizatori(rnd, osoby, last_rocnik, users): | ||||||
| 	return organizatori | 	return organizatori | ||||||
| 
 | 
 | ||||||
| def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size): | 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 | 	# ulohy resene v cisle | ||||||
| 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | ||||||
| 		"Zákeřná", "Fyzikální"] | 		"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: | 	for rocnik in rocniky: | ||||||
| 		k+=1 | 		k+=1 | ||||||
| 		cisla = rocnik_cisla[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): | 			for pi in range(1, ((size + 1) // 2) + 1): | ||||||
| 
 | 
 | ||||||
| 				poc_op = rnd.randint(1, 4) # počet opravovatelů | 				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 | 				p.ulohazadaninode = uloha_zadani | ||||||
| 				otec_syn(cisla[ci-2-1].cislonode, uloha_zadani) | 				otec_syn(cisla[ci-2-1].cislonode, uloha_zadani) | ||||||
| 
 | 
 | ||||||
|  | 				# generování vzorového textu | ||||||
| 				text_vzoraku = Text.objects.create( | 				text_vzoraku = Text.objects.create( | ||||||
| 					na_web = rnd.choice(reseni), | 					na_web = rnd.choice(reseni), | ||||||
| 					do_cisla = 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.opravovatele.set(rnd.sample(organizatori,poc_op)) | ||||||
| 				p.save() | 				p.save() | ||||||
| 
 | 
 | ||||||
| 				# reseni ulohy | 				# generování řešení | ||||||
| 				poc_reseni = rnd.randint(size // 2, size * 2) | 				poc_reseni = rnd.randint(size // 2, size * 2) | ||||||
| 				#poc_resitel = rnd.randint(1, 3) <- k čemu je himbajs tahle proměnná? | 				# generujeme náhodný počet řešení | ||||||
| 				# vybereme nahodny vzorek resitelu o delce poctu reseni |  | ||||||
| 				# (nebo skoro vsechny resitele, pokud jich je mene nez pocet reseni) |  | ||||||
| 				for ri in range(poc_reseni): | 				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 | 					# problem a resitele přiřadíme později, ManyToManyField | ||||||
| 					# se nedá vyplnit v create() | 					# 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.resitele.set(res_vyber) | ||||||
| 					res.save() | 					res.save() | ||||||
| 					hod = Hodnoceni.objects.create( | 					hod = Hodnoceni.objects.create( | ||||||
|  | @ -234,29 +266,33 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | ||||||
| 	return | 	return | ||||||
| 
 | 
 | ||||||
| def gen_soustredeni(rnd, resitele, organizatori): | def gen_soustredeni(rnd, resitele, organizatori): | ||||||
| 		soustredeni = [] | 	logger.info('Generuji soustředění...') | ||||||
| 		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)) | 	soustredeni = [] | ||||||
| 			working_sous = Soustredeni.objects.create( | 	for _ in range(1, 10): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) | ||||||
| 				rocnik=Rocnik.objects.order_by('?').first(), | 		datum_zacatku=datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) | ||||||
| 				verejne_db=rnd.choice([True, False]), | 		working_sous = Soustredeni.objects.create( | ||||||
| 				misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']), | 			rocnik=Rocnik.objects.order_by('?').first(), | ||||||
| 				typ=rnd.choice(['jarni', 'podzimni', 'vikend']), | 			verejne_db=rnd.choice([True, False]), | ||||||
| 				datum_zacatku=datum_zacatku, | 			misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']), | ||||||
| 				datum_konce=datum_zacatku + datetime.timedelta(days=7)) | 			typ=rnd.choice(['jarni', 'podzimni', 'vikend']), | ||||||
| 			ucastnici = rnd.sample(resitele, min(len(resitele), 20)) | 			datum_zacatku=datum_zacatku, | ||||||
| 			working_sous.ucastnici.set(ucastnici) | 			datum_konce=datum_zacatku + datetime.timedelta(days=7)) | ||||||
| 			#for res in rnd.sample(resitele, min(len(resitele), 20)): | 		ucastnici = rnd.sample(resitele, min(len(resitele), 20)) | ||||||
| 			#	Soustredeni_Ucastnici.objects.create(resitel=res, soutredeni=working_sous) | 		working_sous.ucastnici.set(ucastnici) | ||||||
| 			orgove_vyber = rnd.sample(organizatori, min(len(organizatori), 20)) | 		#for res in rnd.sample(resitele, min(len(resitele), 20)): | ||||||
| 			working_sous.organizatori.set(orgove_vyber) | 		#	Soustredeni_Ucastnici.objects.create(resitel=res, soutredeni=working_sous) | ||||||
| 			#for org in rnd.sample(organizatori, min(len(organizatori), 20)): | 		orgove_vyber = rnd.sample(organizatori, min(len(organizatori), 20)) | ||||||
| 			#	Soustredeni_Organizatori.objects.create(organizator=org, soutredeni=working_sous) | 		working_sous.organizatori.set(orgove_vyber) | ||||||
| 			working_sous.save() | 		#for org in rnd.sample(organizatori, min(len(organizatori), 20)): | ||||||
| 			soustredeni.append(working_sous) | 		#	Soustredeni_Organizatori.objects.create(organizator=org, soutredeni=working_sous) | ||||||
| 		return soustredeni | 		working_sous.save() | ||||||
|  | 		soustredeni.append(working_sous) | ||||||
|  | 	return soustredeni | ||||||
| 
 | 
 | ||||||
| def gen_rocniky(last_rocnik, size): | def gen_rocniky(last_rocnik, size): | ||||||
|  | 	logger.info('Generuji ročníky (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
| 	rocniky = [] | 	rocniky = [] | ||||||
| 	node = None | 	node = None | ||||||
| 	for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): | 	for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): | ||||||
|  | @ -268,6 +304,8 @@ def gen_rocniky(last_rocnik, size): | ||||||
| 	return rocniky | 	return rocniky | ||||||
| 
 | 
 | ||||||
| def gen_konfery(size, rnd, organizatori, resitele, soustredeni): | def gen_konfery(size, rnd, organizatori, resitele, soustredeni): | ||||||
|  | 	logger.info('Generuji konfery (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
| 	konfery = [] | 	konfery = [] | ||||||
| 	for _ in range(1, size): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) | 	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, | 		# 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 | 	return konfery | ||||||
| 
 | 
 | ||||||
| def gen_cisla(rnd, rocniky): | def gen_cisla(rnd, rocniky): | ||||||
|  | 	logger.info('Generuji čísla...') | ||||||
|  | 
 | ||||||
| 	rocnik_cisla = [] | 	rocnik_cisla = [] | ||||||
| 	for rocnik in rocniky: | 	for rocnik in rocniky: | ||||||
| 		otec = True | 		otec = True | ||||||
|  | @ -317,7 +357,7 @@ def gen_cisla(rnd, rocniky): | ||||||
| 
 | 
 | ||||||
| 			cislo = Cislo.objects.create( | 			cislo = Cislo.objects.create( | ||||||
| 				rocnik = rocnik, | 				rocnik = rocnik, | ||||||
| 				cislo = str(ci), | 				poradi = str(ci),  | ||||||
| 				datum_vydani=vydano, | 				datum_vydani=vydano, | ||||||
| 				datum_deadline=deadline, | 				datum_deadline=deadline, | ||||||
| 				verejne_db=True | 				verejne_db=True | ||||||
|  | @ -335,6 +375,8 @@ def gen_cisla(rnd, rocniky): | ||||||
| 	return rocnik_cisla | 	return rocnik_cisla | ||||||
| 
 | 
 | ||||||
| def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | ||||||
|  | 	logger.info('Generuji témata...') | ||||||
|  | 
 | ||||||
| 	jake = ["Hravé", "Fyzikální", "Nejlepší", "Totálně masakrální", | 	jake = ["Hravé", "Fyzikální", "Nejlepší", "Totálně masakrální", | ||||||
| 					"Šokující", "Magnetické", "Modré", "Překvapivé", | 					"Šokující", "Magnetické", "Modré", "Překvapivé", | ||||||
| 					"Plasmatické", "Novoroční"] | 					"Plasmatické", "Novoroční"] | ||||||
|  | @ -361,8 +403,9 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | ||||||
| 				garant=rnd.choice(organizatori), | 				garant=rnd.choice(organizatori), | ||||||
| 				kod=str(n), | 				kod=str(n), | ||||||
| 				# atributy třídy Téma | 				# atributy třídy Téma | ||||||
| 				tema_typ=rnd.choice(Tema.TEMA_CHOICES), | 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], | ||||||
| 				rocnik=rocnik | 				rocnik=rocnik, | ||||||
|  | 				abstrakt = "Abstrakt tematka {}".format(n) | ||||||
| 			) | 			) | ||||||
| 			konec_tematu = min(rnd.randint(ci, 7), len(cisla)) | 			konec_tematu = min(rnd.randint(ci, 7), len(cisla)) | ||||||
| 			for i in range(ci, konec_tematu+1): | 			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): | def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori): | ||||||
|  | 	logger.info('Generuji úlohy k tématům...') | ||||||
|  | 
 | ||||||
| 	# ulohy resene v cisle | 	# ulohy resene v cisle | ||||||
| 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | ||||||
| 		"Zákeřná", "Fyzikální"] | 		"Zákeřná", "Fyzikální"] | ||||||
|  | @ -474,6 +519,8 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) | ||||||
| 	return | 	return | ||||||
| 
 | 
 | ||||||
| def gen_novinky(rnd, organizatori): | def gen_novinky(rnd, organizatori): | ||||||
|  | 	logger.info('Generuji novinky...') | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	jake = ["zábavné", "veselé", "dobrodružné", "skvělé"] | 	jake = ["zábavné", "veselé", "dobrodružné", "skvělé"] | ||||||
| 	co = ["soustředění", "Fyziklání", "víkendové setkání"] | 	co = ["soustředění", "Fyziklání", "víkendové setkání"] | ||||||
|  | @ -495,6 +542,8 @@ def otec_syn(otec, syn): | ||||||
| 
 | 
 | ||||||
| @transaction.atomic | @transaction.atomic | ||||||
| def create_test_data(size = 6, rnd = None): | def create_test_data(size = 6, rnd = None): | ||||||
|  | 	logger.info('Vyrábím testovací data (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
| 	assert size >= 1 | 	assert size >= 1 | ||||||
| 	# pevna pseudo-nahodnost | 	# pevna pseudo-nahodnost | ||||||
| 	rnd = rnd or random.Random(x=42) | 	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ě | 	# 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]) | 			aktualni_cislo = Cislo.objects.all()[1]) | ||||||
|  |  | ||||||
|  | @ -3,10 +3,14 @@ from django.contrib.auth.decorators import user_passes_test | ||||||
| from . import views, export | from . import views, export | ||||||
| from .utils import staff_member_required | from .utils import staff_member_required | ||||||
| from django.views.generic.base import RedirectView | 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) | staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |   path('aktualni/temata/', views.TemataRozcestnikView), | ||||||
|  | 	path('<int:rocnik>/t<int:tematko>/', views.TematkoView), | ||||||
|  | 
 | ||||||
| 	# REDIRECTy | 	# REDIRECTy | ||||||
| 	path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), | 	path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), | ||||||
| 
 | 
 | ||||||
|  | @ -85,19 +89,36 @@ urlpatterns = [ | ||||||
| 	path('stav', | 	path('stav', | ||||||
| 		staff_member_required(views.StavDatabazeView), name='stav_databaze'), | 		staff_member_required(views.StavDatabazeView), name='stav_databaze'), | ||||||
| 	path('cislo/<int:rocnik>.<int:cislo>/obalkovani', | 	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', | 	path('cislo/<int:rocnik>.<int:cislo>/tex-download.json', | ||||||
| 		staff_member_required(views.texDownloadView), name='seminar_tex_download'), | 		staff_member_required(views.texDownloadView), name='seminar_tex_download'), | ||||||
| 	path('soustredeni/<int:soustredeni>/obalky.pdf', | 	path('soustredeni/<int:soustredeni>/obalky.pdf', | ||||||
| 		staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), | 		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( | 	path( | ||||||
| 		'tex-upload/', | 		'tex-upload/', | ||||||
| 		staff_member_required(views.texUploadView), | 		staff_member_required(views.texUploadView), | ||||||
| 		name='seminar_tex_upload' | 		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'), | 	path('', views.TitulniStranaView.as_view(), name='titulni_strana'), | ||||||
| 
 | 
 | ||||||
| 	# Ceka na autocomplete v3 | 	# Ceka na autocomplete v3 | ||||||
|  |  | ||||||
|  | @ -2,9 +2,18 @@ | ||||||
| 
 | 
 | ||||||
| import datetime | import datetime | ||||||
| from django.contrib.auth.decorators import user_passes_test | from django.contrib.auth.decorators import user_passes_test | ||||||
|  | from html.parser import HTMLParser  | ||||||
| 
 | 
 | ||||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||||
| 
 | 
 | ||||||
|  | class FirstTagParser(HTMLParser): | ||||||
|  | 	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): | def histogram(seznam): | ||||||
| 	d = {} | 	d = {} | ||||||
| 	for i in seznam: | 	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.shortcuts import get_object_or_404, render | ||||||
| from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse | 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.core.exceptions import PermissionDenied, ObjectDoesNotExist | ||||||
| from django.views import generic | from django.views import generic | ||||||
| from django.utils.translation import ugettext as _ | from django.utils.translation import ugettext as _ | ||||||
| from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect | from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect | ||||||
| from django.db.models import Q | from django.db.models import Q, Sum, Count | ||||||
| from django.views.decorators.csrf import ensure_csrf_cookie | from django.views.decorators.csrf import ensure_csrf_cookie | ||||||
| from django.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 .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||||
| from . import utils | from . import utils | ||||||
| from .unicodecsv import UnicodeWriter | 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 datetime import timedelta, date, datetime | ||||||
|  | from django.utils import timezone | ||||||
| from itertools import groupby | from itertools import groupby | ||||||
| import tempfile | import tempfile | ||||||
| import subprocess | import subprocess | ||||||
|  | @ -30,6 +39,7 @@ import json | ||||||
| import traceback | import traceback | ||||||
| import sys | import sys | ||||||
| import csv | import csv | ||||||
|  | import logging | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def verejna_temata(rocnik): | 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') | 	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): | def AktualniZadaniView(request): | ||||||
| 	nastaveni = get_object_or_404(Nastaveni) | 	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): | #def ZadaniAktualniVysledkovkaView(request): | ||||||
| #	nastaveni = get_object_or_404(Nastaveni) | #	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | @ -143,7 +285,7 @@ class StareNovinkyView(generic.ListView): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Organizatori | # Organizatori | ||||||
| def aktivniOrganizatori(datum=date.today()): | def aktivniOrganizatori(datum=timezone.now()): | ||||||
| 	return Organizator.objects.exclude( | 	return Organizator.objects.exclude( | ||||||
| 		organizuje_do__isnull=False, | 		organizuje_do__isnull=False, | ||||||
| 		organizuje_do__lt=datum | 		organizuje_do__lt=datum | ||||||
|  | @ -252,26 +394,115 @@ class ArchivView(generic.ListView): | ||||||
| 		context["nahledy"] = "\n".join(tags) | 		context["nahledy"] = "\n".join(tags) | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  | ### Výsledky | ||||||
| 
 | 
 | ||||||
| def sloupec_s_poradim(vysledky): | # 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.) | ||||||
| 	# počet řešitelů ve výsledkovce nad aktuálním | def sloupec_s_poradim(seznam_s_body): | ||||||
| 	lepsich_resitelu = 0 | 	aktualni_poradi = 1 | ||||||
|  | 	sloupec_s_poradim = [] | ||||||
| 
 | 
 | ||||||
| 	poradi_l = [] | 	# seskupíme seznam všech bodů podle hodnot | ||||||
| 	# projdeme skupiny řešitelů se stejným počtem bodů | 	for index in range(0, len(seznam_s_body)): | ||||||
| 	for skupina in (list(x) for _, x in groupby(vysledky, lambda x: x.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 | ||||||
| 		# připravíme si obsahy buněk ve sloupci pořadí pro skupinu | 		if (index + 1) < aktualni_poradi: | ||||||
| 		if len(skupina) == 1: | 			sloupec_s_poradim.append("") | ||||||
| 			poradi_l += ["{}.".format(lepsich_resitelu + 1)] | 			continue | ||||||
| 		# je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního | 		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: | 		else: | ||||||
| 			poradi_l += [u"{}.–{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1) | 			sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,  | ||||||
| 		lepsich_resitelu += len(skupina) | 						aktualni_poradi+velikost_skupiny-1)) | ||||||
| 	#pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu | 		# 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): | #def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||||
| #	"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | #	"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||||
|  | @ -288,7 +519,7 @@ def sloupec_s_poradim(vysledky): | ||||||
| #		return None | #		return None | ||||||
| # | # | ||||||
| #	#vybere vsechny vysledky z posledniho (verejneho) cisla a setridi sestupne dle bodu | #	#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: | #	class Vysledkovka: | ||||||
| #		def __init__(self): | #		def __init__(self): | ||||||
|  | @ -304,7 +535,7 @@ def sloupec_s_poradim(vysledky): | ||||||
| #		v.poradi = poradi | #		v.poradi = poradi | ||||||
| #		v.resitel.rocnik = v.resitel.rocnik(rocnik) | #		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: | #		if jen_verejne: | ||||||
| #			verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True) | #			verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True) | ||||||
| # | # | ||||||
|  | @ -380,16 +611,17 @@ class ProblemView(generic.DetailView): | ||||||
| 
 | 
 | ||||||
| class VysledkyResitele(object): | class VysledkyResitele(object): | ||||||
| 	"""Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový | 	"""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): | 	def __init__(self, resitel, cislo, rocnik): | ||||||
| 		resitel_jmeno = jmeno | 		self.resitel = resitel | ||||||
| 		resitel_prijmeni = prijmeni | 		self.cislo = cislo | ||||||
| 		body = {} | 		self.body_cislo = body_resitele_v_cisle(resitel, cislo) | ||||||
| 		body_cislo = 0 | 		self.body = [] | ||||||
| 
 | 		self.rocnik = rocnik | ||||||
| 	def body_za_cislo(self): | 		self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo) | ||||||
| 		return sum(body.values()) | 		self.body_celkem_odjakziva = resitel.vsechny_body() | ||||||
|  | 		self.poradi = 0 | ||||||
| 
 | 
 | ||||||
| class CisloView(generic.DetailView): | class CisloView(generic.DetailView): | ||||||
| 	model = Cislo | 	model = Cislo | ||||||
|  | @ -400,8 +632,8 @@ class CisloView(generic.DetailView): | ||||||
| 		if queryset is None: | 		if queryset is None: | ||||||
| 			queryset = self.get_queryset() | 			queryset = self.get_queryset() | ||||||
| 		rocnik_arg = self.kwargs.get('rocnik') | 		rocnik_arg = self.kwargs.get('rocnik') | ||||||
| 		cislo_arg = self.kwargs.get('cislo') | 		poradi_arg = self.kwargs.get('cislo') | ||||||
| 		queryset = queryset.filter(rocnik__rocnik=rocnik_arg, cislo=cislo_arg) | 		queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_arg) | ||||||
| 
 | 
 | ||||||
| 		try: | 		try: | ||||||
| 			obj = queryset.get() | 			obj = queryset.get() | ||||||
|  | @ -410,88 +642,53 @@ class CisloView(generic.DetailView): | ||||||
| 						{'verbose_name': queryset.model._meta.verbose_name}) | 						{'verbose_name': queryset.model._meta.verbose_name}) | ||||||
| 		return obj | 		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): | 	def get_context_data(self, **kwargs): | ||||||
| 		context = super(CisloView, self).get_context_data(**kwargs) | 		context = super(CisloView, self).get_context_data(**kwargs) | ||||||
| 
 | 
 | ||||||
| 		## TODO upravit dle nového modelu | 		## TODO upravit dle nového modelu | ||||||
| 		cislo = context['cislo'] | 		cislo = context['cislo'] | ||||||
| 		hodnoceni = cislo.hodnoceni_set	# hodnocení, která se vážou k danému číslu | 		hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||||
| 		 |  | ||||||
| 		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) |  | ||||||
| 
 | 
 | ||||||
| 		## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele | 		## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele | ||||||
| 		## TODO možná chytřeji vybírat aktivní řešitele | 		## TODO možná chytřeji vybírat aktivní řešitele | ||||||
| 		## chceme letos něco poslal | 		## chceme letos něco poslal | ||||||
| 		aktivni_resitele = Resitel.objects.filter( | 		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) | 				#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) | ||||||
| 		radky_vysledkovky = [] | 		radky_vysledkovky = [] | ||||||
| 		for ar in aktivni_resitele: | 		for ar in aktivni_resitele: | ||||||
| 			vr = VysledkyResitele(ar.jmeno, ar.prijmeni) | 			# získáme výsledky řešitele - součty přes číslo a ročník | ||||||
| 			for h in hlavni_problemy:  | 			vr = VysledkyResitele(ar, cislo, cislo.rocnik) | ||||||
| 				body = vysledky_resitele_problemu(h, ar, cislo) | 			for hp in hlavni_problemy: | ||||||
| 				vr.body[h.kod_v_rocniku] = body | 				vr.body.append( | ||||||
| 				vr.body_cislo = vr.body_cislo + body  | 				body_resitele_problemu_v_cisle(hp, ar, cislo)) | ||||||
| 			radky_vysledkovky.append(vr) | 			radky_vysledkovky.append(vr) | ||||||
| 
 | 
 | ||||||
| 		## TODO: spočítat počet bodů řešitele v daném ročníku a seřadit je podle toho | 		# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů | ||||||
| 		## TODO: možná použít tyto funkce i v RocnikVysledkovkaView (a umístit sem nebo tam)? | 		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) | ||||||
| 		 | 		 | ||||||
|  | 		# 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 | ||||||
| 
 | 
 | ||||||
| #		vysledky = VysledkyKCisluZaRocnik.objects.filter(cislo = context['cislo']).\ | 		# vytahané informace předáváme do kontextu | ||||||
| #			order_by('-body', 'resitel__prijmeni', 'resitel__jmeno') | 		context['cislo'] = cislo | ||||||
| #		reseni = Reseni.objects.filter(cislo_body = context['cislo']).select_related("resitel") | 		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 | ||||||
| 
 | 
 | ||||||
| 		# typy úloh, které se mají zobrazovat u čísla, tj. těch, které byly  | 		return context | ||||||
| 		# 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') |  | ||||||
| 
 | 
 | ||||||
| #		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())) | #		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 | #		#setridi problemy podle typu a poradi zadani | ||||||
| #		problem_index = {} | #		problem_index = {} | ||||||
|  | @ -529,7 +726,6 @@ class CisloView(generic.DetailView): | ||||||
| #		context['problemy'] = problemy | #		context['problemy'] = problemy | ||||||
| #		context['v_cisle_zadane'] = v_cisle_zadane | #		context['v_cisle_zadane'] = v_cisle_zadane | ||||||
| #		context['resene_problemy'] = resene_problemy | #		context['resene_problemy'] = resene_problemy | ||||||
| #		return context |  | ||||||
| 
 | 
 | ||||||
| class ArchivTemataView(generic.ListView): | class ArchivTemataView(generic.ListView): | ||||||
| 	model = Problem | 	model = Problem | ||||||
|  | @ -601,9 +797,9 @@ def obalkyView(request,resitele): | ||||||
| 	tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content | 	tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content | ||||||
| 
 | 
 | ||||||
| 	tempdir = tempfile.mkdtemp() | 	tempdir = tempfile.mkdtemp() | ||||||
| 	with open(tempdir+"/obalky.tex","wb") as texfile: | 	with open(tempdir+"/obalky.tex","w") as texfile: | ||||||
| 		texfile.write(tex) | 		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) | 	subprocess.call(["pdflatex","obalky.tex"],cwd = tempdir) | ||||||
| 
 | 
 | ||||||
| 	with open(tempdir+"/obalky.pdf","rb") as pdffile: | 	with open(tempdir+"/obalky.pdf","rb") as pdffile: | ||||||
|  | @ -612,7 +808,7 @@ def obalkyView(request,resitele): | ||||||
| 	return response | 	return response | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def obalkovaniView(request, rocnik, cislo): | def oldObalkovaniView(request, rocnik, cislo): | ||||||
| 	rocnik = Rocnik.objects.get(rocnik=rocnik) | 	rocnik = Rocnik.objects.get(rocnik=rocnik) | ||||||
| 	cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) | 	cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) | ||||||
| 
 | 
 | ||||||
|  | @ -749,7 +945,7 @@ def StavDatabazeView(request): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ensure_csrf_cookie | @ensure_csrf_cookie | ||||||
| def LoginView(request): | def TeXUploadLoginView(request): | ||||||
| 	"""Pro přihlášení při nahrávání z texu""" | 	"""Pro přihlášení při nahrávání z texu""" | ||||||
| 	q = request.POST | 	q = request.POST | ||||||
| 	# nastavení cookie csrftoken | 	# nastavení cookie csrftoken | ||||||
|  | @ -938,7 +1134,7 @@ def texDownloadView(request, rocnik, cislo): | ||||||
| 				"body": p.body, | 				"body": p.body, | ||||||
| 				"zadani": p.text_zadani, | 				"zadani": p.text_zadani, | ||||||
| 				"reseni": p.text_reseni, | 				"reseni": p.text_reseni, | ||||||
| 				"cislo_zadani": p.cislo_zadani.cislo, | 				"cislo_zadani": p.cislo_zadani.poradi, | ||||||
| 			} for p in resene | 			} for p in resene | ||||||
| 		], | 		], | ||||||
| 	} | 	} | ||||||
|  | @ -947,26 +1143,213 @@ def texDownloadView(request, rocnik, cislo): | ||||||
| 	cislo.save() | 	cislo.save() | ||||||
| 	return JsonResponse(response) | 	return JsonResponse(response) | ||||||
| 
 | 
 | ||||||
| ## Formulare | class ResitelView(LoginRequiredMixin,generic.DetailView): | ||||||
|  | 	model = Resitel | ||||||
|  | 	template_name = 'seminar/resitel.html' | ||||||
| 
 | 
 | ||||||
| def get_name(request): | 	def get_object(self, queryset=None): | ||||||
| 	# if this is a POST request we need to process the form data | 		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': | 	if request.method == 'POST': | ||||||
| 		# create a form instance and populate it with data from the request: | 		form = LoginForm(request.POST) | ||||||
| 		form = NameForm(request.POST) |  | ||||||
| 		# check whether it's valid: |  | ||||||
| 		if form.is_valid(): | 		if form.is_valid(): | ||||||
| 			# process the data in form.cleaned_data as required | 			user = authenticate(request,  | ||||||
| 			# ... | 				username=form.cleaned_data['username'], | ||||||
| 			# redirect to a new URL: | 				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/') | 			return HttpResponseRedirect('/thanks/') | ||||||
| 
 | 
 | ||||||
| 	# if a GET (or any other method) we'll create a blank form | 	# if a GET (or any other method) we'll create a blank form | ||||||
| 	else: | 	else: | ||||||
| 		form = NameForm() | 		form = PrihlaskaForm() | ||||||
| 
 | 
 | ||||||
| 	return render(request, 'seminar/prihlaska.html', {'form': form}) | 	return render(request, 'seminar/prihlaska.html', {'form': form}) | ||||||
| 
 | 
 | ||||||
|  | class SkolaAutocomplete(autocomplete.Select2QuerySetView): | ||||||
|  | 	def get_queryset(self): | ||||||
|  |         	# Don't forget to filter out results depending on the visitor ! | ||||||
|  | 		qs = Skola.objects.all() | ||||||
|  | 		if self.q: | ||||||
|  | 			qs = qs.filter( | ||||||
|  | 				Q(nazev__istartswith=self.q)| | ||||||
|  | 				Q(kratky_nazev__istartswith=self.q)| | ||||||
|  | 				Q(ulice__istartswith=self.q)| | ||||||
|  | 				Q(mesto__istartswith=self.q)) | ||||||
|  | 		 | ||||||
|  | 		return qs | ||||||
|  | 
 | ||||||
|  | class LoginRequiredAjaxMixin(object): | ||||||
|  | 	def dispatch(self, request, *args, **kwargs): | ||||||
|  | 		#if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme | ||||||
|  | 		if not request.user.is_authenticated: | ||||||
|  | 			return JsonResponse(data={'results': [], 'pagination': {}}, status=401) | ||||||
|  | 		return super(LoginRequiredAjaxMixin, self).dispatch(request, *args, **kwargs) | ||||||
|  | 
 | ||||||
|  | class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): | ||||||
|  | 	def get_queryset(self): | ||||||
|  | 		qs = Resitel.objects.all() | ||||||
|  | 		if self.q: | ||||||
|  | 			qs = qs.filter( | ||||||
|  | 				Q(osoba__jmeno__startswith=self.q)| | ||||||
|  | 				Q(osoba__prijmeni__startswith=self.q)| | ||||||
|  | 				Q(osoba__prezdivka__startswith=self.q) | ||||||
|  | 				) | ||||||
|  | 		return qs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Ceka na autocomplete v3 | # Ceka na autocomplete v3 | ||||||
| # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): | # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): | ||||||
| # 	def get_queryset(self): | # 	def get_queryset(self): | ||||||
|  | @ -987,3 +1370,46 @@ def get_name(request): | ||||||
| # 				Q(user__last_name__isstartswith=query)) | # 				Q(user__last_name__isstartswith=query)) | ||||||
| # | # | ||||||
| # 		return qs | # 		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á