Merge remote-tracking branch 'origin/master' into stable
							
								
								
									
										23
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						|  | @ -1,6 +1,6 @@ | |||
| .PHONY: clean_env init_env clean_virtualenv install_packages clean install run all schema_seminar.pdf schema_all.pdf | ||||
| PYTHON=python2.7 | ||||
| VE_VER=12.0.7 | ||||
| VE_VER=13.1.2 | ||||
| LOCAL_PYTHON=bin/python | ||||
| 
 | ||||
| all: install | ||||
|  | @ -17,7 +17,7 @@ make_env: ${LOCAL_PYTHON} | |||
| 
 | ||||
| # phony, but fast repeated execution
 | ||||
| install_packages: make_env | ||||
| 	bin/pip install -r requirements.txt | ||||
| 	bin/pip install -r requirements.txt --upgrade | ||||
| 
 | ||||
| # phony
 | ||||
| clean_env: | ||||
|  | @ -35,12 +35,13 @@ ${LOCAL_PYTHON}: virtualenv | |||
| virtualenv: | ||||
| 	curl -O https://pypi.python.org/packages/source/v/virtualenv/virtualenv-${VE_VER}.tar.gz  | ||||
| 	tar xvfz virtualenv-${VE_VER}.tar.gz | ||||
| 	mv virtualenv-${VE_VER} virtualenv | ||||
| 	mv -T virtualenv-${VE_VER} virtualenv | ||||
| 	rm virtualenv-${VE_VER}.tar.gz | ||||
| 
 | ||||
| # phony
 | ||||
| clean_virtualenv: | ||||
| 	rm -rf virtualenv/ | ||||
| 	rm -rf virtualenv-*.tar.gz | ||||
| 
 | ||||
| run: | ||||
| 	./manage.py runserver_plus | ||||
|  | @ -60,21 +61,24 @@ schema_all.pdf: | |||
| 
 | ||||
| # Deploy to current *mamweb-test* directory
 | ||||
| deploy_test: | ||||
| 	@if [[ `pwd` != "/akce/MaM/WWW/mamweb-test" ]]; then echo "Only possible in /akce/MaM/WWW/mamweb-test"; exit 1; fi | ||||
| 	@if [ ${USER} != "www-mam" ]; then echo "Only possible by user www-mam"; exit 1; fi | ||||
| 	@if [ `pwd` != "/akce/MaM/WWW/mamweb-test" ]; then echo "Only possible in /akce/MaM/WWW/mamweb-test"; exit 1; fi | ||||
| 	@echo "Installing version from origin/master ..." | ||||
| 	git pull origin master | ||||
| 	git clean -f | ||||
| 	make install | ||||
| 	./manage.py migrate  | ||||
| 	./manage.py collectstatic --noinput | ||||
| 	(chown -Rf :mam . || true ) | ||||
| 	(chmod -Rf g+w . || true ) | ||||
| 	(chown -R :mam . || true ) | ||||
| 	(chmod -R g+rX,go-w . || true ) | ||||
| 	@echo Notifying apache about the change ... | ||||
| 	touch mamweb/wsgi.py | ||||
| 	@echo Done. | ||||
| 
 | ||||
| # Deploy to current *mamweb-prod* directory
 | ||||
| deploy_prod: | ||||
| 	@if [[ `pwd` != "/akce/MaM/WWW/mamweb-prod" ]]; then echo "Only possible in /akce/MaM/WWW/mamweb-prod"; exit 1; fi | ||||
| 	@if [ ${USER} != "www-mam" ]; then echo "Only possible by user www-mam"; exit 1; fi | ||||
| 	@if [ `pwd` != "/akce/MaM/WWW/mamweb-prod" ]; then echo "Only possible in /akce/MaM/WWW/mamweb-prod"; exit 1; fi | ||||
| 	@echo "Backing up production DB ..." | ||||
| 	( cd .. && ./backup_prod_db.sh ) | ||||
| 	@echo "Installing version from origin/stable ..." | ||||
|  | @ -83,8 +87,9 @@ deploy_prod: | |||
| 	make install | ||||
| 	./manage.py migrate  | ||||
| 	./manage.py collectstatic --noinput | ||||
| 	(chown -Rf :mam . || true ) | ||||
| 	(chmod -Rf g+w . || true ) | ||||
| 	(chown -R :mam . || true ) | ||||
| 	(chmod -R g+rX,go-w . || true ) | ||||
| 	@echo Notifying apache about the change ... | ||||
| 	touch mamweb/wsgi.py | ||||
| 	@echo Done. | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										12
									
								
								mamweb/context_processors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,12 @@ | |||
| 
 | ||||
| from datetime import datetime, date | ||||
| 
 | ||||
| def vzhled(request): | ||||
|     ''' Podle casu prida do templatu, zdali je nebo neni noc ''' | ||||
|     hodin = datetime.now().hour | ||||
|     if (hodin <= 6) or (hodin >= 20): | ||||
|         noc = True | ||||
|     else: | ||||
|         noc = False | ||||
|     return {'noc' : noc} | ||||
| 
 | ||||
							
								
								
									
										84
									
								
								mamweb/middleware.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,84 @@ | |||
| from datetime import datetime, date | ||||
| 
 | ||||
| from django.conf import settings | ||||
| from django.http import HttpResponse, HttpResponseRedirect | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class LoggedInHintCookieMiddleware(object): | ||||
|     """Middleware to securely help with 'logged-in' detection for dual HTTP/HTTPS sites. | ||||
|      | ||||
|     On insecure requests: Checks for a (non-secure) cookie settings.LOGGED_IN_HINT_COOKIE_NAME | ||||
|     and if present, redirects to HTTPS (same adress). | ||||
|     Note this usually breaks non-GET (POST) requests. | ||||
| 
 | ||||
|     On secure requests: Updates cookie settings.LOGGED_IN_HINT_COOKIE_NAME to reflect | ||||
|     whether an user is logged in in the current session (cookie set to 'True' or cleared). | ||||
|     The cookie is set to expire at the same time as the sessionid cookie. | ||||
| 
 | ||||
|     By default, LOGGED_IN_HINT_COOKIE_NAME = 'logged_in_hint'. | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         if hasattr(settings, 'LOGGED_IN_HINT_COOKIE_NAME'): | ||||
|             self.cookie_name = settings.LOGGED_IN_HINT_COOKIE_NAME | ||||
|         else: self.cookie_name = 'logged_in_hint' | ||||
|         self.cookie_value = 'True' | ||||
| 
 | ||||
|     def cookie_correct(self, request): | ||||
|         return self.cookie_name in request.COOKIES and request.COOKIES[self.cookie_name] == self.cookie_value | ||||
| 
 | ||||
|     def process_request(self, request): | ||||
|         if not request.is_secure(): | ||||
|             if self.cookie_correct(request): | ||||
|                 # redirect insecure (assuming http) requests with hint cookie to https | ||||
|                 url = HttpRequest.build_absolute_uri() | ||||
|                 assert url[:5] == 'http:' | ||||
|                 return HttpResponseRedirect('https:' + url[5:]) | ||||
|         return None | ||||
| 
 | ||||
|     def process_response(self, request, response): | ||||
|         if request.is_secure(): | ||||
|             # assuming full session info (as the conn. is secure) | ||||
|             if request.user.is_authenticated(): | ||||
|                 if not self.cookie_correct(request): | ||||
|                     expiry = None if request.session.get_expire_at_browser_close() else request.session.get_expiry_date() | ||||
|                     response.set_cookie(self.cookie_name, value=self.cookie_value, expires=expiry, secure=False) | ||||
|             else: | ||||
|                 if self.cookie_name in request.COOKIES: | ||||
|                     response.delete_cookie(self.cookie_name) | ||||
|         return response | ||||
| 
 | ||||
| 
 | ||||
| class vzhled: | ||||
| 
 | ||||
|     def process_request(self, request): | ||||
|         return None | ||||
| 
 | ||||
|     def process_view(self, request, view_func, view_args, view_kwargs): | ||||
|         #print "====== process_request ======" | ||||
|         #print view_func | ||||
|         #print view_args | ||||
|         #print view_kwargs | ||||
|         #print "=============================" | ||||
|         return None | ||||
| 
 | ||||
|     def process_template_response(self, request, response): | ||||
|         hodin = datetime.now().hour | ||||
|         if (hodin <= 6) or (hodin >= 14): # TODO 20 | ||||
|             response.context_data['noc'] = True | ||||
|         else: | ||||
|             response.context_data['noc'] = False | ||||
|         return response | ||||
| 
 | ||||
|     def process_response(self, request, response): | ||||
|         #hodin = datetime.now().hour | ||||
|         #if (hodin <= 6) or (hodin >= 14): # TODO 20 | ||||
|             #response.context_data['noc'] = True | ||||
|         #else: | ||||
|             #response.context_data['noc'] = False | ||||
|         return response | ||||
|          | ||||
| 
 | ||||
|     ##def process_exception(request, exception): | ||||
|         #pass | ||||
|  | @ -60,6 +60,7 @@ MIDDLEWARE_CLASSES = ( | |||
|     'django.contrib.sessions.middleware.SessionMiddleware', | ||||
|     'django.middleware.common.CommonMiddleware', | ||||
|     'django.middleware.csrf.CsrfViewMiddleware', | ||||
|     'mamweb.middleware.LoggedInHintCookieMiddleware', | ||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||
|     'django.contrib.auth.middleware.SessionAuthenticationMiddleware', | ||||
|     'django.contrib.messages.middleware.MessageMiddleware', | ||||
|  | @ -78,6 +79,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( | |||
|     'django.core.context_processors.tz', | ||||
|     'sekizai.context_processors.sekizai', | ||||
|     'django.core.context_processors.static', | ||||
|     'mamweb.context_processors.vzhled', | ||||
| ) | ||||
| 
 | ||||
| INSTALLED_APPS = ( | ||||
|  |  | |||
|  | @ -47,12 +47,18 @@ import os | |||
| 
 | ||||
| SERVER_EMAIL = 'mamweb-prod-errors@mam.mff.cuni.cz' | ||||
| ADMINS = [ | ||||
|         ('Tomas Gavenciak', 'gavento@ucw.cz'), | ||||
|         ('Petr Pecha', 'nejlepsitextovyeditorjevim@gmail.com'), | ||||
|         ('Matěj Kocián', 'matej.kocian@gmail.com'), | ||||
|         ] | ||||
| 
 | ||||
| 
 | ||||
| # SECURITY: only send sensitive cookies via HTTPS | ||||
| 
 | ||||
| SESSION_COOKIE_SECURE = True | ||||
| CSRF_COOKIE_SECURE = True | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # LOGGING = { | ||||
| #     'version': 1, | ||||
| #     'disable_existing_loggers': True, | ||||
|  |  | |||
|  | @ -25,11 +25,11 @@ INSTALLED_APPS += ( | |||
| SECRET_KEY = ')^u=i65*zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzv9l+zo)n' | ||||
| 
 | ||||
| # SECURITY WARNING: don't run with debug turned on in production! | ||||
| DEBUG = True | ||||
| DEBUG = False | ||||
| 
 | ||||
| TEMPLATE_DEBUG = True | ||||
| TEMPLATE_DEBUG = False | ||||
| 
 | ||||
| ALLOWED_HOSTS = ['*.mam.mff.cuni.cz'] | ||||
| ALLOWED_HOSTS = ['*.mam.mff.cuni.cz', 'atrey.karlin.mff.cuni.cz', 'mam.mff.cuni.cz'] | ||||
| 
 | ||||
| # Database | ||||
| # https://docs.djangoproject.com/en/1.7/ref/settings/#databases | ||||
|  | @ -45,6 +45,18 @@ DATABASES = { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| import os | ||||
| 
 | ||||
| SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz' | ||||
| ADMINS = [ | ||||
|     ('Petr Pecha', 'nejlepsitextovyeditorjevim@gmail.com'), | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
| # SECURITY: only send sensitive cookies via HTTPS | ||||
| 
 | ||||
| SESSION_COOKIE_SECURE = True | ||||
| CSRF_COOKIE_SECURE = True | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,16 +58,24 @@ h2 { | |||
| #header { | ||||
| 	position: relative; | ||||
| 	font-size: 250%; | ||||
| 	background: url("../images/header-bg-uvod.png") no-repeat center top; | ||||
| 	background: url("../images/header-bg-uvod.jpg") no-repeat center top; | ||||
| 	height: 255px; | ||||
| 	top: -1px; | ||||
| } | ||||
| 
 | ||||
| #header.cojemam { background-image: url("../images/header-bg-uvod.png"); } | ||||
| #header.soustredeni { background-image: url("../images/header-bg-soustredeni.png"); } | ||||
| #header.zadani { background-image: url("../images/header-bg-zadani.png"); } | ||||
| #header.clanky { background-image: url("../images/header-bg-clanek.png"); } | ||||
| #header.archiv { background-image: url("../images/header-bg-archiv.png"); } | ||||
| 
 | ||||
| #header.cojemam { background-image: url("../images/header-bg-uvod.jpg"); } | ||||
| #header.soustredeni { background-image: url("../images/header-bg-soustredeni.jpg"); } | ||||
| #header.zadani { background-image: url("../images/header-bg-zadani.jpg"); } | ||||
| #header.clanky { background-image: url("../images/header-bg-clanek.jpg"); } | ||||
| #header.archiv { background-image: url("../images/header-bg-archiv.jpg"); } | ||||
| 
 | ||||
| #header.NOC {background-image: url("../images/header-bg-uvod-NOC.jpg"); } | ||||
| #header.NOCcojemam { background-image: url("../images/header-bg-uvod-NOC.jpg"); } | ||||
| #header.NOCsoustredeni { background-image: url("../images/header-bg-soustredeni-NOC.jpg"); } | ||||
| #header.NOCzadani { background-image: url("../images/header-bg-zadani-NOC.jpg"); } | ||||
| #header.NOCclanky { background-image: url("../images/header-bg-clanek-NOC.jpg"); } | ||||
| #header.NOCarchiv { background-image: url("../images/header-bg-archiv-NOC.jpg"); } | ||||
| 
 | ||||
| #header img.logo { | ||||
| 	position: absolute; | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-archiv-NOC.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 37 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-archiv.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 55 KiB | 
| Before Width: | Height: | Size: 400 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-clanek-NOC.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 14 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-clanek.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 86 KiB | 
| Before Width: | Height: | Size: 519 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-odevzdat-NOC.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 64 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-soustredeni-NOC.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 53 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-soustredeni.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 101 KiB | 
| Before Width: | Height: | Size: 562 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-uvod-NOC.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 42 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-uvod.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 88 KiB | 
| Before Width: | Height: | Size: 512 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-zadani-NOC.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 36 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg-zadani.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 60 KiB | 
| Before Width: | Height: | Size: 456 KiB | 
							
								
								
									
										
											BIN
										
									
								
								mamweb/static/images/header-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 122 KiB | 
| Before Width: | Height: | Size: 679 KiB | 
							
								
								
									
										19
									
								
								mamweb/templates/400.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,19 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| {% block content %} | ||||
|   <h2> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       O-jo-jo-jo-joj | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h2> | ||||
| 
 | ||||
|   <p> | ||||
|     Chybička se vloudila. | ||||
|     Zkuste přejít na <a href="/">titulní stránku</a> | ||||
|     nebo se podívat na <a href="/zadani/aktualni/">aktuální zadání</a>. | ||||
|   </p> | ||||
|   <img src="{% static '500.png' %}"> | ||||
| {% endblock %} | ||||
| 
 | ||||
							
								
								
									
										19
									
								
								mamweb/templates/403.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,19 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| {% block content %} | ||||
|   <h2> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       Vrrrrrrrrr | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h2> | ||||
| 
 | ||||
|   <p> | ||||
|     Tady pravděpodobně nemáte co dělat. | ||||
|     Zkuste přejít na <a href="/">titulní stránku</a> | ||||
|     nebo se podívat na <a href="/zadani/aktualni/">aktuální zadání</a>. | ||||
|   </p> | ||||
|   <img src="{% static '500.png' %}"> | ||||
| {% endblock %} | ||||
| 
 | ||||
|  | @ -48,7 +48,7 @@ | |||
| 	  <div class='row'> | ||||
| 		<div class='col-md-12'> | ||||
|           <a href='/'> | ||||
|           <div id="header" class="{% block header %}{% endblock %}"> | ||||
|             <div id="header" class="{% if noc %}NOC{% endif %}{% block header %}{% endblock %}"> | ||||
| 		      <img class="logo" src="{% static 'images/logo.png' %}" /> | ||||
|               <!--<h1>{% block nadpis1b %}Nadpis 1. úrovně{% endblock %}</h1>--> | ||||
|             </div> | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ | |||
| {% endblock %}{% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| <div> | ||||
| {{ flatpage.content }} | ||||
| </div> | ||||
|  |  | |||
|  | @ -19,8 +19,6 @@ urlpatterns = patterns('', | |||
|     url(r'^comments_dj/', include('django_comments.urls')), | ||||
|     url(r'^comments_fl/', include('fluent_comments.urls')), | ||||
| 
 | ||||
|     # Obsah - flatpages | ||||
|     url(r'^', include('django.contrib.flatpages.urls')), # Pozor: musi byt posledni | ||||
| ) | ||||
| 
 | ||||
| # This is only needed when using runserver. | ||||
|  |  | |||
|  | @ -7,11 +7,22 @@ For more information on this file, see | |||
| https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ | ||||
| """ | ||||
| 
 | ||||
| import sys, os, os.path | ||||
| import sys, os, os.path, traceback, time, signal | ||||
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mamweb.settings") | ||||
| 
 | ||||
| sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | ||||
| sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'lib', 'python2.7', 'site-packages')) | ||||
| sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) | ||||
| sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'lib', 'python2.7', 'site-packages'))) | ||||
| 
 | ||||
| 
 | ||||
| try: | ||||
|     from django.core.wsgi import get_wsgi_application | ||||
|     application = get_wsgi_application() | ||||
| 
 | ||||
| except Exception: | ||||
|     print 'handling WSGI exception' | ||||
|     # Error loading applications | ||||
|     if 'mod_wsgi' in sys.modules: | ||||
|         traceback.print_exc() | ||||
|         os.kill(os.getpid(), signal.SIGINT) | ||||
|         time.sleep(2) | ||||
| 
 | ||||
| from django.core.wsgi import get_wsgi_application | ||||
| application = get_wsgi_application() | ||||
|  |  | |||
|  | @ -1,25 +1,27 @@ | |||
| # basic libs | ||||
| 
 | ||||
| psycopg2==2.6 | ||||
| html5lib==0.999 | ||||
| ipython==3.0.0 | ||||
| Pillow==2.7.0 | ||||
| psycopg2==2.6.1 | ||||
| html5lib==0.9999999 | ||||
| ipython==4.0.0 | ||||
| Pillow==2.9.0 | ||||
| pytz==2014.10 | ||||
| six==1.9.0 | ||||
| pexpect==3.3 | ||||
| traitlets==4.0.0 | ||||
| 
 | ||||
| # Django and modules | ||||
| 
 | ||||
| Django==1.7.8 | ||||
| Django==1.7.10 # Updatable to 1.8 (possibly incompatible) | ||||
| django-bootstrap-sass==0.0.6a0 | ||||
| django-mptt==0.7.3 | ||||
| django-reversion==1.8.6 | ||||
| django-reversion==1.9.3 | ||||
| django-sekizai==0.8.1 | ||||
| django-countries==3.2 | ||||
| django-solo==1.1.0 | ||||
| django-ckeditor==4.4.7 | ||||
| django-ckeditor==4.4.7 # Updatable to 5.0 (some incompatible changes) | ||||
| django-flat-theme==0.9.3 | ||||
| django-taggit==0.14.0 | ||||
| django-autocomplete-light==2.1.1 | ||||
| django-taggit==0.17 | ||||
| django-autocomplete-light==2.2.6 | ||||
| django-crispy-forms==1.4.0 | ||||
| 
 | ||||
| # Comments | ||||
|  | @ -30,9 +32,9 @@ django-contrib-comments==1.6.1 | |||
| 
 | ||||
| # debug tools/extensions | ||||
| 
 | ||||
| django-debug-toolbar==1.3.0 | ||||
| django-extensions==1.5.3 | ||||
| sqlparse==0.1.15 | ||||
| django-debug-toolbar==1.3.2 | ||||
| django-extensions==1.5.6 | ||||
| sqlparse==0.1.16 | ||||
| Werkzeug==0.10.4 | ||||
| 
 | ||||
| # G+, FB authorisation | ||||
|  |  | |||
|  | @ -9,10 +9,26 @@ from ckeditor.widgets import CKEditorWidget | |||
| from django.db.models import Count | ||||
| from django.db import models | ||||
| 
 | ||||
| from django.contrib.auth.models import User | ||||
| 
 | ||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Novinky, Organizator | ||||
| import autocomplete_light | ||||
| 
 | ||||
| 
 | ||||
| class UserModelChoiceField(forms.ModelChoiceField): | ||||
|     u"""Vlastní ModelChoiceField pro uživatele. Zobrazí kromě loginu i jméno. | ||||
|     """ | ||||
|     def label_from_instance(self, obj): | ||||
|         return u"{} ({})".format(obj.get_full_name(), obj.username) | ||||
| 
 | ||||
| def get_form_predvypln_autora(self, request, obj=None, *args, **kwargs): | ||||
|     u"""get_form fce pro Adminy. Předvyplňí přihlášeného uživatele jako autora. | ||||
|     """ | ||||
|     form = super(self.__class__, self).get_form(request, *args, **kwargs) | ||||
|     form.base_fields['autor'].initial = request.user.id | ||||
|     return form | ||||
| 
 | ||||
| 
 | ||||
| def make_set_action(atribut, hodnota, nazev): | ||||
|     u""" | ||||
|     Pomocnik pro rychle vytvareni hromadnych admin akci ktere jen nastavuji | ||||
|  | @ -271,13 +287,14 @@ admin.site.register(Reseni, ReseniAdmin) | |||
| 
 | ||||
| from autocomplete_light.contrib.taggit_field import TaggitField, TaggitWidget | ||||
| 
 | ||||
| #TODO: Autocomplete autor/opravovatel | ||||
| 
 | ||||
| class ProblemAdminForm(forms.ModelForm): | ||||
|     text_zadani = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Problem, 'text_zadani')) | ||||
|     text_reseni = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Problem, 'text_reseni')) | ||||
|     text_org = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Problem, 'text_org')) | ||||
|     zamereni = TaggitField(widget=TaggitWidget('TagAutocomplete'), required=False) | ||||
|     autor = UserModelChoiceField(User.objects.filter(is_staff=True)) | ||||
|     opravovatel = UserModelChoiceField(User.objects.filter(is_staff=True), required=False) | ||||
|     class Meta: | ||||
|         model = Problem | ||||
|         exclude = [] | ||||
|  | @ -302,13 +319,16 @@ class ProblemAdmin(reversion.VersionAdmin): | |||
|         return obj.pocet_reseni | ||||
| 
 | ||||
| class ProblemNavrhAdmin(ProblemAdmin): | ||||
|     list_display = ['nazev', 'typ', 'stav', 'autor', 'timestamp'] | ||||
|     list_filter = ['typ', 'stav', 'timestamp'] | ||||
|     list_display = ['nazev', 'typ', 'zamereni', 'stav', 'autor', 'timestamp'] | ||||
|     list_filter = ['typ', 'zamereni', 'timestamp', 'stav'] | ||||
| 
 | ||||
|     def get_queryset(self, request): | ||||
|         qs = super(ProblemNavrhAdmin, self).get_queryset(request) | ||||
|         return qs.filter(stav__in=[Problem.STAV_NAVRH, Problem.STAV_SMAZANY]) | ||||
| 
 | ||||
|     get_form = get_form_predvypln_autora | ||||
| 
 | ||||
| 
 | ||||
| create_modeladmin(ProblemNavrhAdmin, Problem, 'ProblemNavrh', verbose_name=u'Problém (návrh)', verbose_name_plural=u'Problémy (návrhy)') | ||||
| 
 | ||||
| class ProblemZadanyAdmin(ProblemAdmin): | ||||
|  | @ -320,6 +340,8 @@ class ProblemZadanyAdmin(ProblemAdmin): | |||
|         qs = super(ProblemZadanyAdmin, self).get_queryset(request) | ||||
|         return qs.filter(stav=Problem.STAV_ZADANY).annotate(pocet_reseni=Count('reseni')) | ||||
| 
 | ||||
|     get_form = get_form_predvypln_autora | ||||
| 
 | ||||
| create_modeladmin(ProblemZadanyAdmin, Problem, 'ProblemZadany', verbose_name=u'Problém (zadaný)', verbose_name_plural=u'Problémy (zadané)') | ||||
| 
 | ||||
| #admin.site.register(Problem, ProblemAdmin) | ||||
|  | @ -356,6 +378,8 @@ admin.site.register(Soustredeni, SoustredeniAdmin) | |||
| class NovinkyAdminForm(forms.ModelForm): | ||||
|     text = forms.CharField(widget=CKEditorWidget(), required=False, | ||||
|             **field_labels(Novinky, 'text')) | ||||
|     autor = UserModelChoiceField(User.objects.filter(is_staff=True)) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = Novinky | ||||
|         exclude = [] | ||||
|  | @ -380,13 +404,7 @@ class NovinkyAdmin(admin.ModelAdmin): | |||
|     list_display = ['datum', 'autor', 'text', 'zverejneno', 'obrazek'] | ||||
|     actions = [zverejnit_novinky, zneverejnit_novinky] | ||||
| 
 | ||||
|     # předvyplnění přihlášeného uživatele jako autora novinky | ||||
|     def formfield_for_foreignkey(self, db_field, request, **kwargs): | ||||
|         if db_field.name == 'autor': | ||||
|             kwargs['initial'] = request.user.id | ||||
|         return super(NovinkyAdmin, self).formfield_for_foreignkey( | ||||
|             db_field, request, **kwargs | ||||
|         ) | ||||
|     get_form = get_form_predvypln_autora | ||||
| 
 | ||||
| 
 | ||||
| admin.site.register(Novinky, NovinkyAdmin) | ||||
|  |  | |||
|  | @ -77,7 +77,13 @@ class ProblemAutocomplete(autocomplete_light.AutocompleteModelBase): | |||
| 
 | ||||
|     def choice_label(self, p): | ||||
|         if p.stav == Problem.STAV_ZADANY: | ||||
|             return u"%s (%s, %s.%s)" % (p.nazev, p.typ, p.cislo_zadani.rocnik.rocnik, p.kod_v_rocniku()) | ||||
|             popisek = "" | ||||
|             try: | ||||
|                 popisek = u"%s (%s, %s.%s)" % (p.nazev, p.typ, p.cislo_zadani.rocnik.rocnik, p.kod_v_rocniku()) | ||||
|             except: | ||||
|                 #popisek = u"%s (%s, %s.%s)" % (p.nazev, p.typ, p.stav) | ||||
|                 popisek = "CHYBA" | ||||
|             return popisek | ||||
|         else: | ||||
|             return u"%s (%s, %s)" % (p.nazev, p.typ, p.stav) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										20
									
								
								seminar/management/commands/auth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,20 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| from django.core.management.base import NoArgsCommand | ||||
| from django.contrib.sessions.models import Session | ||||
| from django.contrib.auth.models import User | ||||
| 
 | ||||
| class Command(NoArgsCommand): | ||||
|     u"""Vypiš username přihlášeného orga s daným session_key. | ||||
| 
 | ||||
|     Příkaz pro manage.py, který ze vstupu přečte session_key (tak, jak je | ||||
|     uložen v cookie sessionid) a pokud session existuje a příslušný přihlášený | ||||
|     uživatel má právo přihlásit se do admina, vypíše jeho username. | ||||
|     """ | ||||
|     def handle_noargs(self, **options): | ||||
|         session_key = raw_input() | ||||
|         s = Session.objects.get(pk=session_key).get_decoded() | ||||
|         user_id = s['_auth_user_id'] | ||||
|         user = User.objects.get(pk=user_id) | ||||
|         if user.is_staff: | ||||
|             print(user.username) | ||||
							
								
								
									
										44
									
								
								seminar/migrations/0032_cislo_pdf_blank_typos.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,44 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import models, migrations | ||||
| import django_countries.fields | ||||
| import seminar.models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0031_cislo_pdf'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterModelOptions( | ||||
|             name='soustredeni', | ||||
|             options={'ordering': ['-rocnik__rocnik', '-datum_zacatku'], 'verbose_name': 'Soust\u0159ed\u011bn\xed', 'verbose_name_plural': 'Soust\u0159ed\u011bn\xed'}, | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='cislo', | ||||
|             name='cislo', | ||||
|             field=models.CharField(help_text='V\u011bt\u0161inou jen "1", vyj\xedme\u010dn\u011b "7-8", lexikograficky ur\u010duje po\u0159ad\xed v ro\u010dn\xedku!', max_length=32, verbose_name='n\xe1zev \u010d\xedsla', db_index=True), | ||||
|             preserve_default=True, | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='cislo', | ||||
|             name='pdf', | ||||
|             field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=seminar.models.cislo_pdf_filename, null=True, verbose_name='pdf', blank=True), | ||||
|             preserve_default=True, | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='problem', | ||||
|             name='typ', | ||||
|             field=models.CharField(default=b'uloha', max_length=32, verbose_name='typ probl\xe9mu', choices=[(b'uloha', '\xdaloha'), (b'tema', 'T\xe9ma'), (b'serial', 'Seri\xe1l'), (b'org-clanek', 'Organiz\xe1torsk\xfd \u010dl\xe1nek'), (b'res-clanek', '\u0158e\u0161itelsk\xfd \u010dl\xe1nek')]), | ||||
|             preserve_default=True, | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='skola', | ||||
|             name='stat', | ||||
|             field=django_countries.fields.CountryField(default=b'CZ', help_text='ISO 3166-1 k\xf3d zem\u011b velk\xfdmi p\xedsmeny (CZ, SK, ...)', max_length=2, verbose_name='st\xe1t'), | ||||
|             preserve_default=True, | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										20
									
								
								seminar/migrations/0033_organizator_studuje_popisek.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,20 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import models, migrations | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0032_cislo_pdf_blank_typos'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='organizator', | ||||
|             name='studuje', | ||||
|             field=models.CharField(help_text=b"Nap\xc5\x99. 'Studuje Obecnou fyziku (Bc.), 3. ro\xc4\x8dn\xc3\xadk', 'Vystudovala Diskr\xc3\xa9tn\xc3\xad modely a algoritmy (Mgr.)' nebo 'P\xc5\x99edn\xc3\xa1\xc5\xa1\xc3\xad na MFF'", max_length=256, null=True, verbose_name=b'Studium aj.', blank=True), | ||||
|             preserve_default=True, | ||||
|         ), | ||||
|     ] | ||||
|  | @ -83,7 +83,7 @@ class Skola(SeminarModelBase): | |||
|     kratky_nazev = models.CharField(u'zkrácený název', max_length=256, blank=True, | ||||
|         help_text="Zkrácený název pro zobrazení ve výsledkovce") | ||||
| 
 | ||||
|     # Ulice může být jen číslo  | ||||
|     # Ulice může být jen číslo | ||||
|     ulice = models.CharField(u'ulice', max_length=256) | ||||
| 
 | ||||
|     mesto = models.CharField(u'město', max_length=256) | ||||
|  | @ -93,7 +93,7 @@ class Skola(SeminarModelBase): | |||
|     # ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK) | ||||
|     # Ekvivalentní s CharField(max_length=2, default='CZ', ...) | ||||
|     stat = CountryField(u'stát', default='CZ', | ||||
|         help_text=u'ISO 3166-1 kód zeme velkými písmeny (CZ, SK, ...)') | ||||
|         help_text=u'ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)') | ||||
| 
 | ||||
|     # Jaké vzdělání škpla poskytuje? | ||||
|     je_zs = models.BooleanField(u'základní stupeň', default=True) | ||||
|  | @ -161,7 +161,7 @@ class Resitel(SeminarModelBase): | |||
|         ] | ||||
|     zasilat = models.CharField(u'kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU) | ||||
| 
 | ||||
|     # Ulice může být i jen číslo  | ||||
|     # Ulice může být i jen číslo | ||||
|     ulice = models.CharField(u'ulice', max_length=256, blank=True, default='') | ||||
| 
 | ||||
|     mesto = models.CharField(u'město', max_length=256, blank=True, default='') | ||||
|  | @ -325,7 +325,7 @@ class Cislo(SeminarModelBase): | |||
|     rocnik = models.ForeignKey(Rocnik, verbose_name=u'ročník', related_name='cisla', db_index=True) | ||||
| 
 | ||||
|     cislo = models.CharField(u'název čísla', max_length=32, db_index=True, | ||||
|         help_text=u'Většinou jen "1", vyjímečně "7-8", lexikograficky určije pořadí v ročníku!') | ||||
|         help_text=u'Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!') | ||||
| 
 | ||||
|     datum_vydani = models.DateField(u'datum vydání', blank=True, null=True, | ||||
|         help_text=u'Datum vydání finální verze') | ||||
|  | @ -349,7 +349,7 @@ class Cislo(SeminarModelBase): | |||
|     kod.short_description = u'Kód čísla' | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         # Potenciální DB HOG, pokud by se ročník neckešoval | ||||
|         # Potenciální DB HOG, pokud by se ročník necachoval | ||||
|         r = Rocnik.cached_rocnik(self.rocnik_id) | ||||
|         return force_unicode(u'%s.%s' % (r.rocnik, self.cislo, )) | ||||
| 
 | ||||
|  | @ -403,7 +403,7 @@ class Problem(SeminarModelBase): | |||
|         (TYP_TEMA, u'Téma'), | ||||
|         (TYP_SERIAL, u'Seriál'), | ||||
|         (TYP_ORG_CLANEK, u'Organizátorský článek'), | ||||
|         (TYP_RES_CLANEK, u'Řesitelský článek'), | ||||
|         (TYP_RES_CLANEK, u'Řešitelský článek'), | ||||
|         ] | ||||
|     typ = models.CharField(u'typ problému', max_length=32, choices=TYP_CHOICES, blank=False, default=TYP_ULOHA) | ||||
| 
 | ||||
|  | @ -447,18 +447,19 @@ class Problem(SeminarModelBase): | |||
| 
 | ||||
|     # Staré (do 2014) ID problému z DAKOSU -- jen u importovaných záznamů | ||||
|     import_dakos_id = models.CharField(u'importované ID s typem', max_length=32, blank=True, default='', | ||||
|         help_text=(u'ID z importu z DAKOSU s prefixem podle původu: "AZAD:xxx (MAMOPER.MM_AZAD), "' +  | ||||
|         help_text=(u'ID z importu z DAKOSU s prefixem podle původu: "AZAD:xxx (MAMOPER.MM_AZAD), "' + | ||||
|         u'"DOZ:xxx" (MAMOPER.MM_DOZ), "ZAD:rocnik.cislo.uloha.typ" (MAMOPER.MM_ZADANIA), "ULOHA:xxx" (MAMOPER.MM_ULOHY)')) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return force_unicode(u'%s' % (self.nazev, )) | ||||
| 
 | ||||
|     def kod_v_rocniku(self): | ||||
|         if self.typ == self.TYP_ULOHA: | ||||
|             return force_unicode(u"%s.u%s" % (self.cislo_zadani.cislo, self.kod,)) | ||||
|         if self.typ == self.TYP_TEMA: | ||||
|             return force_unicode(u"t%s" % (self.kod,)) | ||||
|         return '' | ||||
|         if self.stav == 'zadany': | ||||
|             if self.typ == self.TYP_ULOHA: | ||||
|                 return force_unicode(u"%s.u%s" % (self.cislo_zadani.cislo, self.kod,)) | ||||
|             if self.typ == self.TYP_TEMA: | ||||
|                 return force_unicode(u"t%s" % (self.kod,)) | ||||
|         return ' Není zadaný ' | ||||
| 
 | ||||
|     def nazev_typu(self): | ||||
|         return dict(self.TYP_CHOICES)[self.typ] | ||||
|  | @ -771,8 +772,11 @@ class Organizator(models.Model): | |||
|             null = True, blank = True) | ||||
|     organizuje_do_roku = models.IntegerField('Organizuje do roku', | ||||
|             null = True, blank = True) | ||||
|     studuje = models.CharField('Studuje', max_length = 256, | ||||
|             null = True, blank = True) | ||||
|     studuje = models.CharField('Studium aj.', max_length = 256, | ||||
|             null = True, blank = True, | ||||
|             help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', " | ||||
|             "'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo " | ||||
|             "'Přednáší na MFF'") | ||||
|     strucny_popis_organizatora = models.TextField('Stručný popis organizátora', | ||||
|             null = True, blank = True) | ||||
|     foto = models.ImageField('Fotografie organizátora', | ||||
|  | @ -789,11 +793,14 @@ class Organizator(models.Model): | |||
|         verbose_name_plural = 'Organizátoři' | ||||
| 
 | ||||
|     def save(self): | ||||
|         if self.id is not None: | ||||
|             puvodni = Organizator.objects.get(id=self.id) | ||||
|         if self.foto: | ||||
|             original = Image.open(self.foto) | ||||
|             jmeno = os.path.basename(self.foto.file.name) | ||||
|             Organizator._vyrobMiniaturu(original, jmeno, 500, self.foto) | ||||
|             Organizator._vyrobMiniaturu(original, jmeno, 200, self.foto_male) | ||||
|             if not puvodni or puvodni.foto != self.foto: | ||||
|                 original = Image.open(self.foto) | ||||
|                 jmeno = os.path.basename(self.foto.file.name) | ||||
|                 Organizator._vyrobMiniaturu(original, jmeno, 500, self.foto) | ||||
|                 Organizator._vyrobMiniaturu(original, jmeno, 200, self.foto_male) | ||||
|         super(Organizator, self).save() | ||||
| 
 | ||||
|     @staticmethod | ||||
|  |  | |||
|  | @ -29,38 +29,17 @@ | |||
|   {% endif %} | ||||
| 
 | ||||
|   {% if vysledkovka %} | ||||
|     <h3>Výsledkovka</h3> | ||||
|     <table class='vysledkovka'> | ||||
|       <tr class='border-b'> | ||||
|         <th class='border-r'># | ||||
|         <th class='border-r'>Jméno | ||||
|         <th class='border-r'>R. | ||||
|         <th class='border-r'>Odjakživa | ||||
|             {% for c in rocnik.verejna_cisla %} | ||||
|             {% if c.verejna_vysledkovka %} | ||||
|         <th class='border-r'><a href="{{ c.verejne_url }}"> | ||||
|                 {{c.rocnik.rocnik}}.{{ c.cislo }}</a> | ||||
|              {% endif %} | ||||
|         {% endfor %} | ||||
|         <th class='border-r'>Celkem | ||||
|     <h3>Výsledková listina</h3> | ||||
|     {% include "seminar/vysledkovka_rocnik.html" %} | ||||
|   {% endif %} | ||||
| 
 | ||||
|     {% for rv in vysledkovka %} | ||||
|       <tr> | ||||
|         <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} | ||||
|         <th class='border-r'> | ||||
|           {% if rv.titul %} | ||||
|             {{ rv.titul }}<sup>MM</sup> | ||||
|           {% endif %} | ||||
|           {{ rv.resitel.plne_jmeno }} | ||||
|         <td class='border-r'>{{ rv.resitel.rocnik }} | ||||
|         <td class='border-r'>{{ rv.body_odjakziva }} | ||||
|         {% for b in rv.body_cisla %} | ||||
|         <td class='border-r'>{{ b }} | ||||
|         {% endfor %} | ||||
|         <td class='border-r'><b>{{ rv.body_rocnik }}</b> | ||||
|       </tr> | ||||
|     {% endfor %} | ||||
|     </table> | ||||
|   {% if user.is_staff and vysledkovka_s_neverejnymi %} | ||||
|     <div class='mam-org-only'> | ||||
|     <h3>Výsledková listina včetně neveřejných bodů</h3> | ||||
|       {% with vysledkovka_s_neverejnymi as vysledkovka %} | ||||
|         {% include "seminar/vysledkovka_rocnik.html" %} | ||||
|       {% endwith %} | ||||
|     </div> | ||||
|   {% endif %} | ||||
| 
 | ||||
| </div> | ||||
|  |  | |||
|  | @ -53,7 +53,7 @@ | |||
|               Aktivní v letech {{org.organizuje_od_roku | default:"?" }}–{{org.organizuje_do_roku | default:"?" }} | ||||
|           {% endif %} | ||||
|       {% if org.studuje %} | ||||
|         <li>Studuje: {{org.studuje}} | ||||
|         <li>{{org.studuje}} | ||||
|       {% endif %} | ||||
|       {% if org.user.email %} | ||||
|         <li>Pošta: | ||||
|  |  | |||
							
								
								
									
										29
									
								
								seminar/templates/seminar/vysledkovka_rocnik.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,29 @@ | |||
| <table class='vysledkovka'> | ||||
|   <tr class='border-b'> | ||||
|     <th class='border-r'># | ||||
|     <th class='border-r'>Jméno | ||||
|     <th class='border-r'>R. | ||||
|     <th class='border-r'>Odjakživa | ||||
|         {% for c in vysledkovka.cisla %} | ||||
|     <th class='border-r'><a href="{{ c.verejne_url }}"> | ||||
|             {{c.rocnik.rocnik}}.{{ c.cislo }}</a> | ||||
|     {% endfor %} | ||||
|     <th class='border-r'>Celkem | ||||
| 
 | ||||
| {% for rv in vysledkovka.radky %} | ||||
|   <tr> | ||||
|     <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} | ||||
|     <th class='border-r'> | ||||
|       {% if rv.titul %} | ||||
|         {{ rv.titul }}<sup>MM</sup> | ||||
|       {% endif %} | ||||
|       {{ rv.resitel.plne_jmeno }} | ||||
|     <td class='border-r'>{{ rv.resitel.rocnik }} | ||||
|     <td class='border-r'>{{ rv.body_odjakziva }} | ||||
|     {% for b in rv.body_cisla %} | ||||
|     <td class='border-r'>{{ b }} | ||||
|     {% endfor %} | ||||
|     <td class='border-r'><b>{{ rv.body_rocnik }}</b> | ||||
|   </tr> | ||||
| {% endfor %} | ||||
| </table> | ||||
							
								
								
									
										35
									
								
								seminar/templates/seminar/zadani/AktualniVysledkovka.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,35 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| 
 | ||||
| {% block submenu %} | ||||
|   {% with "vysledkova-listina" as selected %} | ||||
|   {% include 'seminar/zadani/submenu.html' %} | ||||
|   {% endwith %} | ||||
| {% endblock submenu %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| {% with nastaveni.aktualni_rocnik as rocnik %} | ||||
| 
 | ||||
|   <h2> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       Výsledky | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h2> | ||||
| 
 | ||||
|   {% if vysledkovka %} | ||||
|     {% include "seminar/vysledkovka_rocnik.html" %} | ||||
|   {% else %} | ||||
|     V tomto ročníku zatím žádné výsledky nejsou | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if user.is_staff and vysledkovka_s_neverejnymi %} | ||||
|     <div class='mam-org-only'> | ||||
|     <h2>Výsledky včetně neveřejných</h2> | ||||
|     {% with vysledkovka_s_neverejnymi as vysledkovka %} | ||||
|       {% include "seminar/vysledkovka_rocnik.html" %} | ||||
|     {% endwith %} | ||||
|     </div> | ||||
|   {% endif %} | ||||
| 
 | ||||
| {% endwith %} | ||||
| {% endblock content %} | ||||
|  | @ -18,8 +18,6 @@ | |||
|       {% if ac.zadane_problemy.all %} | ||||
| 	<div class="zadani_azad_termin"> | ||||
| 		Termín odeslání {{ac.cislo}}. série: {{ac.datum_deadline}} | ||||
| 		<br> | ||||
|         Termín odeslání 1. série pro účast na soustředění: 21. září 2015 | ||||
| 	</div> | ||||
|       {% endif %} | ||||
|       {#TODO a co speciální deadline pro účast na soustředění? #} | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ urlpatterns = patterns('', | |||
| 
 | ||||
|     url(r'^zadani/aktualni/$', views.AktualniZadaniView, name='seminar_aktualni_zadani'), | ||||
|     url(r'^zadani/temata/$', views.ZadaniTemataView, name='seminar_temata'), | ||||
|     url(r'^zadani/vysledkova-listina/$', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), | ||||
|     url(r'^$', views.TitulniStranaView.as_view(), name='titulni_strana'), | ||||
|     url(r'^stare-novinky/$', views.StareNovinkyView.as_view(), name='stare_novinky'), | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										126
									
								
								seminar/views.py
									
									
									
									
									
								
							
							
						
						|  | @ -15,17 +15,22 @@ from datetime import timedelta, date, datetime | |||
| from itertools import groupby | ||||
| 
 | ||||
| 
 | ||||
| def verejna_temata(rocnik): | ||||
|     """Vrací queryset zveřejněných témat v daném ročníku. | ||||
|     """ | ||||
|     return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod') | ||||
| 
 | ||||
| 
 | ||||
| def AktualniZadaniView(request): | ||||
|     nastaveni = get_object_or_404(Nastaveni) | ||||
|     problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany') | ||||
|     ulohy = problemy.filter(typ = 'uloha').order_by('kod') | ||||
|     serialy = problemy.filter(typ = 'serial').order_by('kod') | ||||
|     temata = problemy.filter(typ = 'tema').order_by('kod') | ||||
|     jednorazove_problemy = [ulohy, serialy] | ||||
|     return render(request, 'seminar/zadani/AktualniZadani.html', | ||||
|             {'nastaveni': nastaveni, | ||||
|              'jednorazove_problemy': jednorazove_problemy, | ||||
|              'temata': temata, | ||||
|              'temata': verejna_temata(nastaveni.aktualni_rocnik), | ||||
|                 }, | ||||
|             ) | ||||
| 
 | ||||
|  | @ -33,10 +38,23 @@ def ZadaniTemataView(request): | |||
|     nastaveni = get_object_or_404(Nastaveni) | ||||
|     return render(request, 'seminar/zadani/Temata.html', | ||||
|         { | ||||
|             'temata': Problem.objects.filter(typ=Problem.TYP_TEMA, stav=Problem.STAV_ZADANY, cislo_zadani__rocnik=nastaveni.aktualni_rocnik).order_by('kod'), | ||||
|             'temata': verejna_temata(nastaveni.aktualni_rocnik) | ||||
|         } | ||||
|     ) | ||||
| 
 | ||||
| def ZadaniAktualniVysledkovkaView(request): | ||||
|     nastaveni = get_object_or_404(Nastaveni) | ||||
|     vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik) | ||||
|     vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False) | ||||
|     return render(request, 'seminar/zadani/AktualniVysledkovka.html', | ||||
|         { | ||||
|             'nastaveni': nastaveni, | ||||
|             'vysledkovka': vysledkovka, | ||||
|             'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi, | ||||
|         } | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| ### Titulni strana | ||||
| 
 | ||||
| class TitulniStranaView(generic.ListView): | ||||
|  | @ -110,6 +128,63 @@ def sloupec_s_poradim(vysledky): | |||
|     return poradi_l | ||||
| 
 | ||||
| 
 | ||||
| def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||
|     """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||
|     formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | ||||
|     """ | ||||
| 
 | ||||
|     #vyberu vsechny vysledky z rocniku | ||||
|     cisla_v_rocniku = VysledkyKCisluZaRocnik.objects.filter(cislo__rocnik=rocnik).order_by('cislo') | ||||
|     if jen_verejne: | ||||
|         cisla_v_rocniku = cisla_v_rocniku.filter(cislo__verejna_vysledkovka=True) | ||||
| 
 | ||||
|     #pokud žádné nejsou, výsledkovka se nezobrazí | ||||
|     if not cisla_v_rocniku: | ||||
|         return None | ||||
| 
 | ||||
|     #vybere vsechny vysledky z posledniho (verejneho) cisla a setridi sestupne dle bodu | ||||
|     vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].cislo).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel')) | ||||
| 
 | ||||
|     class Vysledkovka: | ||||
|         def __init__(self): | ||||
|             self.radky = [] | ||||
|             self.cisla = [] | ||||
| 
 | ||||
|     vysledkovka = Vysledkovka() | ||||
|     vysledkovka.cisla = (rocnik.verejne_vysledkovky_cisla() if jen_verejne else rocnik.cisla.all().order_by('cislo')) | ||||
| 
 | ||||
|     # doplníme některé údaje do řádků výsledkovky pro řešitele ve skupině | ||||
|     for poradi, v in zip(sloupec_s_poradim(vysledky), vysledky): | ||||
|         v.poradi = poradi | ||||
|         v.resitel.rocnik = v.resitel.rocnik(rocnik) | ||||
| 
 | ||||
|         verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].cislo) | ||||
|         if jen_verejne: | ||||
|             verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True) | ||||
| 
 | ||||
|         v.body_odjakziva = verejne_vysl_odjakziva.filter(resitel = v.resitel)[0].body | ||||
|         v.titul = v.resitel.get_titul(v.body_odjakziva) | ||||
|         v.body_rocnik = v.body | ||||
|         v.body_cisla = [] | ||||
| 
 | ||||
|         #pokud pro dany rok a cislo nema resitel vysledky, ma defaultne 0 | ||||
|         for cis in vysledkovka.cisla: | ||||
|             if not jen_verejne or cis.verejna_vysledkovka: | ||||
|                 #seznam vysledku se spravnym rocnikem a cislem pro resitele | ||||
|                 #zobrazim jen je-li vysledkovka verejna | ||||
|                 body_za_cislo = VysledkyZaCislo.objects.filter(cislo__rocnik=rocnik).filter(cislo = cis).filter(resitel = v.resitel) | ||||
|                 if body_za_cislo: | ||||
|                     #neprazdne vysledky by mely obsahovat prave jeden vysledek | ||||
|                     v.body_cisla.append(body_za_cislo[0].body) | ||||
|                 else: | ||||
|                     #resitel nema za cislo body | ||||
|                     v.body_cisla.append(0) | ||||
| 
 | ||||
|         vysledkovka.radky.append(v) | ||||
| 
 | ||||
|     return vysledkovka | ||||
| 
 | ||||
| 
 | ||||
| class RocnikView(generic.DetailView): | ||||
|     model = Rocnik | ||||
|     template_name = 'seminar/archiv/rocnik.html' | ||||
|  | @ -131,46 +206,9 @@ class RocnikView(generic.DetailView): | |||
|     def get_context_data(self, **kwargs): | ||||
|         context = super(RocnikView, self).get_context_data(**kwargs) | ||||
| 
 | ||||
|         cisla_v_rocniku = VysledkyKCisluZaRocnik.objects.filter(cislo__verejna_vysledkovka = True).filter(cislo__rocnik = context['rocnik']).order_by('cislo') | ||||
|         #vyberu vsechny verejne vysledky z rocniku | ||||
|         #pokud žádné nejsou, výsledkovka se nezobrazí | ||||
|         if cisla_v_rocniku: | ||||
|             vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].cislo).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel')) | ||||
|             #vybere vsechny vysledky z posledniho verejneho cisla a setridi sestupne dle bodu | ||||
|             vysledkovka = [] | ||||
| 
 | ||||
|             # doplníme některé údaje do řádků výsledkovky pro řešitele ve skupině | ||||
|             for poradi, v in zip(sloupec_s_poradim(vysledky), vysledky): | ||||
|                 v.poradi = poradi | ||||
|                 v.resitel.rocnik = v.resitel.rocnik(context['rocnik']) | ||||
| 
 | ||||
|                 verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__verejna_vysledkovka = True).filter(cislo__rocnik = context['rocnik']).filter(cislo = cisla_v_rocniku[0].cislo) | ||||
| 
 | ||||
|                 v.body_odjakziva = verejne_vysl_odjakziva.filter(resitel = v.resitel)[0].body | ||||
|                 v.titul = v.resitel.get_titul(v.body_odjakziva) | ||||
|                 v.body_rocnik = v.body | ||||
|                 v.body_cisla = [] | ||||
| 
 | ||||
|                 #pokud pro dany rok a cislo nema resitel vysledky, ma defaultne 0 | ||||
|                 for cis in context['rocnik'].verejna_cisla(): | ||||
|                     if cis.verejna_vysledkovka: | ||||
|                         body_za_cislo = VysledkyZaCislo.objects.filter(cislo__rocnik = context['rocnik']).filter(cislo = cis).filter(resitel = v.resitel) | ||||
|                         #seznam vysledku se spravnym rocnikem a cislem pro resitele | ||||
|                         #zobrazim jen je-li vysledkovka verejna | ||||
|                         if body_za_cislo: | ||||
|                             v.body_cisla.append(body_za_cislo[0].body) | ||||
|                             #neprazdne vysledky by mely obsahovat prave jeden vysledek | ||||
|                         else: | ||||
|                             v.body_cisla.append(0) | ||||
|                             #resitel nema za cislo body | ||||
| 
 | ||||
|                 vysledkovka.append(v) | ||||
| 
 | ||||
| 
 | ||||
|             context['vysledkovka'] = vysledkovka | ||||
| 
 | ||||
|         temata_v_rocniku = Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=context['rocnik']).order_by('kod') | ||||
|         context['temata_v_rocniku'] = temata_v_rocniku | ||||
|         context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) | ||||
|         context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) | ||||
|         context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) | ||||
| 
 | ||||
|         return context | ||||
| 
 | ||||
|  | @ -227,7 +265,7 @@ class CisloView(generic.DetailView): | |||
| 
 | ||||
|         resene_problemy = Problem.objects.filter(cislo_reseni=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('cislo_zadani__cislo', 'kod') | ||||
| 
 | ||||
|         problemy = sorted(list(set([r.problem for r in reseni])), key=lambda x:(0 if x.typ==Problem.TYP_ULOHA else 1, x.kod_v_rocniku)) | ||||
|         problemy = sorted(set(r.problem for r in reseni), key=lambda x:(0 if x.typ==Problem.TYP_ULOHA else 1, x.kod_v_rocniku())) | ||||
|         #setridi problemy podle typu a poradi zadani | ||||
|         problem_index = {} | ||||
|         for i in range(len(problemy)): | ||||
|  |  | |||
 Bc. Petr Pecha
						Bc. Petr Pecha