Merge branch 'data_migrations' into test
This commit is contained in:
		
						commit
						99d7bd50a1
					
				
					 54 changed files with 91581 additions and 988 deletions
				
			
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -34,6 +34,8 @@ install_web: venv_check | |||
| 	pip install --upgrade setuptools | ||||
| 	# Instalace závislostí webu | ||||
| 	pip install -r requirements.txt --upgrade | ||||
| 	# Po vygenerování testdat spusť  ./manage.py loaddata sitetree_new.json, ať máš menu | ||||
| 	# Pro synchronizaci flatpages spusť make sync_prod_flatpages | ||||
| 
 | ||||
| install_venv: | ||||
| 	${VENV} ${VENV_PATH} | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										10
									
								
								mamweb/routers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								mamweb/routers.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| from rest_framework import routers | ||||
| from seminar import viewsets as vs | ||||
| 
 | ||||
| router = routers.DefaultRouter() | ||||
| 
 | ||||
| router.register(r'ulohavzoraknode', vs.UlohaVzorakNodeViewSet,basename='ulohavzoraknode') | ||||
| router.register(r'text', vs.TextViewSet) | ||||
| router.register(r'textnode', vs.TextNodeViewSet) | ||||
| router.register(r'castnode', vs.CastNodeViewSet) | ||||
| 
 | ||||
|  | @ -120,6 +120,9 @@ INSTALLED_APPS = ( | |||
|     'imagekit', | ||||
| 
 | ||||
|     'polymorphic', | ||||
|      | ||||
|     'webpack_loader', | ||||
|     'rest_framework', | ||||
| 
 | ||||
|     # MaMweb | ||||
|     'mamweb', | ||||
|  | @ -184,6 +187,27 @@ CKEDITOR_CONFIGS = { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| # Webpack loader | ||||
| VUE_FRONTEND_DIR = os.path.join(BASE_DIR, 'vue_frontend') | ||||
| 
 | ||||
| WEBPACK_LOADER = { | ||||
|     'DEFAULT': { | ||||
|         'CACHE': False, | ||||
|         'BUNDLE_DIR_NAME': 'vue/',  # must end with slash | ||||
|         'STATS_FILE': os.path.join(VUE_FRONTEND_DIR, 'webpack-stats.json'), | ||||
|         'POLL_INTERVAL': 0.1, | ||||
|         'TIMEOUT': None, | ||||
|         'IGNORE': [r'.+\.hot-update.js', r'.+\.map'] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| # Dajngo REST Framework | ||||
| 
 | ||||
| REST_FRAMEWORK = { | ||||
|     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', | ||||
|     'PAGE_SIZE': 100 | ||||
| } | ||||
| 
 | ||||
| # Comments | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										38
									
								
								mamweb/static/css/mamweb-dev.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								mamweb/static/css/mamweb-dev.css
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| /* | ||||
| .pink { | ||||
| 	background-color: #ffc0cb; | ||||
| } | ||||
| 
 | ||||
| div.borderized { | ||||
| 	border-style: solid; | ||||
| 	border-radius: 5px; | ||||
| 	padding: 5px; | ||||
| 	padding-right: 20px; | ||||
| } | ||||
| 
 | ||||
| div.tnmenu { | ||||
| 	float: right; | ||||
| 	margin-right: 0px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| div.parent { | ||||
| 	border-width: 2px; | ||||
| } | ||||
| 
 | ||||
| div.children { | ||||
| 	border-width: 1px; | ||||
| } | ||||
| 
 | ||||
| div.node_type { | ||||
| 	background-color: #d4d4d4; | ||||
| } | ||||
| 
 | ||||
| .hidden-tn { | ||||
| 	display: none; | ||||
| } | ||||
| 
 | ||||
| /*test*/ | ||||
| h1 { | ||||
| 	color: chartreuse; | ||||
| } | ||||
|  | @ -101,6 +101,26 @@ h6 { | |||
| 	color: black; | ||||
| } | ||||
| 
 | ||||
| .button { | ||||
| 	margin: 10px 0px 10px 0px; | ||||
| 	padding: 4px 0; /*vertikální centování textu*/ | ||||
| 	text-align: center; | ||||
| 	background-color: #e84e10; | ||||
| 	color: #fffbf6; | ||||
| 	font-size: 150%; | ||||
| 	font-weight: bold; | ||||
| 	font-variant: small-caps; | ||||
| 	filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); | ||||
| } | ||||
| 
 | ||||
| .button:hover { | ||||
| 	position: relative; | ||||
| 	top: 2px; | ||||
| 	left:2px; | ||||
| 	background-color: #df490e; | ||||
| ; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .org-logged-in .mam-text-plugin { | ||||
|   border: dashed 1px #6a0043; | ||||
|  | @ -286,7 +306,11 @@ ul.submenu li>a:hover { | |||
| 	color: black; | ||||
| } | ||||
| 
 | ||||
| ul.menu li.active>a, ul.submenu li.active>a { | ||||
| ul.menu li.active>a { | ||||
| 	color: #f9d59e; | ||||
| } | ||||
| 
 | ||||
| ul.submenu li.active>a { | ||||
| 	color: black; | ||||
| } | ||||
| 
 | ||||
|  | @ -367,13 +391,82 @@ input[type="file"] { | |||
| } | ||||
| 
 | ||||
| .field-with-comment:hover span.field-comment{ | ||||
|   display:block; | ||||
|   display: block; | ||||
| } | ||||
| 
 | ||||
| input { | ||||
| 	margin: 5px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* titulni stranka */ | ||||
| 
 | ||||
| .titulnistrana { | ||||
| 	display: flex; | ||||
| 	text-align: justify; | ||||
| } | ||||
| 
 | ||||
| .titulnistrana h1 { | ||||
| 	text-align: center; | ||||
| } | ||||
| 
 | ||||
| .zjistit_vic{ | ||||
|   text-align: center; | ||||
|   margin-bottom: 30px; | ||||
| } | ||||
| 
 | ||||
| .zjistit_vic hr { | ||||
| 	display: none; | ||||
| } | ||||
| 
 | ||||
| .graf-svg { | ||||
| 	display: flex; | ||||
| } | ||||
| 
 | ||||
| #svg-graf { | ||||
| 	width: 100%; | ||||
| 	height: auto; | ||||
| 	margin: 30px; | ||||
| } | ||||
| 
 | ||||
| .titulnistrana_obsah { | ||||
| 	width: 66%; | ||||
| } | ||||
| 
 | ||||
| .vitej_titulka, .temata_titulka { | ||||
| 	width: 49%; | ||||
| 	padding: 10px; | ||||
| 	display: table-cell; | ||||
| } | ||||
| 
 | ||||
| .titulnistrana_novinky { | ||||
| 	width: 33%; | ||||
| 	padding: 10px; | ||||
| } | ||||
| 
 | ||||
| .novinka_obrazek { | ||||
| 	margin: 10px 0px 10px 0px; | ||||
| 	width: 100%; | ||||
| } | ||||
| 
 | ||||
| .novinka_datum { | ||||
| 	font-weight: bold; | ||||
| } | ||||
| 
 | ||||
| .novinka_autor { | ||||
| 	text-align: right; | ||||
| 	font-style: italic; | ||||
| } | ||||
| 
 | ||||
| div.org-text { | ||||
|     font-style: italic; | ||||
| } | ||||
| 
 | ||||
| div.odpocet { | ||||
| 	margin: 20px; | ||||
| 	text-align: center; | ||||
| } | ||||
| 
 | ||||
| /********************** | ||||
|  * Zmenšování displeje | ||||
| ***********************/ | ||||
|  | @ -403,42 +496,22 @@ input { | |||
| 		width: 100%; | ||||
| 	} | ||||
| 
 | ||||
|   div.novinky{ | ||||
|     max-width: 100%; | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
|     float: none; | ||||
|   } | ||||
| 
 | ||||
|   div.graf{ | ||||
| 		width: 70%; | ||||
|     float: none; | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
| 		margin-top: 10px; | ||||
|   } | ||||
| 	#svg-graf{ | ||||
| 		width: 100%; | ||||
| 		height: auto;; | ||||
| 	ul.menu { | ||||
| 		font-size: 90%; | ||||
| 		margin-top: -7px; | ||||
| 	} | ||||
| 
 | ||||
| ul.menu { | ||||
| 	font-size: 90%; | ||||
| 	margin-top: -7px; | ||||
| } | ||||
| 	ul.menu li { | ||||
| 		margin-top: 10px; /* posunutí textu hlavního menu níže */ | ||||
| 	} | ||||
| 
 | ||||
| ul.menu li { | ||||
| 	margin-top: 10px; /* posunutí textu hlavního menu níže */ | ||||
| } | ||||
| 
 | ||||
| ul.submenu li { | ||||
| 	margin-top: 0px; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ | ||||
| } | ||||
| 
 | ||||
| ul.submenu { | ||||
| 	margin-top: 8px; /* mezera mezi hlavním menu a submenu */ | ||||
| } | ||||
| 	ul.submenu li { | ||||
| 		margin-top: 0px; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ | ||||
| 	} | ||||
| 
 | ||||
| 	ul.submenu { | ||||
| 		margin-top: 8px; /* mezera mezi hlavním menu a submenu */ | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | @ -470,11 +543,53 @@ ul.submenu { | |||
| 		display: inline-grid; | ||||
| 		max-width: 300px; | ||||
| 	} | ||||
| 
 | ||||
| 	/* titulni stranka */ | ||||
| 
 | ||||
| 	.titulnistrana { | ||||
| 		display: block; | ||||
| 	} | ||||
| 
 | ||||
| 	.graf { | ||||
| 		padding-top: 40px; | ||||
| 	} | ||||
| 
 | ||||
| 	.titulnistrana_obsah { | ||||
| 		width: 100%; | ||||
| 	} | ||||
| 
 | ||||
| 	.vitej_titulka, .temata_titulka { | ||||
| 		width: 49%; | ||||
| 		padding: 10px; | ||||
| 		display: table-cell; | ||||
| 	} | ||||
| 
 | ||||
| 	.titulnistrana_novinky { | ||||
| 		width: 100%; | ||||
| 		max-width: 500px; | ||||
| 		padding: 10px; | ||||
| 		margin: auto; | ||||
| 	} | ||||
| 
 | ||||
| 	#svg-graf { | ||||
| 		width: 100%; | ||||
| 		max-width: 500px; | ||||
| 		padding: 10px; | ||||
| 		margin: auto; | ||||
| 	} | ||||
| 
 | ||||
| 	.zjistit_vic hr { | ||||
| 		display: flex; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* malý tablet, mobil */ | ||||
| @media (max-width: 650px) { | ||||
| 
 | ||||
| 	.no-mobile{ | ||||
| 		display: none; | ||||
| 	} | ||||
| 
 | ||||
|   #hide-if-small.login-bar-flatpage { | ||||
| 		display: none; | ||||
| 	} | ||||
|  | @ -611,19 +726,35 @@ ul.submenu { | |||
| 		text-align: justify; | ||||
| 	} | ||||
| 
 | ||||
|   div.novinky { | ||||
|     max-width: 100%; | ||||
|     float: none; | ||||
|   } | ||||
| 
 | ||||
| 	div.graf { | ||||
| 		width: 100%; | ||||
| 	} | ||||
| 
 | ||||
| 	table.form td, table.form tr { | ||||
|  		display: inherit; | ||||
|  	} | ||||
| 
 | ||||
| 	 /* titulni stranka */ | ||||
| 
 | ||||
| 	.titulnistrana { | ||||
| 		display: block; | ||||
| 	} | ||||
| 
 | ||||
| 	.graf { | ||||
| 		padding-top: 40px; | ||||
| 	} | ||||
| 
 | ||||
| 	.titulnistrana_obsah { | ||||
| 		width: 100%; | ||||
| 	} | ||||
| 
 | ||||
| 	.vitej_titulka, .temata_titulka { | ||||
| 		width: 100%; | ||||
| 		padding: 10px; | ||||
| 		display: block; | ||||
| 	} | ||||
| 
 | ||||
| 	.titulnistrana_novinky { | ||||
| 		width: 100%; | ||||
| 		padding: 10px; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /*stránka organizátorů*/ | ||||
|  | @ -704,11 +835,6 @@ div.org_email { | |||
|  backface-visibility: hidden; | ||||
| } | ||||
| 
 | ||||
| /* Style the front side (fallback if image is missing) */ | ||||
| .flip-card-front { | ||||
|  background-color: #bbb; | ||||
| } | ||||
| 
 | ||||
| div.flip-card-foto img { | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
|  | @ -724,6 +850,10 @@ div.flip-card-foto img { | |||
|  padding-top: 20px; | ||||
| } | ||||
| 
 | ||||
| #archiv.flip-card-back { | ||||
| 	background-color: white; | ||||
| } | ||||
| 
 | ||||
| /* karty archiv */ | ||||
| 
 | ||||
| div.popis_rocniku { | ||||
|  | @ -920,46 +1050,8 @@ div.cislo_odkazy ul { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* titulni stranka */ | ||||
| /**/ | ||||
| 
 | ||||
| .zjistit_vic{ | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .graf{ | ||||
|   float: left; | ||||
| } | ||||
| 
 | ||||
| .novinky{ | ||||
|   float: right; | ||||
|   max-width: 42%; | ||||
| } | ||||
| 
 | ||||
| .novinka_obrazek img { | ||||
|     margin-bottom: 15px; | ||||
| } | ||||
| 
 | ||||
| div.novinka_obrazek { | ||||
| 	width: 100%; | ||||
| 	max-width: 400px; /*FIXME*/ | ||||
| } | ||||
| 
 | ||||
| div.org-text { | ||||
|     font-style: italic; | ||||
| } | ||||
| 
 | ||||
| div.nahledy_cisel { | ||||
|     float: right; | ||||
|     height: 297px; | ||||
|     width: 420px; | ||||
|     position: relative; | ||||
|     margin-right: 10%; | ||||
|     margin-bottom: 50px; | ||||
| } | ||||
| 
 | ||||
| div.nahledy_cisel div, div.nahledy_cisel img { | ||||
|     position: absolute; | ||||
| } | ||||
| ul.form { | ||||
| 	list-style-type: none; | ||||
| 	padding-left: 0px; | ||||
|  | @ -984,3 +1076,20 @@ p.gdpr { | |||
| div.gdpr { | ||||
| 	font-size: 6pt; | ||||
| } | ||||
| 
 | ||||
| /* Jak řešit */ | ||||
| 
 | ||||
| .jakresit img { | ||||
| 	width: 33%; | ||||
| 	padding: 10px; | ||||
| 	filter: none; | ||||
| } | ||||
| 
 | ||||
| @media(max-width: 860px) { | ||||
| 	.jakresit img { | ||||
| 		margin: auto; | ||||
| 		display: grid; | ||||
| 		width: 100%; | ||||
| 		max-width: 360px; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										26847
									
								
								mamweb/static/images/jakresit_1.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26847
									
								
								mamweb/static/images/jakresit_1.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| After Width: | Height: | Size: 1.8 MiB | 
							
								
								
									
										26847
									
								
								mamweb/static/images/jakresit_2.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26847
									
								
								mamweb/static/images/jakresit_2.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| After Width: | Height: | Size: 1.8 MiB | 
							
								
								
									
										26847
									
								
								mamweb/static/images/jakresit_3.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26847
									
								
								mamweb/static/images/jakresit_3.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| After Width: | Height: | Size: 1.8 MiB | 
|  | @ -6,7 +6,8 @@ | |||
|     <title>{% block title %}{% block nadpis1a %}{% endblock %} – Korespondenční seminář M&M{% endblock title %}</title> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <link rel="shortcut icon" href="{% static 'images/MATFYZ_MM_barevne.svg' %}" type="image/x-icon"> | ||||
|     {% render_block "css" %} | ||||
|     {% render_block css %} | ||||
|     {% block custom_css %}{% endblock %} | ||||
|     <link href="{% static 'css/bootstrap-theme.css' %}" rel="stylesheet"> | ||||
|     <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> | ||||
|     <link href="{% static 'css/mamweb.css' %}" rel="stylesheet"> | ||||
|  | @ -56,14 +57,17 @@ | |||
| 		<div class='col-md-12'> | ||||
|           <a href='/'> | ||||
| 	    <div id="title" >M&M - korespondenční seminář a časopis MFF UK</div> | ||||
|             <div id="header" style="background-image: url('{{ fotka }}')"> | ||||
|             <div id="header"> | ||||
|                 <div class="no-mobile" style="background-image: url('{{ fotka }}')"> | ||||
|                 {# TODO style=… není fancy řešení, ale u <img> se bojím, že mi to rozbije vzhled #} | ||||
| 		{% sitetree_menu from "main_menu" include "trunk" template "logo.html" %} | ||||
|                 </div> | ||||
| 		<img class="logo-mobile" src="{% static 'images/logo-mobile.svg' %}" /> | ||||
|             </div> | ||||
|             </a> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class='row'> | ||||
| 	    <div class='col-md-12'> | ||||
| 
 | ||||
|  | @ -73,6 +77,9 @@ | |||
| 
 | ||||
| {# ========= MENU MOBILE ========== #} | ||||
| 
 | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
| <!--Navbar--> | ||||
| <nav class="nav-button"> | ||||
| 
 | ||||
|  | @ -95,13 +102,15 @@ | |||
| 
 | ||||
| {# ========= END MENU ========== #} | ||||
| 
 | ||||
| 
 | ||||
|     <div class='row'> | ||||
| 	<div class='row content'> | ||||
| 	<div class='col-md-12'> | ||||
| 	    {% block content %} | ||||
| 	    {% endblock content %} | ||||
| 	</div> | ||||
| 	        </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
| 	<div class='row'> | ||||
| 	  <div class='col-md-12'> | ||||
| 		<div id="footer"> | ||||
|  | @ -110,6 +119,7 @@ | |||
| 		<p class="license-mobile">Korespondenční seminář M&M organizují převážně studenti <a href="https://www.mff.cuni.cz/">MFF UK</a>. Organizaci semináře a vydávání časopisu podporuje <a href="https://jcmf.cz/">Jednota českých matematiků a fyziků</a>. S obsahem webu M&M je možné nakládat dle licence <a href="https://creativecommons.org/licenses/by/3.0/cz/">Creative Commons Attribution 3.0</a>.</p> | ||||
| 	  </div> | ||||
| 	</div> | ||||
| 
 | ||||
|     </div> | ||||
| 
 | ||||
|     <script src="{% static 'js/bootstrap.js' %}"></script> | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ from django.views.generic.base import TemplateView | |||
| from django import views | ||||
| from django.urls import path # As per docs. | ||||
| 
 | ||||
| from .routers import router | ||||
| 
 | ||||
| urlpatterns = [ | ||||
| 
 | ||||
| 	# Admin a nastroje | ||||
|  | @ -25,6 +27,9 @@ urlpatterns = [ | |||
| 	path('comments_dj/', include('django_comments.urls')), | ||||
| 	path('comments_fl/', include('fluent_comments.urls')), | ||||
| 
 | ||||
| 	# REST API | ||||
| 	path('api/', include(router.urls)), | ||||
| 
 | ||||
| ] | ||||
| 
 | ||||
| # This is only needed when using runserver. | ||||
|  |  | |||
|  | @ -28,6 +28,9 @@ django-imagekit | |||
| django-polymorphic | ||||
| django-sitetree | ||||
| django_reverse_admin | ||||
| django-rest-framework | ||||
| django-webpack-loader | ||||
| django-rest-polymorphic | ||||
| 
 | ||||
| # Comments | ||||
| akismet==1.0.1 | ||||
|  |  | |||
|  | @ -107,6 +107,8 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin): | |||
| 		m.PohadkaNode, | ||||
| 		m.UlohaVzorakNode, | ||||
| 		m.TextNode, | ||||
| 		m.CastNode, | ||||
| 		m.OrgTextNode, | ||||
| 		] | ||||
| 
 | ||||
| 	actions = ['aktualizuj_nazvy'] | ||||
|  | @ -160,6 +162,17 @@ class TextNodeAdmin(PolymorphicChildModelAdmin): | |||
| 	base_model = m.TextNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.CastNode) | ||||
| class TextNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.CastNode | ||||
| 	show_in_index = True | ||||
| 	fields = ('nadpis',) | ||||
| 
 | ||||
| @admin.register(m.OrgTextNode) | ||||
| class TextNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.OrgTextNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| 
 | ||||
| admin.site.register(m.Nastaveni, SingletonModelAdmin) | ||||
| admin.site.register(m.Novinky) | ||||
|  |  | |||
|  | @ -277,3 +277,8 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, | |||
| 
 | ||||
| 		) | ||||
| 
 | ||||
| class NahrajObrazekKTreeNoduForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
| 		model = m.Obrazek | ||||
| 		fields = ('na_web',) | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ from reversion import revisions as reversion | |||
| from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode | ||||
| 
 | ||||
| from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) | ||||
| from seminar.treelib import safe_pred | ||||
| 
 | ||||
| from polymorphic.models import PolymorphicModel | ||||
| 
 | ||||
|  | @ -706,6 +705,7 @@ class Problem(SeminarModelBase,PolymorphicModel): | |||
| 		(STAV_SMAZANY, 'Smazaný'), | ||||
| 		] | ||||
| 	stav = models.CharField('stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH) | ||||
| 	# Téma je taky Problém, takže má stavy, "zadané" témátko je aktuálně otevřené a dá se k němu něco poslat (řešení nebo článek) | ||||
| 
 | ||||
| 	zamereni = TaggableManager(verbose_name='zaměření',  | ||||
| 		help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True) | ||||
|  | @ -745,6 +745,7 @@ class Problem(SeminarModelBase,PolymorphicModel): | |||
| 	def verejne(self): | ||||
| 		# FIXME: Tohle se liší podle typu problému, má se udělat polymorfně. | ||||
| 		# Zatím je tu jen dummy fail-safe default: nic není veřejné. | ||||
| 		# Doporučené řešení: dělat tohle podle stavu problému a veřejnosti čísla, ve kterém je | ||||
| 		return False | ||||
| 		# FIXME: Tohle je blbost | ||||
| 		return (self.cislo_zadani and self.cislo_zadani.verejne()) | ||||
|  | @ -843,9 +844,7 @@ class Text(SeminarModelBase): | |||
| 			tn.save() | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		parser = FirstTagParser()		 | ||||
| 		parser.feed(str(self.na_web)) | ||||
| 		return parser.firstTag | ||||
| 		return str(self.na_web)[:20] | ||||
| 		 | ||||
| class Uloha(Problem): | ||||
| 	class Meta: | ||||
|  | @ -1361,6 +1360,7 @@ class MezicisloNode(TreeNode): | |||
| 
 | ||||
| 	# TODO: Využít TreeLib | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		from seminar.treelib import safe_pred | ||||
| 		if safe_pred(self) is not None: | ||||
| 			if (self.prev.get_real_instance_class() != CisloNode and | ||||
| 				self.prev.get_real_instance_class() != MezicisloNode): | ||||
|  |  | |||
							
								
								
									
										18
									
								
								seminar/static/seminar/treenode_editor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/static/seminar/treenode_editor.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| function showSelectedItemForm(sel,id){ | ||||
| 	var option; | ||||
| 	var name; | ||||
| 	var div; | ||||
| 	Array.from(sel.options).forEach(function(option){ | ||||
| 		console.log(option); | ||||
| 		name = 'pridat-'+option.value+'-'+id; | ||||
| 		div = document.getElementById(name); | ||||
| 		console.log(div); | ||||
| 		div.style.display = 'none'; | ||||
| 	}); | ||||
| 	name = sel.options[sel.selectedIndex].value; | ||||
| 	name = 'pridat-'+name+'-'+id; | ||||
| 	div = document.getElementById(name); | ||||
| 	console.log(div); | ||||
| 	div.style.display = 'block'; | ||||
| 
 | ||||
| } | ||||
|  | @ -8,10 +8,6 @@ | |||
|     {% endblock %}{% endblock %} | ||||
|   </h2> | ||||
| 
 | ||||
|   <!-- <div class='nahledy_cisel'> | ||||
|     {% autoescape off %}{{ nahledy }}{% endautoescape %} | ||||
|   </div>--> | ||||
| 
 | ||||
|     {% for rocnik, url_png in object_list.items %} | ||||
| 
 | ||||
|     <div class="rocnik_pole"> | ||||
|  | @ -33,7 +29,7 @@ | |||
|       </div> | ||||
| 
 | ||||
|       </div> | ||||
|        <div class="flip-card-back"> | ||||
|        <div class="flip-card-back" id="archiv"> | ||||
|       <div class="popis_rocniku"> | ||||
|         Jednotlivá čísla: | ||||
|         <ul> | ||||
|  |  | |||
							
								
								
									
										16
									
								
								seminar/templates/seminar/jak-resit.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								seminar/templates/seminar/jak-resit.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| {% extends 'base.html' %} | ||||
| 
 | ||||
| {% load humanize %} | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| <div class=jakresit> | ||||
| 
 | ||||
| <img class="jakresit_obrazek" alt="" src="{% static 'images/jakresit_1.svg' %}" /> | ||||
| <img class="jakresit_obrazek" alt="" src="{% static 'images/jakresit_2.svg' %}" /> | ||||
| <img class="jakresit_obrazek" alt="" src="{% static 'images/jakresit_3.svg' %}" /> | ||||
| 
 | ||||
| </div> | ||||
| {% endblock %} | ||||
|  | @ -9,7 +9,7 @@ | |||
|   {% endif %} | ||||
|   {% if novinka.zverejneno or user.je_org %} | ||||
|   {# datum #} | ||||
|   <div><b>{{novinka.datum}}</b></div> | ||||
|   <div class=novinka_datum>{{novinka.datum}}</div> | ||||
|   {# text #} | ||||
|   {{ novinka.text | safe }} | ||||
|   {# obrazek #} | ||||
|  | @ -25,12 +25,12 @@ | |||
|     </div> | ||||
|   {% endif %} | ||||
|   {# autor #} | ||||
|   <div class=novinky_name><p>{{novinka.autor.first_name}} | ||||
|     {% if novinka.autor.organizator.prezdivka%} | ||||
|       „{{novinka.autor.organizator.prezdivka}}“ | ||||
|   <div class=novinka_autor> | ||||
|     {{novinka.autor.osoba.jmeno}} | ||||
|     {% if novinka.autor.osoba.prezdivka%} | ||||
|       „{{novinka.autor.osoba.prezdivka}}“ | ||||
|     {% endif %} | ||||
|     {{novinka.autor.last_name}} | ||||
|       </p> | ||||
|     {{novinka.autor.osoba.prijmeni}} | ||||
|   </div> | ||||
|   {% endif %} | ||||
|   {% if not novinka.zverejneno and user.je_org %} | ||||
|  |  | |||
							
								
								
									
										23
									
								
								seminar/templates/seminar/orphanage.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								seminar/templates/seminar/orphanage.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| {% extends "seminar/archiv/base.html" %} | ||||
| {% load staticfiles %} | ||||
| {% load sekizai_tags %} | ||||
| 
 | ||||
| {# toto z nejakeho duvodu nefunguje #} | ||||
| {% addtoblock css %} | ||||
| dfsdfs  | ||||
| <link rel="stylesheet" type="text/css" href="{% static 'css/mamweb-dev.css' %}" /> | ||||
| {% endaddtoblock "css" %} | ||||
| 
 | ||||
| {% block custom_css %} | ||||
| <link rel="stylesheet" type="text/css" href="{% static 'css/mamweb-dev.css' %}" /> | ||||
| {% endblock custom_css %} | ||||
| 
 | ||||
| {% load comments %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <ul> | ||||
| {% for obj in object_list %} | ||||
| <li>{{obj}} (id {{obj.id}})</li> | ||||
| {% endfor %} | ||||
| </ul> | ||||
| {% endblock content %} | ||||
|  | @ -1,14 +1,25 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| <p style="text-align:justify">Témata jsou texty nejen z oblasti matematiky, fyziky a informatiky, které popisují nějaký problém a jsou doprovázeny návodnými úlohami. Vaším úkolem je zamyslet se nad daným problémem a sepsat vaše úvahy ve formě krátkého textu.</p> | ||||
| 
 | ||||
| <p style="text-align:justify"><a href="/co-je-MaM/jak-resit/">Jak řešit téma?</a></p> | ||||
| 
 | ||||
| <p style="text-align:justify"> </p> | ||||
| 
 | ||||
| <h1>Aktuální témata</h1> | ||||
| 
 | ||||
| {% for tematko in tematka %} | ||||
| <h1>{{tematko.nazev}}</h1> | ||||
| <h2>{{tematko.nazev}}</h2> | ||||
| <div class="tematko-obrazek"> | ||||
|     {% if tematko.obrazek %} | ||||
|       <a href="{{tematko.obrazek.url}}" class="ref-tema-obr"><img src="{{tematko.obrazek.url}}" height="{{tematko.obrazek.height}}" alt="{{tematko.nazev}}"></a> | ||||
|     {% else %} {# pokud témátko nemá fotku, zobrazuje se defaultní obrázek #} | ||||
|       {% load static %} <img src="{% static 'images/no-photo.png' %}" height=200px alt="{{tematko.nazev}}"> | ||||
|     {% endif %} | ||||
| </div> | ||||
| 
 | ||||
| <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 %} | ||||
| {% endblock %} | ||||
|  |  | |||
|  | @ -5,32 +5,53 @@ | |||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
|   {# Uvitaci text #} | ||||
|   <h1> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       Vítej! | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h1> | ||||
| <p> | ||||
| M&M je korespondenční seminář. Několikrát do roka zdarma vydáváme časopis a v něm zajímavé podněty k přemýšlení. Ty na ně můžeš reagovat.<br> | ||||
| 
 | ||||
|  M&M je taky soutěž. Můžeš vyhrát knížky, deskovky nebo dokonce dort. Můžeš se dostat na matfyz bez přijímaček. A především s námi můžeš jet na skvělé soustředění. | ||||
| </p> | ||||
| 
 | ||||
| <div class="novinky"> | ||||
|   {% if dead %} | ||||
|   <div class="odpocet"> | ||||
|     <p><b>Do konce <a href="/zadani/aktualni/">odeslání řešení</a> {% if deadline_soustredeni %}(pro účast na soustředění) {% endif %}zbývá:<br> | ||||
|       <big>{{ted|timesince:dead}}</big></b></p> | ||||
|   </div> | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {# Novinky #} | ||||
|   <h1>Novinky</h1> | ||||
|   {% include 'seminar/novinky.html' %} | ||||
| 
 | ||||
|   <a href='/stare-novinky/'>Archiv novinek</a> | ||||
| {% if nejblizsi_deadline %} | ||||
| <div class="odpocet"> | ||||
| 	<p><b><big>Do konce <a href="/zadani/aktualni/">odeslání řešení</a> {% if typ_deadline == 'soustredeni' %}(pro účast na soustředění) {% elif typ_deadline == 'preddeadline' %}(pro otištění došlých řešení) {% endif %}zbývá: | ||||
|       {{nejblizsi_deadline|timeuntil}}</big></b></p> | ||||
| </div> | ||||
| {% endif %} | ||||
| 
 | ||||
| <div class=titulnistrana> | ||||
| 
 | ||||
| <div class="titulnistrana_obsah"> | ||||
| 
 | ||||
| <div class="vitej_titulka"> | ||||
| 
 | ||||
|   <h1> | ||||
|     {% block nadpis1a %} | ||||
|       Vítej | ||||
|     {% endblock %} | ||||
|       mezi námi | ||||
|   </h1> | ||||
| 
 | ||||
|   <div> | ||||
|     M&M je korespondenční seminář. Vydáváme časopis a v něm zajímavé podněty k přemýšlení. Ty na ně můžeš reagovat, | ||||
|     experimentovat a objevovat s námi fascinující zákoutí matiky, fyziky a informatiky. | ||||
|     <a href="auth/registrace"> <div class="button"> Zaregistruj se! </div> </a> {# FIXME odkaz #} | ||||
|     M&M je taky soutěž. Za svá řešení dostaneš body a můžeš vyhrát zajímavé ceny, dostat se | ||||
|     na Matfyz bez přijímaček a především, můžeš s námi jet na skvělé soustředění. | ||||
|     <a href="cojemam/odmeny"> <div class="button"> Co můžeš vyhrát? </div> </a> {# FIXME odkaz #} | ||||
|   </div> | ||||
|    | ||||
| </div> | ||||
| 
 | ||||
| <div class="temata_titulka"> | ||||
| 
 | ||||
|   <h1> | ||||
|       Řeš témata! | ||||
|   </h1> | ||||
| 
 | ||||
|   <div> | ||||
|     Přidej se k nám! Pusť se do řešení témát. | ||||
|     {% for tema in aktualni_temata %} | ||||
|     <a href="{{ tema.url }}"> <div class="button"> {{ tema.nazev }} </div> </a> | ||||
|     {% endfor %} | ||||
|   </div> | ||||
|    | ||||
| </div> | ||||
| 
 | ||||
| <div class="graf"> | ||||
| 
 | ||||
| <div class="graf-svg"> | ||||
|  | @ -38,8 +59,24 @@ M&M je korespondenční seminář. Několikrát do roka zdarma vydáváme č | |||
| </div> | ||||
| 
 | ||||
|   <span class="zjistit_vic"> | ||||
|     <h2><a href="/co-je-MaM/uvod/">Zjistit víc!</a></h2> | ||||
|     <h2><a href="/co-je-MaM/uvod/">Zjisti víc!</a></h2> | ||||
|     <hr> | ||||
|   </span> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| <div class="titulnistrana_novinky"> | ||||
| 
 | ||||
|   {# Novinky #} | ||||
|   <h1>Co je nového?</h1> | ||||
|   {% include 'seminar/novinky.html' %} | ||||
| 
 | ||||
|   <a href='/stare-novinky/'>Archiv novinek</a> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  |  | |||
|  | @ -1,10 +1,12 @@ | |||
| {% extends "base.html" %} | ||||
| {% load render_bundle from webpack_loader %} | ||||
| 
 | ||||
| {% load comments %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div id="app"> | ||||
|   <app></app> | ||||
| </div> | ||||
| {% render_bundle 'chunk-vendors' %} | ||||
| {% render_bundle 'vue_app_01' %} | ||||
| 
 | ||||
| {%with obj=tnldata depth=1 template_name="seminar/treenode_recursive.html" %} | ||||
|    {%include template_name%} | ||||
| {%endwith%} | ||||
| {% endblock content %} | ||||
|  |  | |||
							
								
								
									
										27
									
								
								seminar/templates/seminar/treenode_add_stub.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								seminar/templates/seminar/treenode_add_stub.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| {% load treenodes %} | ||||
| {% if kam_slug == "syn" %} | ||||
| 	{% appendableChildren obj as dostupne_typy %} | ||||
| {% else %} | ||||
| 	{% appendableChildren obj.parent as dostupne_typy %} | ||||
| {% endif %} | ||||
| 
 | ||||
| {# ulohaZadani ulohaVzorak Reseni Cast Text #} | ||||
| {% if dostupne_typy %} | ||||
| 	<div class="pink">Přidat {{kam}} | ||||
| 		<select name="pridat-typ-{{obj.node.id}}-{{kam_slug}}" onchange="showSelectedItemForm(this,'{{obj.node.id}}-{{kam_slug}}')"> | ||||
| 			{% for typ in dostupne_typy %} | ||||
| 			<option value="{{typ.0}}">{{typ.1}}</option> | ||||
| 			{% endfor %} | ||||
| 		</select> | ||||
| 		<div class="hidden-tn" id="pridat-castNode-{{obj.node.id}}-{{kam_slug}}"> | ||||
| 			Nadpis: <input name="pridat-cast-{{obj.node.id}}-{{kam_slug}}" type="text">  | ||||
| 			<button action="submit" formaction="{%url 'treenode_pridat' obj.node.id kam_slug%}">Přidat</button> | ||||
| 		</div> | ||||
| 		<div class="hidden-tn" id="pridat-textNode-{{obj.node.id}}-{{kam_slug}}"> Vytvořit</div> | ||||
| 		<div class="hidden-tn" id="pridat-reseniNode-{{obj.node.id}}-{{kam_slug}}">Vytvořit, Tady bude autocomplete na reseniNode</div> | ||||
| 		<div class="hidden-tn" id="pridat-ulohaZadaniNode-{{obj.node.id}}-{{kam_slug}}">Vytvořit zadání</div> | ||||
| 		<div class="hidden-tn" id="pridat-ulohaVzorakNode-{{obj.node.id}}-{{kam_slug}}">Vytvořit vzorák k: Tady bude autocomplete na problémy k aktuálnímu kontextu</div> | ||||
| 
 | ||||
| 
 | ||||
| 	</div> | ||||
| {% endif %}{# appendablebleChildren #} | ||||
							
								
								
									
										18
									
								
								seminar/templates/seminar/treenode_name.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/templates/seminar/treenode_name.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| {% load treenodes %} | ||||
| {% if obj.node|isRocnik %} | ||||
| <h{{depth}}> Ročník {{obj.node.rocnik}} </h{{depth}}> | ||||
| {% elif obj.node|isCislo %} | ||||
| <h{{depth}}> Číslo {{obj.node.cislo}} </h{{depth}}> | ||||
| {% elif obj.node|isTemaVCisle %} | ||||
| <h{{depth}}> Téma {{obj.node.tema.nazev}} </h{{depth}}> | ||||
| {% elif obj.node|isUlohaZadani %} | ||||
| <h{{depth}}>Úloha {{obj.node.uloha.kod_v_rocniku}} ({{obj.node.uloha.max_body}} b)</h{{depth}}> | ||||
| {% elif obj.node|isUlohaVzorak %} | ||||
| <h{{depth}}>Řešení: {{obj.node.uloha.kod_v_rocniku}}</h{{depth}}> | ||||
| {% elif obj.node|isCast %} | ||||
| <h{{depth}}> {{obj.node.nadpis}} </h{{depth}}> | ||||
| {% elif obj.node|isText %} | ||||
| {{obj.node.text.na_web}} | ||||
| {% else %} | ||||
| Objekt jiného typu {{obj.node}} | ||||
| {% endif %} | ||||
|  | @ -1,28 +1,55 @@ | |||
| {% load treenodes %} | ||||
| {# <b>{{depth}}</b> #} | ||||
| <div> | ||||
| {%   if obj.node|isRocnik %} | ||||
| <h{{depth}}> Ročník {{obj.node.rocnik}} </h{{depth}}> | ||||
| {% elif obj.node|isCislo %} | ||||
| <h{{depth}}> Číslo {{obj.node.cislo}} </h{{depth}}> | ||||
| {% elif obj.node|isTemaVCisle %} | ||||
| <h{{depth}}> Téma {{obj.node.tema.nazev}} </h{{depth}}> | ||||
| {% elif obj.node|isUlohaZadani %} | ||||
| <h{{depth}}>Úloha {{obj.node.uloha.kod_v_rocniku}} ({{obj.node.uloha.max_body}} b)</h{{depth}}> | ||||
| {% elif obj.node|isUlohaVzorak %} | ||||
| <h{{depth}}>Řešení: {{obj.node.uloha.kod_v_rocniku}}</h{{depth}}> | ||||
| {% elif obj.node|isText %} | ||||
| {{obj.node.text.na_web}} | ||||
| {% else %} | ||||
| Objekt jiného typu {{obj.node}} | ||||
| {% endif %} | ||||
|     {%if obj.children %} | ||||
| 	<div> | ||||
|          {%for ch in obj.children %} | ||||
|               {%with obj=ch depth=depth|add:"1" template_name="seminar/treenode_recursive.html" %} | ||||
|                    {%include template_name%} | ||||
|               {%endwith%} | ||||
|          {%endfor%} | ||||
| 	</div> | ||||
|     {%endif%} | ||||
| <div class="borderized parent"> | ||||
| <div class="node_type"> | ||||
| 	{{obj.node}} | ||||
| 	{{obj.node.id}} | ||||
| 	{% if obj.node|deletable %} | ||||
| 		<button type="submit" formaction="{%url 'treenode_smazat' obj.node.id%}">Smazat</button> | ||||
| 	{% endif %} | ||||
| 	{% if obj.parent and  obj.parent|editableSiblings %} | ||||
| 		<button type="submit" formaction="{%url 'treenode_odvesitpryc' obj.node.id%}">Odvěsit pryč ze stromu {{obj.parent.node}}</button> | ||||
| 	{% endif %} | ||||
| 	{% if obj|canPodvesitPred %} | ||||
| 		<button type="submit" formaction="{%url 'treenode_podvesit' obj.node.id 'pred'%}">Podvěsit pod předchozí</button> - nejsou testovací data | ||||
| 	{% endif %} | ||||
| 	{% if obj|canPodvesitZa %} | ||||
| 		<button type="submit" formaction="{%url 'treenode_podvesit' obj.node.id 'za'%}">Podvěsit pod následující</button> - nejsou testovací data | ||||
| 	{% endif %} | ||||
| 
 | ||||
| 
 | ||||
| </div> | ||||
| {% if False %} | ||||
| 	<div class="node_move"> | ||||
| 		FIXME: není zatím implementováno | ||||
| 		<button>Zvyš úroveň nadpisu</button> - nejsou testovací data | ||||
| 	</div> | ||||
| {% endif %} | ||||
| {% include "seminar/treenode_name.html" %} | ||||
| {%if obj.children %} | ||||
| 	<div class="borderized children"> | ||||
| 
 | ||||
| 		{% with kam="před" kam_slug="syn" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} | ||||
| 		{%for ch in obj.children %} | ||||
| 		 | ||||
| 			{# ----------- Vypisujeme podstrom ----------#} | ||||
| 			{%with obj=ch depth=depth|add:"1" %} {%include "seminar/treenode_recursive.html" %} {%endwith%} | ||||
| 			{# ----------- Přidáváme mezi syny / za posledního -------- #} | ||||
| 			{% if forloop.last %} | ||||
| 				{% with kam="za" kam_slug="za" obj=ch %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} | ||||
| 			{% else %} | ||||
| 				{% with kam="mezi" obj=ch kam_slug="za" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} | ||||
| 			{% endif %} | ||||
| 			{# ----------- Prohazujeme sousedy ----------#} | ||||
| 			<div class="pink"> | ||||
| 			{% if not forloop.last and ch|editableSiblings %} | ||||
| 				<button type="submit" formaction="{%url 'treenode_prohodit' ch.node.id%}">Prohodit ^ a v</button> | ||||
| 			{% endif %} | ||||
| 			</div> | ||||
| 		{% endfor %} | ||||
| 	</div> | ||||
| {% else %} | ||||
| 	{# ----------- Přidáváme prvního syna ----------#} | ||||
| 	{% with kam="jako syna" kam_slug="syn" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} | ||||
| {%endif%} | ||||
| </div> | ||||
|  |  | |||
							
								
								
									
										7
									
								
								seminar/templates/seminar/vuetest.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								seminar/templates/seminar/vuetest.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| {% load render_bundle from webpack_loader %} | ||||
| 
 | ||||
| <div id="app"> | ||||
|   <app></app> | ||||
| </div> | ||||
| {% render_bundle 'chunk-vendors' %} | ||||
| {% render_bundle 'vue_app_01' %} | ||||
|  | @ -1,49 +1,243 @@ | |||
| from django import template | ||||
| from enum import Enum | ||||
| import seminar.models as m | ||||
| 
 | ||||
| register = template.Library() | ||||
| 
 | ||||
| @register.filter | ||||
| def nodeType(value): | ||||
| 	if isinstance(value,RocnikNode): return "Ročník" | ||||
| 	if isinstance(value,CisloNode): return "Číslo" | ||||
| 	if isinstance(value,CastNode): return "Část" | ||||
| 	if isinstance(value,TextNode): return "Text" | ||||
| 	if isinstance(value,TemaVCisleNode): return "Téma v čísle" | ||||
| 	if isinstance(value,KonferaNode): return "Konfera" | ||||
| 	if isinstance(value,ClanekNode): return "Článek" | ||||
| 	if isinstance(value,UlohaVzorakNode): return "Vzorák" | ||||
| 	if isinstance(value,UlohaZadaniNode): return "Zadání úlohy" | ||||
| 	if isinstance(value,PohadkaNode): return "Pohádka" | ||||
| 
 | ||||
| ### NASLEDUJICI FUNKCE SE POUZIVAJI VE views_all.py V SEKCI PRIPRAVJICI TNLData | ||||
| ### NEMAZAT, PRESUNOUT S TNLDaty NEKAM BOKEM | ||||
| 
 | ||||
| @register.filter | ||||
| def isRocnik(value): | ||||
|     return isinstance(value, m.RocnikNode) | ||||
| 	return isinstance(value, m.RocnikNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isCislo(value): | ||||
|     return isinstance(value, m.CisloNode) | ||||
| 	return isinstance(value, m.CisloNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isCast(value): | ||||
|     return isinstance(value, m.CastNode) | ||||
| 	return isinstance(value, m.CastNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isText(value): | ||||
|     return isinstance(value, m.TextNode) | ||||
| 	return isinstance(value, m.TextNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isTemaVCisle(value): | ||||
|     return isinstance(value, m.TemaVCisleNode) | ||||
| 	return isinstance(value, m.TemaVCisleNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isKonfera(value): | ||||
|     return isinstance(value, m.KonferaNode) | ||||
| 	return isinstance(value, m.KonferaNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isClanek(value): | ||||
|     return isinstance(value, m.ClanekNode) | ||||
| 	return isinstance(value, m.ClanekNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isUlohaVzorak(value): | ||||
|     return isinstance(value, m.UlohaVzorakNode) | ||||
| 	return isinstance(value, m.UlohaVzorakNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isUlohaZadani(value): | ||||
|     return isinstance(value, m.UlohaZadaniNode) | ||||
| 	return isinstance(value, m.UlohaZadaniNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isPohadka(value): | ||||
|     return isinstance(value, m.PohadkaNode) | ||||
| 	return isinstance(value, m.PohadkaNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isReseni(value): | ||||
| 	return False | ||||
| #	return isinstance(value, m.OtisteneReseniNode) | ||||
| 
 | ||||
| @register.filter | ||||
| def isOrgText(value): | ||||
| 	return False | ||||
| #	return isinstance(value, m.OrgTextNode) | ||||
| 
 | ||||
| 
 | ||||
| ###  | ||||
| 
 | ||||
| #@register.filter | ||||
| #def isOtisteneReseniNode(value): | ||||
| #    return isinstance(value, m.OtisteneReseniNode) | ||||
| #def podvesitelneNody(value): | ||||
| #	if isText() | ||||
| 
 | ||||
| @register.filter | ||||
| def deletable(value): | ||||
| 	if isTemaVCisle(value): | ||||
| 		return True | ||||
| 	if isOrgText(value): | ||||
| 		return True | ||||
| 	if isReseni(value): | ||||
| 		return True | ||||
| 	if isUlohaZadani(value): | ||||
| 		return True | ||||
| 	if isUlohaVzorak(value): | ||||
| 		return True | ||||
| 	if isCast(value): | ||||
| 		return True | ||||
| 	if isText(value): | ||||
| 		return True | ||||
| 	return False | ||||
| 
 | ||||
| @register.filter | ||||
| def editableSiblings(value): | ||||
| 	if isCast(value.node): | ||||
| 		return True | ||||
| 	if isText(value.node): | ||||
| 		return True | ||||
| 	if isReseni(value.node) and value.tema_in_path: | ||||
| 		return True | ||||
| 	if isUlohaZadani(value.node) and value.tema_in_path: | ||||
| 		return True | ||||
| 	if isUlohaVzorak(value.node) and value.tema_in_path: | ||||
| 		return True | ||||
| 	return False | ||||
| 
 | ||||
| @register.filter | ||||
| def editableChildren(value): | ||||
| 	if isRocnik(value.node): | ||||
| 		return False | ||||
| 	if isCislo(value.node): | ||||
| 		return False | ||||
| 	if isText(value.node): | ||||
| 		return False | ||||
| 	return True | ||||
| 
 | ||||
| @register.filter | ||||
| def textOnlySubtree(value): | ||||
| 	text_only = True | ||||
| 	if isText(value.node): | ||||
| 		return True | ||||
| 	if not isCast(value.node): | ||||
| 		return False | ||||
| 	for ch in value.children: | ||||
| 		if not textOnlySubtree(ch): | ||||
| 			return False | ||||
| 	return True | ||||
| 
 | ||||
| def canPodvesit(obj,new_parent): | ||||
| 	if isCast(new_parent.node): | ||||
| #		print("Lze",obj,new_parent) | ||||
| 		return True | ||||
| 	if textOnlySubtree(obj): | ||||
| #		print("Lze",obj,new_parent) | ||||
| 		return True | ||||
| 	return False | ||||
| 
 | ||||
| @register.filter | ||||
| def canPodvesitZa(value): | ||||
| 	if not value.index or value.index+1 >= len(value.parent.children): | ||||
| 		return False | ||||
| 	new_parent = value.parent.children[value.index+1] | ||||
| 	return canPodvesit(value,new_parent) | ||||
| 
 | ||||
| @register.filter | ||||
| def canPodvesitPred(value): | ||||
| 	if not value.index or value.index <= 0: | ||||
| 		return False | ||||
| 	new_parent = value.parent.children[value.index-1] | ||||
| 	return canPodvesit(value,new_parent) | ||||
| 
 | ||||
| 
 | ||||
| class NodeTypes(Enum): | ||||
| 	ROCNIK = ('rocnikNode','Ročník') | ||||
| 	CISLO = ('cisloNode', 'Číslo') | ||||
| 	MEZICISLO = ('mezicisloNode', 'Mezičíslo') | ||||
| 	CAST = ('castNode', 'Část') | ||||
| 	TEXT = ('textNode', 'Text') | ||||
| 	TEMAVCISLE = ('temaVCisleNode', 'Téma v čísle') | ||||
| 	RESENI = ('reseniNode','Řešení') | ||||
| 	ULOHAZADANI = ('ulohaZadaniNode','Zadání') | ||||
| 	ULOHAVZORAK = ('ulohaVzorakNode','Vzorák') | ||||
| 	POHADKA = ('pohadkaNode','Pohádka') | ||||
| 	ORGTEXT = ('orgText','Orgtext') | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @register.simple_tag | ||||
| def appendableChildren(value): | ||||
| 	print(value) | ||||
| 	print(value.node) | ||||
| 	print(isUlohaZadani(value.node)) | ||||
| 	if isTemaVCisle(value.node): | ||||
| 		return (NodeTypes.RESENI.value[0], | ||||
| 			NodeTypes.ULOHAZADANI.value[0], | ||||
| 			NodeTypes.ULOHAVZORAK.value[0], | ||||
| 			NodeTypes.CAST.value[0], | ||||
| 			NodeTypes.TEXT.value[0], | ||||
| 			) | ||||
| 	if isOrgText(value.node) or isReseni(value.node) or isUlohaZadani(value.node) or isUlohaVzorak(value.node): | ||||
| 		print("Text/Cast") | ||||
| 		return (NodeTypes.CAST.value[0], | ||||
| 			NodeTypes.TEXT.value[0], | ||||
| 			) | ||||
| 	if isCast(value.node): | ||||
| 		return appendableChildren(value.parent) | ||||
| 	return [] | ||||
| 
 | ||||
| @register.simple_tag | ||||
| def canAppendReseni(value): | ||||
| 	if isTemaVCisle(value.node): | ||||
| 		return True | ||||
| 	if isCast(value.node): | ||||
| 		return canAppendReseni(value.parent) | ||||
| 	return False | ||||
| 
 | ||||
| @register.simple_tag | ||||
| def canAppendUlohaZadani(value): | ||||
| 	if isTemaVCisle(value.node): | ||||
| 		return True | ||||
| 	if isCast(value.node): | ||||
| 		return canAppendUlohaZadani(value.parent) | ||||
| 	return False | ||||
| 
 | ||||
| @register.simple_tag | ||||
| def canAppendUlohaVzorak(value): | ||||
| 	if isTemaVCisle(value.node): | ||||
| 		return True | ||||
| 	if isCast(value.node): | ||||
| 		return canAppendUlohaVzorak(value.parent) | ||||
| 	return False | ||||
| 
 | ||||
| @register.simple_tag | ||||
| def canAppendCast(value): | ||||
| 	if isTemaVCisle(value.node) or isOrgText(value.node) or isReseni(value.node) or isUlohaZadani(value.node) or isUlohaVzorak(value.node): | ||||
| 		return True | ||||
| 	if isCast(value.node): | ||||
| 		return canAppendCast(value.parent) | ||||
| 	return False | ||||
| 
 | ||||
| @register.simple_tag | ||||
| def canAppendText(value): | ||||
| 	if isTemaVCisle(value.node) or isOrgText(value.node) or isReseni(value.node) or isUlohaZadani(value.node) or isUlohaVzorak(value.node): | ||||
| 		return True | ||||
| 	if isCast(value.node): | ||||
| 		return canAppendText(value.parent) | ||||
| 	return False | ||||
| 
 | ||||
| #@register.filter | ||||
| #def is(value): | ||||
| #	return | ||||
| # | ||||
| #			NodeTypes..value, | ||||
| #@register.filter | ||||
| #def is(value): | ||||
| #	return | ||||
| 
 | ||||
|  |  | |||
|  | @ -244,8 +244,8 @@ def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi | |||
| 		na_web = text, | ||||
| 		do_cisla = text, | ||||
| 	) | ||||
| 	zad = TextNode.objects.create(text = text_zadani) | ||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad) | ||||
| 	zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) | ||||
| 	p.ulohazadaninode = uloha_zadani | ||||
| 	otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani) | ||||
| 
 | ||||
|  | @ -266,8 +266,8 @@ def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu): | |||
| 		na_web = obsah, | ||||
| 		do_cisla = obsah | ||||
| 	) | ||||
| 	vzorak = TextNode.objects.create(text = text_vzoraku) | ||||
| 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak) | ||||
| 	vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha.ulohavzoraknode = uloha_vzorak | ||||
| 
 | ||||
| 	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) | ||||
|  | @ -434,7 +434,7 @@ def gen_cisla(rnd, rocniky): | |||
| 				datum_deadline=deadline, | ||||
| 				verejne_db=True | ||||
| 			) | ||||
| 			node2 = CisloNode.objects.create(cislo = cislo, succ = node) | ||||
| 			node2 = CisloNode.objects.create(cislo = cislo, succ = node, root=rocnik.rocniknode) | ||||
| 			cislo.save() | ||||
| 			node = node2 | ||||
| 			if otec: | ||||
|  | @ -473,54 +473,54 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | |||
| 	for cislo in cisla: | ||||
| 		# Přidáme TemaVCisleNode do daného čísla | ||||
| 		cislo_node = cislo.cislonode	 | ||||
| 		tema_cislo_node = TemaVCisleNode.objects.create(tema = tema) | ||||
| 		tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) | ||||
| 		insert_last_child(cislo_node, tema_cislo_node) | ||||
| 		 | ||||
| 		# Přidávání obsahu do čísla | ||||
| 		cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod)) | ||||
| 		cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) | ||||
| 		add_first_child(tema_cislo_node, cast_node) | ||||
| 	 | ||||
| 		text_node = TextNode.objects.create(text = get_text()) | ||||
| 		text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node, text_node) | ||||
| 
 | ||||
| 		cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém") | ||||
| 		cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) | ||||
| 		add_first_child(text_node, cast_node2) | ||||
| 		 | ||||
| 		text_node2 = TextNode.objects.create(text = get_text()) | ||||
| 		text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node2, text_node2) | ||||
| 		 | ||||
| 		cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém") | ||||
| 		cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) | ||||
| 		add_first_child(text_node2, cast_node3) | ||||
| 
 | ||||
| 		text_node3 = TextNode.objects.create(text = get_text()) | ||||
| 		text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node3, text_node3) | ||||
| 
 | ||||
| 		cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém") | ||||
| 		cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) | ||||
| 		add_first_child(text_node3, cast_node4)	 | ||||
| 
 | ||||
| 		text_node4 = TextNode.objects.create(text = get_text()) | ||||
| 		text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node3, text_node4) | ||||
| 		 | ||||
| 		cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " | ||||
| 					"druhým podproblémem") | ||||
| 					"druhým podproblémem", root=cislo_node.root) | ||||
| 		cast_node3.succ = cast_node3a | ||||
| 		cast_node3.save() | ||||
| 
 | ||||
| 		text_node3a = TextNode.objects.create(text = get_text()) | ||||
| 		text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node3a, text_node3a) | ||||
| 
 | ||||
| 		# Občas přidáme mezičíslo | ||||
| 		if rnd.randint(1, 3) == 1: | ||||
| 			create_node_after(cislo_node, m.MezicisloNode)		 | ||||
| 			create_node_after(cislo_node, m.MezicisloNode, root=cislo_node.root) | ||||
| 			mezicislo_node = cislo_node.succ | ||||
| 	 | ||||
| 			cast_node_mezicislo = m.CastNode.objects.create( | ||||
| 					nadpis = "Příspěvek k mezičíslu".format(cislo.kod)) | ||||
| 					nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root) | ||||
| 			add_first_child(mezicislo_node, cast_node_mezicislo) | ||||
| 
 | ||||
| 			odstavec = lorem.paragraph() | ||||
| 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec)	 | ||||
| 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo) | ||||
| 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) | ||||
| 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) | ||||
| 			add_first_child(cast_node_mezicislo, text_node_mezicislo) | ||||
| 
 | ||||
| 	return tema | ||||
|  | @ -564,7 +564,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | |||
| 
 | ||||
| 			# Vyrobíme TemaVCisleNody pro obsah | ||||
| 			for i in range(zacatek_tematu, konec_tematu+1): | ||||
| 				node = TemaVCisleNode.objects.create(tema = t) | ||||
| 				node = TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) | ||||
| 				# FIXME: Není to off-by-one? | ||||
| 				otec = cisla[i-1].cislonode | ||||
| 				otec_syn(otec, node) | ||||
|  | @ -621,8 +621,8 @@ def gen_ulohy_tematu(rnd, organizatori, tema, kod, cislo, cislo_se_vzorakem): | |||
| 		na_web = obsah, | ||||
| 		do_cisla = obsah, | ||||
| 		) | ||||
| 	zad = TextNode.objects.create(text = text_zadani) | ||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad) | ||||
| 	zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) | ||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) | ||||
| 	uloha.ulohazadaninode = uloha_zadani | ||||
| 
 | ||||
| 	return uloha, uloha_zadani | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.db import transaction | ||||
| # NOTE: node.prev a node.succ jsou implementovány přímo v models.TreeNode | ||||
| # TODO: Všechny tyto funkce se naivně spoléhají na to, že jako parametr dostanou nějaký TreeNode (některé možná zvládnou i None) | ||||
| # TODO: Chceme, aby všechno nějak zvládlo None jako parametr. | ||||
| # TODO: Do nějakých consistency-checků přidat hledání polo-sirotků (kteří nesplňují invarianty, třeba nejsou dosažitelní a mají root, vyrábějí DAG, ...) | ||||
| 
 | ||||
| # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. | ||||
| def print_tree(node,indent=0): | ||||
|  | @ -12,20 +14,31 @@ def print_tree(node,indent=0): | |||
| 	if node.succ: | ||||
| 		print_tree(node.succ, indent=indent) | ||||
| 
 | ||||
| def is_orphan(node): | ||||
| 	""" Zjišťuje, jestli už je daný Node někde pověšený či nikoli. """ | ||||
| 	if safe_father_of_first(node) is None and safe_pred(node) is None: | ||||
| 		return True | ||||
| 	else: | ||||
| 		return False | ||||
| 
 | ||||
| # Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist | ||||
| def safe_pred(node): | ||||
| 	if node is None: | ||||
| 		return None | ||||
| 	try: | ||||
| 		return node.prev | ||||
| 	except ObjectDoesNotExist: | ||||
| 		return None | ||||
| 
 | ||||
| # FIXME: Proč????? | ||||
| def safe_succ(node): | ||||
| 	try: | ||||
| 		return node.succ | ||||
| 	except ObjectDoesNotExist: | ||||
| 		return None | ||||
| 
 | ||||
| def safe_father_of_first(node): | ||||
| 	if node is None: | ||||
| 		return None | ||||
| 	first = first_brother(node) | ||||
| 	try: | ||||
| 		return first.father_of_first | ||||
| 	except ObjectDoesNotExist: | ||||
| 		return None | ||||
| 
 | ||||
| def first_brother(node): | ||||
| 	if node is None: | ||||
| 		return None | ||||
|  | @ -34,15 +47,8 @@ def first_brother(node): | |||
| 		brother = safe_pred(brother) | ||||
| 	return brother | ||||
| 
 | ||||
| # A to samé pro .father_of_first | ||||
| def safe_father_of_first(node): | ||||
| 	first = first_brother(node) | ||||
| 	try: | ||||
| 		return first.father_of_first | ||||
| 	except ObjectDoesNotExist: | ||||
| 		return None | ||||
| 
 | ||||
| ## Rodinné vztahy | ||||
| # Tohle se teď zrovna k None chová správně, ale je potřeba na to dávat pozor | ||||
| def get_parent(node): | ||||
| 	# Nejdřív získáme prvního potomka... | ||||
| 	while safe_pred(node) is not None: | ||||
|  | @ -51,14 +57,33 @@ def get_parent(node): | |||
| 	return safe_father_of_first(node) | ||||
| 
 | ||||
| def get_last_child(node): | ||||
| 	if node is None: | ||||
| 		return None | ||||
| 	first = node.first_child | ||||
| 	if first is None: | ||||
| 		return None | ||||
| 	else: | ||||
| 		return last_brother(first) | ||||
| 
 | ||||
| def is_orphan(node): | ||||
| 	""" Zjišťuje, jestli už je daný Node někde pověšený či nikoli. """ | ||||
| 	# None jsem se rozhodl, že sirotek není | ||||
| 	if node is None: | ||||
| 		return False | ||||
| 	if get_parent(node) is None: | ||||
| 		if node.succ is not None or safe_pred(node) is not None or safe_father_of_first(node) is not None or node.root is not None: | ||||
| 			import logging | ||||
| 			logger = logging.getLogger(__name__) | ||||
| 			# Error = pošle mail :-) | ||||
| 			logger.error(f"Node-sirotek s id {node.id} má rodinné vztahy (Node: {node})") | ||||
| 		return True | ||||
| 	else: | ||||
| 		return False | ||||
| 
 | ||||
| # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) | ||||
| def general_next(node): | ||||
| 	if node is None: | ||||
| 		return None | ||||
| 	# Máme potomka? | ||||
| 	if node.first_child is not None: | ||||
| 		return node.first_child | ||||
|  | @ -71,6 +96,8 @@ def general_next(node): | |||
| 	return node.succ | ||||
| 
 | ||||
| def last_brother(node): | ||||
| 	if node is None: | ||||
| 		return None | ||||
| 	while node.succ is not None: | ||||
| 		node = node.succ | ||||
| 	return node | ||||
|  | @ -78,6 +105,7 @@ def last_brother(node): | |||
| def general_prev(node): | ||||
| 	# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. | ||||
| 	# Otce vyřešíme nejdřív: | ||||
| 	# Tady se ošetří node=None samo | ||||
| 	if safe_pred(node) is None: | ||||
| 		return safe_father_of_first(node) | ||||
| 	pred = safe_pred(node) | ||||
|  | @ -95,12 +123,16 @@ def me_and_right_brothers(node): | |||
| 		current = current.succ | ||||
| 
 | ||||
| def right_brothers(node): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	generator = me_and_right_brothers(node.succ) | ||||
| 	for item in generator: | ||||
| 		yield item | ||||
| 
 | ||||
| # Generátor všech sourozenců (vč. sám sebe) | ||||
| def all_brothers(node): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	# Najdeme prvního bratra | ||||
| 	fb = first_brother(node) | ||||
| 	marb = me_and_right_brothers(fb) | ||||
|  | @ -108,6 +140,8 @@ def all_brothers(node): | |||
| 		yield cur | ||||
| 	 | ||||
| def all_proper_brothers(node): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	all = all_brothers(node) | ||||
| 	for br in all: | ||||
| 		if br is node: | ||||
|  | @ -116,12 +150,16 @@ def all_proper_brothers(node): | |||
| 
 | ||||
| def all_children(node): | ||||
| 	""" Generátor všech potomků zadaného Node. """ | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	brothers = all_brothers(node.first_child) | ||||
| 	for br in brothers: | ||||
| 		yield br | ||||
| 
 | ||||
| def all_children_of_type(node, type): | ||||
| 	""" Generuje všechny potomky daného Node a daného typu. """ | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	brothers = all_brothers(node.first_child) | ||||
| 	for br in brothers: | ||||
| 		if isinstance(br, type): | ||||
|  | @ -130,6 +168,8 @@ def all_children_of_type(node, type): | |||
| # Generátor následníků v "the-right-order" | ||||
| # Bez tohoto vrcholu | ||||
| def all_following(node): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	current = general_next(node) | ||||
| 	while current is not None: | ||||
| 		yield current | ||||
|  | @ -139,12 +179,16 @@ def all_following(node): | |||
| # Najdi dalšího bratra nějakého typu, nebo None. | ||||
| # hledá i podtřídy, i.e. get_next_brother_of_type(neco, TreeNode) je prostě succ. | ||||
| def get_next_brother_of_type(node, type): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	for current in right_brothers(node): | ||||
| 		if isinstance(current, type): | ||||
| 			return current | ||||
| 	return None | ||||
| 	 | ||||
| def get_prev_brother_of_type(node, type): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||
| 	current = node | ||||
| 	while safe_pred(current) is not None: | ||||
|  | @ -155,6 +199,8 @@ def get_prev_brother_of_type(node, type): | |||
| 
 | ||||
| # Totéž pro "the-right-order" pořadí | ||||
| def get_next_node_of_type(node, type): | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	for cur in all_folowing(node): | ||||
| 		if isinstance(cur, type): | ||||
| 			return cur | ||||
|  | @ -162,6 +208,8 @@ def get_next_node_of_type(node, type): | |||
| 
 | ||||
| def get_prev_node_of_type(node, type): | ||||
| 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	current = node | ||||
| 	while general_prev(current) is not None: | ||||
| 		current = general_prev(current) | ||||
|  | @ -171,20 +219,38 @@ def get_prev_node_of_type(node, type): | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # Exception, kterou některé metody při špatném použití mohou házet | ||||
| # Hlavní důvod je možnost informovat o selhání, aby se příslušný problém dal zobrazit na frontendu, | ||||
| class TreeLibError(RuntimeError): | ||||
| 	pass | ||||
| 
 | ||||
| 
 | ||||
| # Editace stromu: | ||||
| def create_node_after(predecessor, type, **kwargs): | ||||
| 	from seminar.models import TreeNode | ||||
| 	if predecessor is None: | ||||
| 		raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") | ||||
| 	if not issubclass(type, TreeNode): | ||||
| 		raise TreeLibError("Nový node není node!") | ||||
| 	new_node = type.objects.create(**kwargs) | ||||
| 	new_node.root = predecessor.root | ||||
| 	new_node.save() | ||||
| 	succ = predecessor.succ | ||||
| 	predecessor.succ = new_node | ||||
| 	predecessor.save() | ||||
| 	new_node.succ = succ | ||||
| 	new_node.save() | ||||
| 	return new_node | ||||
| 
 | ||||
| # Vyrábí prvního syna, ostatní nalepí za (existují-li) | ||||
| def create_child(parent, type, **kwargs): | ||||
| 	from seminar.models import TreeNode | ||||
| 	if parent is None: | ||||
| 		raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") | ||||
| 	if not issubclass(type, TreeNode): | ||||
| 		raise TreeLibError("Nový node není node!") | ||||
| 	new_node = type.objects.create(**kwargs) | ||||
| 	new_node.root = parent.root | ||||
| 	new_node.save() | ||||
| 	orig_child = parent.first_child | ||||
| 	parent.first_child = new_node | ||||
|  | @ -193,8 +259,11 @@ def create_child(parent, type, **kwargs): | |||
| 		# Přidáme původního prvního syna jako potomka nového vrcholu | ||||
| 		new_node.succ = orig_child | ||||
| 		new_node.save() | ||||
| 	return new_node | ||||
| 
 | ||||
| def insert_last_child(parent, node): | ||||
| 	if parent is None: | ||||
| 		raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") | ||||
| 	""" Zadaný Node přidá jako posledního potomka otce. """ | ||||
| 	last = get_last_child(parent) | ||||
| 	if not is_orphan(node): | ||||
|  | @ -213,6 +282,11 @@ def insert_last_child(parent, node): | |||
| 		last.save() | ||||
| 
 | ||||
| def create_node_before(successor, type, **kwargs): | ||||
| 	from seminar.models import TreeNode | ||||
| 	if successor is None: | ||||
| 		raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") | ||||
| 	if not issubclass(type, TreeNode): | ||||
| 		raise TreeLibError("Nový node není node!") | ||||
| 	if safe_pred(successor) is not None: | ||||
| 		# Easy: přidáme za předchůdce | ||||
| 		create_node_after(successor.prev, type, **kwargs) | ||||
|  | @ -223,36 +297,18 @@ def create_node_before(successor, type, **kwargs): | |||
| 		create_child(successor.father_of_first, type, **kwargs) | ||||
| 	# Teď už easy: Jsme sirotci, takže se vyrobíme a našeho následníka si přidáme jako succ | ||||
| 	new = type.objects.create(**kwargs) | ||||
| 	new.root = successor.root | ||||
| 	new.succ = successor | ||||
| 	new.save() | ||||
| 	return new | ||||
| 
 | ||||
| 
 | ||||
| # ValueError, pokud je (aspoň) jeden parametr None | ||||
| def swap(node, other): | ||||
| 	raise NotImplementedError("YAGNI (You aren't gonna need it).") | ||||
| 
 | ||||
| # Exception, kterou některé metody při špatném použití mohou házet | ||||
| # Hlavní důvod je možnost informovat o selhání, aby se příslušný problém dal zobrazit na frontendu, | ||||
| class TreeLibError(RuntimeError): | ||||
| 	pass | ||||
| 
 | ||||
| def swap_pred(node): | ||||
| 	if node is None: | ||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||
| 	pred = safe_pred(node) | ||||
| 	if pred is None: | ||||
| 		raise TreeLibError("Nelze posunout vlevo, není tam žádný další uzel.") | ||||
| 	pre_pred = safe_pred(pred) | ||||
| 	succ = node.succ | ||||
| 
 | ||||
| 	if pre_pred is not None: | ||||
| 		pre_pred.succ = node | ||||
| 		pre_pred.save() | ||||
| 	node.succ = pred | ||||
| 	node.save() | ||||
| 	pred.succ = succ | ||||
| 	pred.save() | ||||
| 
 | ||||
| @transaction.atomic | ||||
| def swap_succ(node): | ||||
| 	if node is None: | ||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||
|  | @ -263,63 +319,147 @@ def swap_succ(node): | |||
| 	post_succ = succ.succ | ||||
| 
 | ||||
| 	if pred is not None: | ||||
| 		pred.succ = succ | ||||
| 		pred.succ = None | ||||
| 		pred.save() | ||||
| 
 | ||||
| 	# Nemame predchudce -> je potreba upravit otce | ||||
| 	father = safe_father_of_first(node) | ||||
| 	if pred is None and father is not None: # Mame otce | ||||
| 		father.first_child = succ | ||||
| 		father.save() | ||||
| 
 | ||||
| 
 | ||||
| 	succ.succ = node | ||||
| 	succ.save() | ||||
| 	node.succ = post_succ | ||||
| 	node.save() | ||||
| 	if pred is not None: | ||||
| 		pred.succ = succ | ||||
| 		pred.save() | ||||
| 
 | ||||
| @transaction.atomic | ||||
| def swap_pred(node): | ||||
| 	if node is None: | ||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||
| 	pred = safe_pred(node) | ||||
| 	if pred is None: | ||||
| 		raise TreeLibError("Nelze posunout vlevo, není tam žádný další uzel.") | ||||
| 	return swap_succ(pred) | ||||
| 
 | ||||
| # Rotace stromu | ||||
| # Dokumentace viz wiki: | ||||
| # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) | ||||
| def raise_node(node): | ||||
| 	if node is None: | ||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||
| 	# Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1) | ||||
| 	# FIXME: Velmi naivní, chybí error checky | ||||
| 	# FIXME: Trochu méně naivní, nevěřím tomu, prosím otestovat | ||||
| 	D = node | ||||
| 	C = get_parent(D) | ||||
| 	E = C.succ | ||||
| 	subtree4_head = D.first_child | ||||
| 	subtree4_tail = last_brother(subtree4_head) | ||||
| 	subtree3P_head = D.succ | ||||
| 	subtree3L_head = C.first_child | ||||
| 	subtree3L_tail = safe_pred(D) | ||||
| 	if C is None: | ||||
| 		raise TreeLibError("Nelze povýšit vrchol, jenž nemá otce.") | ||||
| 	E = C.succ	# Může být None a ničemu to nevadí | ||||
| 	subtree4_head = D.first_child	# Může být None, ale pak se musí z 3P udělat přímo potomek D | ||||
| 	subtree4_tail = last_brother(subtree4_head)	# Měl by být None právě když je sub4_head=None | ||||
| 	subtree3P_head = D.succ	# Může být None a ničemu to nevadí | ||||
| 	subtree3L_tail = safe_pred(D)	# Pokud je None, D je první syn C a C má tedy skončit bezdětný | ||||
| 	 | ||||
| 	# Prostor pro motlitbu... | ||||
| 	pass | ||||
| 	 | ||||
| 	# Amen. | ||||
| 	C.succ = D | ||||
| 	# Teď už nesmíme spadnout, protože jinak skončíme se stromem v nekonzistentním stavu | ||||
| 	C.succ = D	# Nespadne | ||||
| 	C.save() | ||||
| 	D.succ = E | ||||
| 	D.succ = E	# Nespadne | ||||
| 	D.save() | ||||
| 	subtree3L_tail.succ = None | ||||
| 	subtree3L_tail.save() | ||||
| 	subtree4_tail.succ = subtree3P.head | ||||
| 	subtree4_tail.save() | ||||
| 
 | ||||
| 	if subtree3L_tail is not None: | ||||
| 		subtree3L_tail.succ = None | ||||
| 		subtree3L_tail.save() | ||||
| 	else: | ||||
| 		assert C.first_child is D | ||||
| 		C.first_child = None | ||||
| 		C.save() | ||||
| 
 | ||||
| 	if subtree4_tail is not None: | ||||
| 		subtree4_tail.succ = subtree3P_head | ||||
| 		subtree4_tail.save() | ||||
| 	else: | ||||
| 		D.first_child = subtree3P_head | ||||
| 		D.save() | ||||
| 
 | ||||
| 	# To by mělo být všechno... | ||||
| 
 | ||||
| # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) | ||||
| def lower_node(node): | ||||
| 	if node is None: | ||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||
| 	# Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1) | ||||
| 	# FIXME: Velmi naivní, chybí error checky | ||||
| 	# FIXME: Trochu naivní, prosím otestovat | ||||
| 	C = node | ||||
| 	D = C.succ | ||||
| 	D = C.succ	# Může být None a ničemu to nevadí | ||||
| 	B = safe_pred(C) | ||||
| 	subtree2_head = B.first_child | ||||
| 	subtree2_tail = last_brother(subtree2_head) | ||||
| 	if B is None: | ||||
| 		raise TreeLibError("Nelze ponížit prvního syna (není pod co)") | ||||
| 	subtree2_head = B.first_child	# Je-li None, pak se z C má stát první syn | ||||
| 	subtree2_tail = last_brother(subtree2_head)	# None iff head=None, doufám | ||||
| 	 | ||||
| 	# Prostor pro motlitbu... | ||||
| 	pass | ||||
| 	 | ||||
| 	# Amen. | ||||
| 	B.succ = D | ||||
| 	# Teď už nesmíme spadnout, protože jinak skončíme se stromem v nekonzistentním stavu | ||||
| 	B.succ = D	# Nespadne | ||||
| 	B.save() | ||||
| 	subtree2_tail.succ = C | ||||
| 	subtree2_tail.save() | ||||
| 	if subtree2_tail is not None: | ||||
| 		subtree2_tail.succ = C | ||||
| 		subtree2_tail.save() | ||||
| 	else: | ||||
| 		assert subtree2_head is None | ||||
| 		B.first_child = C | ||||
| 		B.save() | ||||
| 
 | ||||
| 	# To by mělo být všechno... | ||||
| 
 | ||||
| def disconnect_node(node): | ||||
| 	#FIXME: dodělat odstranění roota všem potomkům | ||||
| 	if node is None: | ||||
| 		raise TreeLibError("Nelze odpojit None. Tohle by se nemělo stát.") | ||||
| 
 | ||||
| 	print(f'My:{node}, predchudce:{safe_pred(node)}, naslednik:{safe_succ(node)}, otec:{safe_father_of_first(node)}') | ||||
| 	 | ||||
| 	# Jsme prvnim synem | ||||
| 	if safe_pred(node) is None: | ||||
| 		if safe_succ(node) is None: # Jsme jedinym synem - upravime otce (pokud mame) a odpojime se | ||||
| 			father = safe_father_of_first(node) | ||||
| 			if father is not None: | ||||
| 				father.first_child = None | ||||
| 				father.save() | ||||
| 			return  | ||||
| 
 | ||||
| 		else: # mame bratra | ||||
| 			swap_succ(node) # Staneme se neprvním synem, pokracujeme mimo if | ||||
| 
 | ||||
| 	# Jsme neprvním synem | ||||
| 	prev = node.prev | ||||
| 	prev.succ = node.succ | ||||
| 	node.succ = None | ||||
| 	node.save() | ||||
| 	clear_root(node) | ||||
| 	prev.save() | ||||
| 
 | ||||
| def clear_root(node): | ||||
| 	node.root = None | ||||
| 	node.save() | ||||
| 	if node.first_child: | ||||
| 		clear_root(node.first_child) | ||||
| 	if node.succ: | ||||
| 		clear_root(node.succ) | ||||
| 
 | ||||
| def set_root(node,root): | ||||
| 	node.root = root | ||||
| 	node.save() | ||||
| 	if node.first_child: | ||||
| 		clear_root(node.first_child) | ||||
| 	if node.succ: | ||||
| 		clear_root(node.succ) | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| from django.urls import path, include | ||||
| from django.urls import path, include, re_path | ||||
| from django.contrib.auth.decorators import login_required | ||||
| from . import views, export | ||||
| from .utils import org_required, resitel_required | ||||
|  | @ -17,9 +17,17 @@ urlpatterns = [ | |||
| 	path('archiv/temata/', views.ArchivTemataView.as_view()), | ||||
| 
 | ||||
| 	path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'), | ||||
| 	path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu | ||||
| 	path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), | ||||
| 	path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'), | ||||
| 	path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'), | ||||
| 	path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), | ||||
| 	path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='seminar_textnode_web'), | ||||
| 	path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'), | ||||
| 	path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), | ||||
| 	path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), | ||||
| 	path('treenode/editor/podvesit/<int:pk>/<str:kam>/', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), | ||||
| 	path('treenode/editor/prohodit/<int:pk>/', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), | ||||
| 	path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), | ||||
| 	#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), | ||||
| 
 | ||||
| 	# Soustredeni | ||||
|  | @ -50,7 +58,7 @@ urlpatterns = [ | |||
| 
 | ||||
| 	# Zadani | ||||
| 	path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), | ||||
| #	path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), | ||||
| 	path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), | ||||
| 	#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), | ||||
| 	path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), | ||||
| 
 | ||||
|  | @ -149,7 +157,11 @@ urlpatterns = [ | |||
| 	path('temp/add_solution', org_required(views.AddSolutionView.as_view()), name='seminar_vloz_reseni'), | ||||
| 	path('temp/nahraj_reseni', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'), | ||||
| 
 | ||||
| 	re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'), | ||||
| 	path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()), | ||||
| 
 | ||||
| 	path('', views.TitulniStranaView.as_view(), name='titulni_strana'), | ||||
| 	path('jak-resit/', views.JakResitView.as_view(), name='jak-resit'), | ||||
| 
 | ||||
| 	# Ceka na autocomplete v3 | ||||
| 	# path('autocomplete/organizatori/', | ||||
|  |  | |||
|  | @ -1,2 +1,3 @@ | |||
| from .views_all import * | ||||
| from .autocomplete import * | ||||
| from .views_rest import * | ||||
|  |  | |||
|  | @ -16,15 +16,20 @@ from django.contrib.auth import views as auth_views | |||
| from django.contrib.auth.models import User, Permission | ||||
| from django.contrib.auth.mixins import LoginRequiredMixin | ||||
| from django.db import transaction | ||||
| from django.core import serializers | ||||
| from django.forms.models import model_to_dict | ||||
| 
 | ||||
| import seminar.models as s | ||||
| import seminar.models as m | ||||
| from seminar.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 seminar import utils, treelib | ||||
| from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | ||||
| import seminar.forms as f | ||||
| import seminar.templatetags.treenodes as tnltt | ||||
| import seminar.views.views_rest as vr | ||||
| 
 | ||||
| from datetime import timedelta, date, datetime | ||||
| from datetime import timedelta, date, datetime, MAXYEAR | ||||
| from django.utils import timezone | ||||
| from itertools import groupby | ||||
| from collections import OrderedDict | ||||
|  | @ -44,14 +49,15 @@ import time | |||
| 
 | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | ||||
| 
 | ||||
| 
 | ||||
| def verejna_temata(rocnik): | ||||
| 	"""Vrací queryset zveřejněných témat v daném ročníku. | ||||
| 	""" | ||||
| 	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) | ||||
| # ze starého modelu | ||||
| #def verejna_temata(rocnik): | ||||
| #	""" | ||||
| #	Vrací queryset zveřejněných témat v daném ročníku. | ||||
| #	""" | ||||
| #	return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod') | ||||
| # | ||||
| #def temata_v_rocniku(rocnik): | ||||
| #	return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) | ||||
| 
 | ||||
| def get_problemy_k_tematu(tema): | ||||
| 	return Problem.objects.filter(nadproblem = tema) | ||||
|  | @ -88,17 +94,99 @@ class ObalkovaniView(generic.ListView): | |||
| 		return context | ||||
| 
 | ||||
| class TNLData(object): | ||||
| 	def __init__(self,anode): | ||||
| 	def __init__(self,anode,parent=None, index=None): | ||||
| 		self.node = anode | ||||
| 		self.sernode = vr.TreeNodeSerializer(anode) | ||||
| 		self.children = [] | ||||
| 		self.parent = parent | ||||
| 		self.tema_in_path = False | ||||
| 		self.index = index | ||||
| 
 | ||||
| 		if parent: | ||||
| 			self.tema_in_path = parent.tema_in_path | ||||
| 		if isinstance(anode, m.TemaVCisleNode): | ||||
| 			self.tema_in_path = True | ||||
| 
 | ||||
| 	def add_edit_options(self): | ||||
| 		self.deletable = tnltt.deletable(self)  | ||||
| 		self.editable_siblings = tnltt.editableSiblings(self) | ||||
| 		self.editable_children = tnltt.editableChildren(self) | ||||
| 		self.text_only_subtree = tnltt.textOnlySubtree(self) | ||||
| 		self.can_podvesit_za = tnltt.canPodvesitZa(self) | ||||
| 		self.can_podvesit_pred = tnltt.canPodvesitPred(self) | ||||
| 		self.appendable_children = tnltt.appendableChildren(self) | ||||
| 		print("appChld",self.appendable_children) | ||||
| 		if self.parent: | ||||
| 			self.appendable_siblings = tnltt.appendableChildren(self.parent) | ||||
| 		else: | ||||
| 			self.appendable_siblings = [] | ||||
| 
 | ||||
| 
 | ||||
| def treenode_strom_na_seznamy(node): | ||||
| 	out = TNLData(node) | ||||
| 	for ch in treelib.all_children(node): | ||||
| 		outitem = treenode_strom_na_seznamy(ch) | ||||
| 		out.children.append(outitem) | ||||
| 	return out | ||||
| 
 | ||||
| 	@classmethod | ||||
| 	def from_treenode(cls,anode,parent=None,index=None): | ||||
| 		out = cls(anode,parent,index) | ||||
| 		for (idx,ch) in enumerate(treelib.all_children(anode)): | ||||
| 			outitem = cls.from_treenode(ch,out,idx) | ||||
| 			out.children.append(outitem) | ||||
| 		out.add_edit_options() | ||||
| 		return out | ||||
| 	 | ||||
| 	@classmethod | ||||
| 	def from_tnldata_list(cls, tnllist): | ||||
| 		"""Vyrobíme virtuální TNL, který nemá obsah, ale má za potomky všechna zadaná TNLData""" | ||||
| 		result = cls(None) | ||||
| 		for idx, tnl in enumerate(tnllist): | ||||
| 			result.children.append(tnl) | ||||
| 			tnl.parent = result | ||||
| 			tnl.index = idx | ||||
| 		result.add_edit_options() | ||||
| 		return result | ||||
| 
 | ||||
| 	@classmethod | ||||
| 	def filter_treenode(cls, treenode, predicate): | ||||
| 		tnll = cls._filter_treenode_recursive(treenode, predicate) # TreeNodeList List :-) | ||||
| 		return TNLData.from_tnldata_list(tnll) | ||||
| 
 | ||||
| 	@classmethod | ||||
| 	def _filter_treenode_recursive(cls, treenode, predicate): | ||||
| 		if predicate(treenode): | ||||
| 			return [cls.from_treenode(treenode)] | ||||
| 		else: | ||||
| 			found = [] | ||||
| 			for tn in all_children(treenode): | ||||
| 				result = cls.filter_treenode(tn, predicate) | ||||
| 				# Result by v tuhle chvíli měl být seznam TNLDat odpovídající treenodům, jež matchnuly predikát. | ||||
| 				for tnl in result: | ||||
| 					found.append(tnl) | ||||
| 			return found | ||||
| 	 | ||||
| 	def to_json(self): | ||||
| 		#self.node = anode | ||||
| 		#self.children = [] | ||||
| 		#self.parent = parent | ||||
| 		#self.tema_in_path = False | ||||
| 		#self.index = index | ||||
| 		out = {} | ||||
| 		out['node'] = self.sernode.data | ||||
| 		out['children'] = [n.to_json() for n in self.children] | ||||
| 		out['tema_in_path'] = self.tema_in_path | ||||
| 		out['index'] = self.index | ||||
| 		out['deletable'] = self.deletable | ||||
| 		out['editable_siblings'] = self.editable_siblings | ||||
| 		out['editable_children'] = self.editable_children | ||||
| 		out['text_only_subtree'] = self.text_only_subtree | ||||
| 		out['can_podvesit_za'] = self.can_podvesit_za | ||||
| 		out['can_podvesit_pod'] = self.can_podvesit_pred | ||||
| 		out['appendable_children'] = self.appendable_children | ||||
| 		out['appendable_siblings'] = self.appendable_siblings | ||||
| 
 | ||||
| 		return out | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 	def __repr__(self): | ||||
| 		return("TNL({})".format(self.node)) | ||||
| 
 | ||||
| class TreeNodeView(generic.DetailView): | ||||
| 	model = s.TreeNode | ||||
|  | @ -106,22 +194,180 @@ class TreeNodeView(generic.DetailView): | |||
| 
 | ||||
| 	def get_context_data(self,**kwargs): | ||||
| 		context = super().get_context_data(**kwargs) | ||||
| 		context['tnldata'] = treenode_strom_na_seznamy(self.object) | ||||
| 		context['tnldata'] = TNLData.from_treenode(self.object) | ||||
| 		return context | ||||
| 	 | ||||
| # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | ||||
| class AktualniZadaniView(TreeNodeView): | ||||
| 	def get_object(self): | ||||
| 		nastaveni = get_object_or_404(Nastaveni) | ||||
| 		return nastaveni.aktualni_cislo.cislonode | ||||
| 
 | ||||
| 	def get_context_data(self,**kwargs): | ||||
| 		nastaveni = get_object_or_404(Nastaveni) | ||||
| class TreeNodeJSONView(generic.DetailView): | ||||
| 	model = s.TreeNode | ||||
| 
 | ||||
| 	def get(self,request,*args, **kwargs): | ||||
| 		self.object = self.get_object() | ||||
| 		data = TNLData.from_treenode(self.object).to_json() | ||||
| 		return JsonResponse(data) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class TreeNodePridatView(generic.View): | ||||
| 	type_from_str = { | ||||
| 		'rocnikNode': m.RocnikNode, | ||||
| 		'cisloNode': m.CisloNode, | ||||
| 		'castNode': m.CastNode, | ||||
| 		'textNode': m.TextNode, | ||||
| 		'temaVCisleNode': m.TemaVCisleNode, | ||||
| 		'reseniNode': m.ReseniNode, | ||||
| 		'ulohaZadaniNode': m.UlohaZadaniNode, | ||||
| 		'ulohaVzorakNode': m.UlohaVzorakNode, | ||||
| 		'pohadkaNode': m.PohadkaNode, | ||||
| 		'orgText': m.OrgTextNode, | ||||
| 		} | ||||
| 
 | ||||
| 	def post(self, request, *args, **kwargs): | ||||
| 		######## FIXME: ROZEPSANE, NEFUNGUJE, DOPSAT !!!!!! ########### | ||||
| 		node = s.TreeNode.objects.get(pk=self.kwargs['pk']) | ||||
| 		kam = self.kwargs['kam'] | ||||
| 		co = self.kwargs['co'] | ||||
| 		typ = self.type_from_str[co] | ||||
| 
 | ||||
| 		raise NotImplementedError('Neni to dopsane, dopis to!') | ||||
| 
 | ||||
| 		if kam not in ('pred','syn','za'): | ||||
| 			raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna') | ||||
| 		 | ||||
| 		if co == m.TextNode: | ||||
| 			new_obj = m.Text() | ||||
| 			new_obj.save() | ||||
| 		elif co == m.CastNode: | ||||
| 			new_obj = m.CastNode() | ||||
| 			new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam)) | ||||
| 			new_obj.save() | ||||
| 		elif co == m.ReseniNode: | ||||
| 			new_obj = m | ||||
| 			pass | ||||
| 		elif co == m.UlohaZadaniNode: | ||||
| 			pass | ||||
| 		elif co == m.UlohaReseniNode: | ||||
| 			pass | ||||
| 		else: | ||||
| 			new_obj = None | ||||
| 
 | ||||
| 
 | ||||
| 		if kam == 'pred': | ||||
| 			pass | ||||
| 
 | ||||
| 
 | ||||
| 		if kam == 'syn': | ||||
| 			if typ == m.TextNode: | ||||
| 				text_obj = m.Text() | ||||
| 				text_obj.save() | ||||
| 				node = treelib.create_child(node,typ,text=text_obj) | ||||
| 			else: | ||||
| 				node = treelib.create_child(node,typ) | ||||
| 		if kam == 'za': | ||||
| 			if typ == m.TextNode: | ||||
| 				text_obj = m.Text() | ||||
| 				text_obj.save() | ||||
| 				node = treelib.create_node_after(node,typ,text=text_obj) | ||||
| 			else: | ||||
| 				node = treelib.create_node_after(node,typ) | ||||
| 
 | ||||
| 		return redirect(node.get_admin_url()) | ||||
| 		 | ||||
| 
 | ||||
| class TreeNodeSmazatView(generic.base.View): | ||||
| 	def post(self, request, *args, **kwargs): | ||||
| 		node = s.TreeNode.objects.get(pk=self.kwargs['pk']) | ||||
| 		if node.first_child: | ||||
| 			raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!') | ||||
| 		treelib.disconnect_node(node) | ||||
| 		node.delete() | ||||
| 		return redirect(request.headers.get('referer')) | ||||
| 
 | ||||
| class TreeNodeOdvesitPrycView(generic.base.View): | ||||
| 	def post(self, request, *args, **kwargs): | ||||
| 		node = s.TreeNode.objects.get(pk=self.kwargs['pk']) | ||||
| 		treelib.disconnect_node(node) | ||||
| 		node.root = None | ||||
| 		node.save() | ||||
| 		return redirect(request.headers.get('referer')) | ||||
| 
 | ||||
| 
 | ||||
| class TreeNodePodvesitView(generic.base.View): | ||||
| 	def post(self, request, *args, **kwargs): | ||||
| 		node = s.TreeNode.objects.get(pk=self.kwargs['pk']) | ||||
| 		kam = self.kwargs['kam'] | ||||
| 		if kam == 'pred': | ||||
| 			treelib.lower_node(node) | ||||
| 		elif kam == 'za': | ||||
| 			raise NotImplementedError('Podvěsit za není zatím podporováno') | ||||
| 		return redirect(request.headers.get('referer')) | ||||
| 
 | ||||
| class TreeNodeProhoditView(generic.base.View): | ||||
| 	def post(self, request, *args, **kwargs): | ||||
| 		node = s.TreeNode.objects.get(pk=self.kwargs['pk']) | ||||
| 		treelib.swap_succ(node) | ||||
| 		return redirect(request.headers.get('referer')) | ||||
| 		#FIXME ve formulari predat puvodni url a vratit redirect na ni | ||||
| 
 | ||||
| class SirotcinecView(generic.ListView): | ||||
| 	model = s.TreeNode | ||||
| 	template_name = 'seminar/orphanage.html' | ||||
| 	 | ||||
| 	def get_queryset(self): | ||||
| 		return s.TreeNode.objects.not_instance_of(s.RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None) | ||||
| 
 | ||||
| # FIXME pouzit Django REST Framework | ||||
| class TextWebView(generic.DetailView): | ||||
| 	model = s.Text | ||||
| 
 | ||||
| 	def get(self,request,*args, **kwargs): | ||||
| 		self.object = self.get_object() | ||||
| 		return JsonResponse(model_to_dict(self.object,exclude='do_cisla')) | ||||
| 
 | ||||
| 
 | ||||
| class ProblemView(generic.DetailView): | ||||
| 	model = s.Problem | ||||
| 	# Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView | ||||
| 	template_name = TreeNodeView.template_name | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super().get_context_data(**kwargs) | ||||
| 		verejne = nastaveni.aktualni_cislo.verejne() | ||||
| 		context['verejne'] = verejne	 | ||||
| 		# Teď potřebujeme doplnit tnldata do kontextu. | ||||
| 		# Ošklivý type switch, hezčí by bylo udělat to polymorfni. FIXME. | ||||
| 		if False: | ||||
| 			# Hezčí formátování zbytku :-P | ||||
| 			pass | ||||
| 		elif isinstance(self.object, s.Clanek) or  isinstance(self.object, s.Konfera): | ||||
| 			# Tyhle Problémy mají ŘešeníNode | ||||
| 			context['tnldata'] = TNLData.from_treenode(self.object.reseninode) | ||||
| 		elif isinstance(self.object, s.Uloha): | ||||
| 			# FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever | ||||
| 			tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode) | ||||
| 			tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode) | ||||
| 			context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) | ||||
| 		elif isinstance(self.object, s.Tema): | ||||
| 			rocniknode = self.object.rocnik.rocniknode | ||||
| 			context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, s.TemaVCisleNode)) | ||||
| 		else: | ||||
| 			raise ValueError("Obecný problém nejde zobrazit.") | ||||
| 		return context | ||||
| 
 | ||||
| 
 | ||||
| class AktualniZadaniView(generic.TemplateView): | ||||
| 	template_name = 'seminar/treenode.html' | ||||
| 
 | ||||
| # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | ||||
| #class AktualniZadaniView(TreeNodeView): | ||||
| #	def get_object(self): | ||||
| #		nastaveni = get_object_or_404(Nastaveni) | ||||
| #		return nastaveni.aktualni_cislo.cislonode | ||||
| # | ||||
| #	def get_context_data(self,**kwargs): | ||||
| #		nastaveni = get_object_or_404(Nastaveni) | ||||
| #		context = super().get_context_data(**kwargs) | ||||
| #		verejne = nastaveni.aktualni_cislo.verejne() | ||||
| #		context['verejne'] = verejne	 | ||||
| #		return context | ||||
| 
 | ||||
| #def AktualniZadaniView(request): | ||||
| #	nastaveni = get_object_or_404(Nastaveni) | ||||
| #	verejne = nastaveni.aktualni_cislo.verejne() | ||||
|  | @ -137,7 +383,19 @@ class AktualniZadaniView(TreeNodeView): | |||
| #				}, | ||||
| #			) | ||||
| # | ||||
| #def ZadaniTemataView(request): | ||||
| def ZadaniTemataView(request): | ||||
|         nastaveni = get_object_or_404(Nastaveni) | ||||
|         verejne = nastaveni.aktualni_cislo.verejne() | ||||
|         akt_rocnik = nastaveni.aktualni_cislo.rocnik | ||||
|         temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') | ||||
|         return render(request, 'seminar/tematka/rozcestnik.html', | ||||
|                         { | ||||
|                          'tematka': temata, | ||||
|                          'verejne': verejne, | ||||
|                                 }, | ||||
|                         ) | ||||
| 
 | ||||
| 
 | ||||
| #	nastaveni = get_object_or_404(Nastaveni) | ||||
| #	temata = verejna_temata(nastaveni.aktualni_rocnik) | ||||
| #	for t in temata: | ||||
|  | @ -242,45 +500,51 @@ def spravne_novinky(request): | |||
| 		qs = qs.filter(zverejneno=True) | ||||
| 	return qs.order_by('-datum') | ||||
| 
 | ||||
| def aktualni_temata(rocnik): | ||||
| 	""" | ||||
| 	Vrací PolymorphicQuerySet témat v daném ročníku, ke kterým se aktuálně dá něco odevzdat. | ||||
| 	""" | ||||
| 	return Tema.objects.filter(rocnik=rocnik, stav='zadany').order_by('kod') | ||||
| 
 | ||||
| 
 | ||||
| class TitulniStranaView(generic.ListView): | ||||
| 	template_name='seminar/titulnistrana.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		return spravne_novinky(self.request)[:5] | ||||
| 		return spravne_novinky(self.request)[:3] | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super(TitulniStranaView, self).get_context_data(**kwargs) | ||||
| 		nastaveni = get_object_or_404(Nastaveni) | ||||
| 		 | ||||
| 		deadline_soustredeni = (nastaveni.aktualni_cislo.datum_deadline_soustredeni, "soustredeni") | ||||
| 		preddeadline = (nastaveni.aktualni_cislo.datum_preddeadline, "preddeadline") | ||||
| 		deadline = (nastaveni.aktualni_cislo.datum_deadline, "deadline") | ||||
| 
 | ||||
| 		# zjisteni spravneho terminu | ||||
| 		if nastaveni.aktualni_cislo.datum_deadline_soustredeni: | ||||
| 			cas_deadline_soustredeni = nastaveni.aktualni_cislo.\ | ||||
| 				datum_deadline_soustredeni | ||||
| 			if (datetime.now().date() <= cas_deadline_soustredeni): | ||||
| 				cas_deadline = cas_deadline_soustredeni | ||||
| 				deadline_soustredeni = True | ||||
| 			else: | ||||
| 				cas_deadline = nastaveni.aktualni_cislo.datum_deadline | ||||
| 				deadline_soustredeni = False | ||||
| 		else: | ||||
| 			cas_deadline = nastaveni.aktualni_cislo.datum_deadline | ||||
| 			deadline_soustredeni = False | ||||
| 		try: | ||||
| 			nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0] | ||||
| 		except IndexError: | ||||
| 			nejblizsi_deadline = (None, None) # neni zadna aktualni deadline | ||||
| 
 | ||||
| 		# Pokud neni zverejnene cislo nezverejnuj odpocet | ||||
| 		if nastaveni.aktualni_cislo.verejne(): | ||||
| 			# pokus se zjistit termin odeslani a pokud neni zadany, | ||||
| 			# nezverejnuj odpocet | ||||
| 			context['deadline_soustredeni'] = deadline_soustredeni | ||||
| 			try: | ||||
| 				context['dead'] = datetime.combine(cas_deadline, | ||||
| 												 datetime.max.time()) | ||||
| 				context['ted'] = datetime.now() | ||||
| 			except: | ||||
| 				context['dead'] = None | ||||
| 		if nejblizsi_deadline[0] is not None: | ||||
| 			context['nejblizsi_deadline'] = datetime.combine(nejblizsi_deadline[0], datetime.max.time()) | ||||
| 		else: | ||||
| 			context['dead'] = None | ||||
| 			context['deadline_soustredeni'] = deadline_soustredeni | ||||
| 			context['nejblizsi_deadline'] = None | ||||
| 		 | ||||
| 		context['typ_deadline'] = nejblizsi_deadline[1] | ||||
| 
 | ||||
| 		# Aktuální témata | ||||
| 		nazvy_a_odkazy_na_aktualni_temata = [] | ||||
| 		akt_temata = aktualni_temata(nastaveni.aktualni_rocnik) | ||||
| 
 | ||||
| 		for tema in akt_temata: | ||||
| 			# FIXME: netuším, jestli funguje tema.verejne_url(), nemáme testdata na témátka - je to asi url vzhledem k ročníku | ||||
| 			nazvy_a_odkazy_na_aktualni_temata.append({'nazev':tema.nazev,'url':tema.verejne_url()}) | ||||
| 
 | ||||
| 		context['aktualni_temata'] = nazvy_a_odkazy_na_aktualni_temata | ||||
| 
 | ||||
| 		print(context) | ||||
| 
 | ||||
| 		return context | ||||
| 
 | ||||
| class StareNovinkyView(generic.ListView): | ||||
|  | @ -343,7 +607,8 @@ class ArchivView(generic.ListView): | |||
| ### Výsledky | ||||
| 
 | ||||
| def sloupec_s_poradim(setrizene_body): | ||||
| 	""" Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
| 	""" | ||||
| 	Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
| 	vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.), | ||||
| 	podle toho, jak jdou za sebou ve výsledkovce. | ||||
| 	Parametr: | ||||
|  | @ -383,7 +648,8 @@ def sloupec_s_poradim(setrizene_body): | |||
| 	return sloupec_s_poradim | ||||
| 
 | ||||
| def cisla_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Vrátí všechna čísla daného ročníku. | ||||
| 	""" | ||||
| 	Vrátí všechna čísla daného ročníku. | ||||
| 	Parametry: | ||||
| 		rocnik (Rocnik): ročník semináře | ||||
| 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | ||||
|  | @ -538,7 +804,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): | |||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) | ||||
| 	 | ||||
| 
 | ||||
| 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | ||||
| 	setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | ||||
| 	poradi = sloupec_s_poradim(setrizene_body) | ||||
|  | @ -773,6 +1039,7 @@ def vysledkovka_cisla(cislo, context=None): | |||
| 	return context | ||||
| 
 | ||||
| class CisloView(generic.DetailView): | ||||
| 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | ||||
| 	model = Cislo | ||||
| 	template_name = 'seminar/archiv/cislo.html' | ||||
| 
 | ||||
|  | @ -1308,6 +1575,32 @@ class PasswordChangeView(auth_views.PasswordChangeView): | |||
| 	#template_name = 'seminar/password_change.html' | ||||
| 	success_url = reverse_lazy('titulni_strana') | ||||
| 
 | ||||
| class VueTestView(generic.TemplateView): | ||||
| 	template_name = 'seminar/vuetest.html' | ||||
| 
 | ||||
| class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView): | ||||
| 	model = s.Obrazek | ||||
| 	form_class = f.NahrajObrazekKTreeNoduForm | ||||
| 
 | ||||
| 	def get_initial(self): | ||||
| 		initial = super().get_initial() | ||||
| 		initial['na_web'] = self.request.FILES['upload'] | ||||
| 		return initial | ||||
| 
 | ||||
| 
 | ||||
| 	def form_valid(self,form): | ||||
| 		print(self.request.headers) | ||||
| 		print(self.request.headers['Textid']) | ||||
| 		print(form.instance) | ||||
| 		print(form) | ||||
| 		self.object = form.save(commit=False) | ||||
| 		print(self.object.na_web) | ||||
| 		self.object.text = m.Text.objects.get(pk=int(self.request.headers['Textid'])) | ||||
| 		self.object.save() | ||||
| 
 | ||||
| 		return JsonResponse({"url":self.object.na_web.url}) | ||||
| 
 | ||||
| 	 | ||||
| 
 | ||||
| # Jen hloupé rozhazovátko | ||||
| def profilView(request): | ||||
|  | @ -1332,3 +1625,10 @@ def formularOKView(request): | |||
| 	} | ||||
| 	return render(request, template_name, context) | ||||
| 
 | ||||
| #------------------ Jak řešit - možná má být udělané úplně jinak | ||||
|      | ||||
| class JakResitView(generic.ListView): | ||||
| 	template_name = 'seminar/jak-resit.html' | ||||
|      | ||||
| 	def get_queryset(self): | ||||
| 		return None | ||||
|  |  | |||
							
								
								
									
										162
									
								
								seminar/views/views_rest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								seminar/views/views_rest.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| from rest_framework import serializers | ||||
| from rest_polymorphic.serializers import PolymorphicSerializer | ||||
| 
 | ||||
| import seminar.models as m | ||||
| from seminar import treelib | ||||
| 
 | ||||
| DEFAULT_NODE_DEPTH = 2 | ||||
| 
 | ||||
| class TextSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.Text | ||||
| 		fields = '__all__' | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class UlohaVzorakNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.UlohaVzorakNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 	 | ||||
| class UlohaZadaniNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.UlohaZadaniNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class RocnikNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.RocnikNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class CisloNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.CisloNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class MezicisloNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.MezicisloNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class TemaVCisleNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.TemaVCisleNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class OrgTextNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.OrgTextNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class PohadkaNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.PohadkaNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class TextNodeSerializer(serializers.ModelSerializer): | ||||
| 	text = TextSerializer() | ||||
| 
 | ||||
| 	class Meta: | ||||
| 		model = m.TextNode | ||||
| 		fields = ('id','text','polymorphic_ctype') | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class TextNodeWriteSerializer(serializers.ModelSerializer): | ||||
| 	text = TextSerializer() | ||||
| 
 | ||||
| 	def update(self,node,validated_data): | ||||
| 		node.text.na_web = validated_data.get('text').get('na_web') | ||||
| 		node.text.save() | ||||
| 		return node | ||||
| 
 | ||||
| 	class Meta: | ||||
| 		model = m.TextNode | ||||
| 		fields = ('id','text') | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class TextNodeCreateSerializer(serializers.ModelSerializer): | ||||
| 	text = TextSerializer() | ||||
| 	refnode = serializers.CharField() | ||||
| 	where = serializers.CharField() | ||||
| 
 | ||||
| 	def create(self,validated_data): | ||||
| 		temp_text = validated_data.pop('text') | ||||
| 		where = validated_data.pop('where') | ||||
| 		refnode_id = validated_data.pop('refnode') | ||||
| 		refnode = m.TreeNode.objects.get(pk=refnode_id) | ||||
| 		text = m.Text.objects.create(**temp_text) | ||||
| 		if where == 'syn': | ||||
| 			node = treelib.create_child(refnode,m.TextNode,text=text) | ||||
| 		elif where == 'za': | ||||
| 			node = treelib.create_node_after(refnode,m.TextNode,text=text) | ||||
| 		elif where == 'pred': | ||||
| 			node = treelib.create_node_before(refnode,m.TextNode,text=text) | ||||
| 		node.where = None | ||||
| 		node.refnode = None | ||||
| 		return node | ||||
| 
 | ||||
| 	class Meta: | ||||
| 		model = m.TextNode | ||||
| 		fields = ('text','where','refnode') | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class CastNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.CastNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class CastNodeCreateSerializer(serializers.ModelSerializer): | ||||
| 	refnode = serializers.CharField() | ||||
| 	where = serializers.CharField() | ||||
| 
 | ||||
| 	def create(self,validated_data): | ||||
| 		temp_nadpis = validated_data.pop('nadpis') | ||||
| 		where = validated_data.pop('where') | ||||
| 		refnode_id = validated_data.pop('refnode') | ||||
| 		refnode = m.TreeNode.objects.get(pk=refnode_id) | ||||
| 		if where == 'syn': | ||||
| 			node = treelib.create_child(refnode,m.CastNode,nadpis=temp_nadpis) | ||||
| 		elif where == 'za': | ||||
| 			node = treelib.create_node_after(refnode,m.CastNode,nadpis=temp_nadpis) | ||||
| 		elif where == 'pred': | ||||
| 			node = treelib.create_node_before(refnode,m.CastNode,nadpis=temp_nadpis) | ||||
| 		node.where = None | ||||
| 		node.refnode = None | ||||
| 		return node | ||||
| 
 | ||||
| 	class Meta: | ||||
| 		model = m.CastNode | ||||
| 		fields = ('nadpis','where','refnode') | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| class ReseniNodeSerializer(serializers.ModelSerializer): | ||||
| 	class Meta: | ||||
| 		model = m.ReseniNode | ||||
| 		fields = '__all__' | ||||
| 		depth = DEFAULT_NODE_DEPTH | ||||
| 
 | ||||
| 
 | ||||
| class TreeNodeSerializer(PolymorphicSerializer): | ||||
| 	model_serializer_mapping = { | ||||
| 		m.RocnikNode: RocnikNodeSerializer, | ||||
| 		m.CisloNode: CisloNodeSerializer, | ||||
| 		m.MezicisloNode: MezicisloNodeSerializer, | ||||
| 		m.TemaVCisleNode: TemaVCisleNodeSerializer, | ||||
| 		m.OrgTextNode: OrgTextNodeSerializer, | ||||
| 		m.UlohaZadaniNode: UlohaZadaniNodeSerializer, | ||||
| 		m.UlohaVzorakNode: UlohaVzorakNodeSerializer, | ||||
| 		m.PohadkaNode: PohadkaNodeSerializer, | ||||
| 		m.TextNode: TextNodeSerializer, | ||||
| 		m.CastNode: CastNodeSerializer, | ||||
| 		m.ReseniNode: ReseniNodeSerializer, | ||||
| 	} | ||||
| 
 | ||||
							
								
								
									
										77
									
								
								seminar/viewsets.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								seminar/viewsets.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| from rest_framework import viewsets,filters | ||||
| from . import models as m | ||||
| from . import views | ||||
| 
 | ||||
| class ReadWriteSerializerMixin(object): | ||||
| 	""" | ||||
| 	Overrides get_serializer_class to choose the read serializer | ||||
| 	for GET requests and the write serializer for POST requests. | ||||
| 
 | ||||
| 	Set read_serializer_class and write_serializer_class attributes on a | ||||
| 	viewset.  | ||||
| 	""" | ||||
| 
 | ||||
| 	read_serializer_class = None | ||||
| 	create_serializer_class = None | ||||
| 	write_serializer_class = None | ||||
| 
 | ||||
| 	def get_serializer_class(self):		 | ||||
| 		if self.action == "create": | ||||
| 			return self.get_create_serializer_class() | ||||
| 		if self.action in ["update", "partial_update", "destroy"]: | ||||
| 			return self.get_write_serializer_class() | ||||
| 		return self.get_read_serializer_class() | ||||
| 
 | ||||
| 	def get_read_serializer_class(self): | ||||
| 		assert self.read_serializer_class is not None, ( | ||||
| 			"'%s' should either include a `read_serializer_class` attribute," | ||||
| 			"or override the `get_read_serializer_class()` method." | ||||
| 			% self.__class__.__name__ | ||||
| 		) | ||||
| 		return self.read_serializer_class | ||||
| 
 | ||||
| 	def get_write_serializer_class(self): | ||||
| 		assert self.write_serializer_class is not None, ( | ||||
| 			"'%s' should either include a `write_serializer_class` attribute," | ||||
| 			"or override the `get_write_serializer_class()` method." | ||||
| 			% self.__class__.__name__ | ||||
| 		) | ||||
| 		return self.write_serializer_class | ||||
| 
 | ||||
| 	def get_create_serializer_class(self): | ||||
| 		assert self.create_serializer_class is not None, ( | ||||
| 			"'%s' should either include a `create_serializer_class` attribute," | ||||
| 			"or override the `get_create_serializer_class()` method." | ||||
| 			% self.__class__.__name__ | ||||
| 		) | ||||
| 		return self.create_serializer_class | ||||
| 
 | ||||
| class UlohaVzorakNodeViewSet(viewsets.ModelViewSet): | ||||
| 	queryset = m.UlohaVzorakNode.objects.all() | ||||
| 	serializer_class = views.UlohaVzorakNodeSerializer | ||||
| 
 | ||||
| class TextViewSet(viewsets.ModelViewSet): | ||||
| 	queryset = m.Text.objects.all() | ||||
| 	serializer_class = views.TextSerializer | ||||
| 
 | ||||
| class TextNodeViewSet(ReadWriteSerializerMixin,viewsets.ModelViewSet): | ||||
| 	queryset = m.TextNode.objects.all() | ||||
| 	read_serializer_class = views.TextNodeSerializer | ||||
| 	write_serializer_class = views.TextNodeWriteSerializer | ||||
| 	create_serializer_class = views.TextNodeCreateSerializer | ||||
| 
 | ||||
| class CastNodeViewSet(ReadWriteSerializerMixin,viewsets.ModelViewSet): | ||||
| 	queryset = m.CastNode.objects.all() | ||||
| 	read_serializer_class = views.CastNodeSerializer | ||||
| 	write_serializer_class = views.CastNodeSerializer | ||||
| 	create_serializer_class = views.CastNodeCreateSerializer | ||||
| 
 | ||||
| class UlohaVzorakNodeViewSet(viewsets.ModelViewSet): | ||||
| 	serializer_class = views.UlohaVzorakNodeSerializer | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		queryset = m.UlohaVzorakNode.objects.all() | ||||
| 		nazev = self.request.query_params.get('nazev',None) | ||||
| 		if nazev is not None: | ||||
| 			queryset = queryset.filter(nazev__contains=nazev) | ||||
| 		return queryset | ||||
							
								
								
									
										22
									
								
								vue_frontend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vue_frontend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| .DS_Store | ||||
| node_modules | ||||
| /dist | ||||
| 
 | ||||
| # local env files | ||||
| .env.local | ||||
| .env.*.local | ||||
| 
 | ||||
| # Log files | ||||
| npm-debug.log* | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
| pnpm-debug.log* | ||||
| 
 | ||||
| # Editor directories and files | ||||
| .idea | ||||
| .vscode | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
							
								
								
									
										5
									
								
								vue_frontend/babel.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vue_frontend/babel.config.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| module.exports = { | ||||
|   presets: [ | ||||
|     '@vue/cli-plugin-babel/preset' | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										48
									
								
								vue_frontend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vue_frontend/package.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| { | ||||
|   "name": "vue_frontend", | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "serve": "vue-cli-service serve", | ||||
|     "build": "vue-cli-service build", | ||||
|     "lint": "vue-cli-service lint" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@ckeditor/ckeditor5-upload": "^23.0.0", | ||||
|     "@ckeditor/ckeditor5-vue": "^1.0.1", | ||||
|     "axios": "^0.19.2", | ||||
|     "ckeditor5-build-classic-simple-upload-adapter-image-resize": "^1.0.4", | ||||
|     "core-js": "^3.6.5", | ||||
|     "vue": "^2.6.11", | ||||
|     "vue-router": "^3.4.3" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@vue/cli-plugin-babel": "~4.4.0", | ||||
|     "@vue/cli-plugin-eslint": "~4.4.0", | ||||
|     "@vue/cli-service": "^4.5.6", | ||||
|     "babel-eslint": "^10.1.0", | ||||
|     "eslint": "^6.7.2", | ||||
|     "eslint-plugin-vue": "^6.2.2", | ||||
|     "vue-template-compiler": "^2.6.11", | ||||
|     "webpack-bundle-tracker": "0.4.3" | ||||
|   }, | ||||
|   "eslintConfig": { | ||||
|     "root": true, | ||||
|     "env": { | ||||
|       "node": true | ||||
|     }, | ||||
|     "extends": [ | ||||
|       "plugin:vue/essential", | ||||
|       "eslint:recommended" | ||||
|     ], | ||||
|     "parserOptions": { | ||||
|       "parser": "babel-eslint" | ||||
|     }, | ||||
|     "rules": {} | ||||
|   }, | ||||
|   "browserslist": [ | ||||
|     "> 1%", | ||||
|     "last 2 versions", | ||||
|     "not dead" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										7
									
								
								vue_frontend/src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vue_frontend/src/App.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| <template> | ||||
|   <router-view id="app"/> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { name: 'app' } | ||||
| </script> | ||||
							
								
								
									
										50
									
								
								vue_frontend/src/components/AddNewNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vue_frontend/src/components/AddNewNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| <template> | ||||
| 	<div class="addnewnode"> | ||||
| 		<button v-if="types.includes('castNode')" v-on:click="selected='castNode'" :disabled="selected && selected !== 'castNode'">Část</button> | ||||
| 		<button v-if="types.includes('textNode')" v-on:click="selected='textNode'" :disabled="selected && selected !== 'textNode'">Text</button> | ||||
| 		<button v-if="types.includes('reseniNode')" v-on:click="selected='reseniNode'" :disabled="selected && selected !== 'reseniNode'">Řešení</button> | ||||
| 		<button v-if="types.includes('ulohaZadaniNode')" v-on:click="selected='ulohaZadaniNode'" :disabled="selected && selected !== 'ulohaZadaniNode'">Zadání úlohy</button> | ||||
| 		<button v-if="types.includes('ulohaVzorakNode')" v-on:click="selected='ulohaVzorakNode'" :disabled="selected && selected !== 'ulohaVzorakNode'">Vzorák</button> | ||||
| 		<div v-if="selected"> | ||||
| 			<component :is='selected' :item='null' :where="where" :refnode="refnode" create></component> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import castNode from './CastNode.vue' | ||||
| import textNode from './TextNode.vue' | ||||
| import ulohaZadaniNode from './UlohaZadaniNode.vue' | ||||
| import ulohaVzorakNode from './UlohaVzorakNode.vue' | ||||
| //import reseniNode from './UlohaVzorakNode.vue' | ||||
| 
 | ||||
| export default { | ||||
| 	name: 'AddNewNode', | ||||
| 	props: { | ||||
| 		types: Array, | ||||
| 		where: String, | ||||
| 		refnode: Object, | ||||
| 	}, | ||||
| 	data: () => ({ | ||||
| 		selected: null, | ||||
| 	}), | ||||
| 	components: { | ||||
| 		castNode, | ||||
| 		textNode, | ||||
| 		ulohaZadaniNode, | ||||
| 		ulohaVzorakNode, | ||||
| 	}, | ||||
| 	mounted: function() { | ||||
| 		this.$root.$on('dataUpdated',(arg) => { | ||||
| 			this.selected = null; | ||||
| 			console.log('dataUpdated'+arg); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| .addnewnode { | ||||
| 	display: inline; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										86
									
								
								vue_frontend/src/components/CastNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vue_frontend/src/components/CastNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| <template> | ||||
| 	<div class="castnode"> | ||||
| 		<!--pre>CastNode {{item}} {{typeof(item)}}</pre--> | ||||
| 		<div v-if="editorShow"> | ||||
| 			<input type="text" v-model="currentText" /> | ||||
| 			<button v-on:click="saveText">Uložit</button> | ||||
| 			<button v-on:click="currentText = originalText;editorShow=!editorShow;">Zahodit úpravy</button> | ||||
| 		</div> | ||||
| 		<div v-else> | ||||
| 		<h4>{{ currentText }} </h4> <button v-if="editorMode" v-on:click="editorShow=!editorShow">Upravit</button> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import axios from 'axios' | ||||
| 
 | ||||
| export default { | ||||
| 	name: 'CastNode', | ||||
| 	data: () => ({ | ||||
| 		editorShow: false, | ||||
| 		currentText: "", | ||||
| 		originalText: "", | ||||
| 	}), | ||||
| 	props: { | ||||
| 		item: Object, | ||||
| 		editorShow: Boolean, | ||||
| 		create: Boolean, | ||||
| 		where: String, | ||||
| 		refnode: Object | ||||
| 	}, | ||||
| 	mounted: function() { | ||||
| 		if (this.create){ | ||||
| 			this.currentText = ""; | ||||
| 			this.originalText = ""; | ||||
| 			this.editorShow = true; | ||||
| 		} | ||||
| 		else { | ||||
| 			this.currentText = this.item.node.nadpis; | ||||
| 			this.originalText = this.item.node.nadpis; | ||||
| 		} | ||||
| 
 | ||||
| 		//this.getText(); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		saveText: function() { | ||||
| 			console.log("Saving cast"); | ||||
| 			console.log(this.currentText); | ||||
| 			if (this.create){ | ||||
| 				console.log(this.refnode); | ||||
| 				console.log(this.where); | ||||
| 				axios.post('/api/castnode/',{ | ||||
| 					'nadpis': this.currentText, | ||||
| 					'refnode': this.refnode.id, | ||||
| 					'where': this.where | ||||
| 				}).then(response => { | ||||
| 					this.originalText = response.data.nadpis; | ||||
| 					this.$root.$emit('updateData',"castNode create update"); | ||||
| 				}) | ||||
| 				.catch(e => { | ||||
| 					console.log(e); | ||||
| 					this.errors.push(e); | ||||
| 				}); | ||||
| 			} else { | ||||
| 				axios.put('/api/castnode/'+this.item.node.id+'/',{ | ||||
| 					'nadpis': this.currentText, | ||||
| 					'id': this.item.node.id | ||||
| 				}).then(response => { | ||||
| 					this.originalText = response.data.nadpis; | ||||
| 				}) | ||||
| 				.catch(e => { | ||||
| 					console.log(e); | ||||
| 					this.errors.push(e) | ||||
| 				}); | ||||
| 				 | ||||
| 			} | ||||
| 
 | ||||
| 			this.editorShow = false; | ||||
| 		} | ||||
| 		} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| </style> | ||||
							
								
								
									
										19
									
								
								vue_frontend/src/components/CisloNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vue_frontend/src/components/CisloNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| <template> | ||||
| 	<div class="cislonode"> | ||||
| 		<!--pre>CisloNode {{item}} {{typeof(item)}}</pre--> | ||||
| 		<h1>{{ item.node.cislo.poradi }}. číslo</h1> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
| 	name: 'CisloNode', | ||||
| 	props: { | ||||
| 		item: Object | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| </style> | ||||
							
								
								
									
										21
									
								
								vue_frontend/src/components/RocnikNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vue_frontend/src/components/RocnikNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| <template> | ||||
| 
 | ||||
| 	<div class="rocniknode"> | ||||
| 		<!--pre>RocnikNode {{item}} {{typeof(item)}}</pre--> | ||||
| 		<h1>Ročník {{ item.node.rocnik.rocnik }} ({{ item.node.rocnik.prvni_rok }}/{{item.node.rocnik.prvni_rok+1 }})</h1> | ||||
| 	</div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
| 	name: 'RocnikNode', | ||||
| 	props: { | ||||
| 		item: Object | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| </style> | ||||
							
								
								
									
										19
									
								
								vue_frontend/src/components/TemaVCisleNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vue_frontend/src/components/TemaVCisleNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| <template> | ||||
| 	<div class="temavcislenode"> | ||||
| 		<!--pre>TemaVCisleNode {{item}} {{typeof(item)}}</pre--> | ||||
| 		<h2>Téma {{ item.node.tema.kod }}: {{ item.node.tema.nazev }}</h2> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
| 	name: 'TemaVCisleNode', | ||||
| 	props: { | ||||
| 		item: Object | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| </style> | ||||
							
								
								
									
										174
									
								
								vue_frontend/src/components/TextNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vue_frontend/src/components/TextNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | |||
| <template> | ||||
| 	<div v-if="loading" class="loading"> | ||||
| 	<p>Loading...</p> | ||||
| 	</div> | ||||
| 	<div v-else class="textnode"> | ||||
| 		<!--pre>TextNode {{item}} {{typeof(item)}}</pre--> | ||||
| 		<template v-if="editorShow"> | ||||
| 
 | ||||
| 			<div v-if="plainEditShow"> | ||||
| 				<textarea id="textarea" v-model="currentText" rows="8" cols="50"></textarea> | ||||
| 				<br> | ||||
| 				<button v-on:click="plainEditShow=!plainEditShow">Editovat v editoru</button> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<div v-else> | ||||
| 				<component v-bind:is="editorComponent" :editor="editor" v-model="currentText" :config="editorConfig"></component> | ||||
| 				<button v-on:click="plainEditShow=!plainEditShow">Editovat HTML</button> | ||||
| 			</div> | ||||
| 			<button v-on:click="saveText">Uložit</button> | ||||
| 			<button v-on:click="currentText = originalText;editorShow=!editorShow;">Zahodit úpravy</button> | ||||
| 
 | ||||
| 		</template> | ||||
| 
 | ||||
| 		<template v-else v-bind:class="changedObject"> | ||||
| 			<p v-html="currentText"></p> | ||||
| 			<button v-if="editorMode" v-on:click="editorShow=!editorShow">Upravit</button> | ||||
| 		</template> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import axios from 'axios' | ||||
| 
 | ||||
| //import ClassicEditor from '@ckeditor/ckeditor5-build-classic' | ||||
| import ClassicEditor from 'ckeditor5-build-classic-simple-upload-adapter-image-resize'; | ||||
| import CKEditor from '@ckeditor/ckeditor5-vue'; | ||||
| 
 | ||||
| export default { | ||||
| 	name: 'TextNode', | ||||
| 	data: () => ({ | ||||
| 		loading: false, | ||||
| 		editor: ClassicEditor, | ||||
| 		editorData: '<p>Content of the editor.</p>', | ||||
| 		editorConfig: { | ||||
| 			extraPlugins: ['SimpleUploadAdapter'], | ||||
| 			simpleUpload: { | ||||
| 				uploadUrl: "/temp/image_upload/", | ||||
| 				headers: {}, | ||||
| 				withCredentials: true | ||||
| 			} | ||||
| 			// The configuration of the editor. | ||||
| 		}, | ||||
| 		editorShow: false, | ||||
| 		plainEditShow: false, | ||||
| 		editorComponent: CKEditor.component, | ||||
| 		currentText: "",// this.item.node.text.na_web, | ||||
| 		originalText: "",// this.item.node.text.na_web, | ||||
| 	}), | ||||
| 	computed: { | ||||
| 		changedObject: function () { | ||||
| 			//console.log(this.currentText); | ||||
| 			//console.log(this.originalText); | ||||
| 			return { | ||||
| 				changed: this.currentText !== this.originalText, | ||||
| 				} | ||||
| 			}, | ||||
| 		textId: function () { | ||||
| 			console.log(this.create); | ||||
| 			console.log(this.node.text.id); | ||||
| 			if (this.create){ | ||||
| 				return null; | ||||
| 			} | ||||
| 			return this.node.text.id; | ||||
| 
 | ||||
| 		} | ||||
| 	}, | ||||
| 	props: { | ||||
| 		item: Object, | ||||
| 		editorShow: Boolean, | ||||
| 		editorMode: Boolean, | ||||
| 		create: Boolean, | ||||
| 		where: String, | ||||
| 		refnode: Object | ||||
| 	}, | ||||
| 	mounted: function() { | ||||
| 		//console.log("mounted"); | ||||
| 		this.editorConfig.simpleUpload.headers['X-CSRFToken'] = this.getCookie('csrftoken'); | ||||
| 		axios.defaults.headers.common['X-CSRFToken'] = this.getCookie('csrftoken'); | ||||
| 		if (this.create){ | ||||
| 			this.currentText = ""; | ||||
| 			this.originalText = ""; | ||||
| 			this.editorShow = true; | ||||
| 		} else { | ||||
| 			this.currentText = this.item.node.text.na_web; | ||||
| 			this.originalText = this.item.node.text.na_web; | ||||
| 			this.editorConfig.simpleUpload.headers.textId = this.item.node.text.id; | ||||
| 
 | ||||
| 		} | ||||
| 		//this.getText(); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		getCookie: function (name){ | ||||
| 			var cookieValue = null; | ||||
| 			if (document.cookie && document.cookie != '') { | ||||
| 				var cookies = document.cookie.split(';'); | ||||
| 				for (var i = 0; i < cookies.length; i++) { | ||||
| 					var cookie = cookies[i].trim(); | ||||
| 					// Does this cookie string begin with the name we want? | ||||
| 
 | ||||
| 					if (cookie.substring(0, name.length + 1) == (name + '=')) { | ||||
| 						cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			return cookieValue; | ||||
| 		}, | ||||
| 		getText: function() { | ||||
| 			this.loading = true; | ||||
| 			console.log(this.item); | ||||
| 			console.log(this.item.node.text); | ||||
| 			axios.get('/treenode/text/'+this.item.node.text) | ||||
| 			.then((response) => { | ||||
| 				this.text = response.data.na_web; | ||||
| 				this.loading = false; | ||||
| 			}) | ||||
| 			.catch((err) => { | ||||
| 				this.loading = false; | ||||
| 				console.log(err); | ||||
| 				}) | ||||
| 		}, | ||||
| 		 | ||||
| 		saveText: function() { | ||||
| 			console.log("Saving text"); | ||||
| 			console.log(this.currentText); | ||||
| 			if (this.create){ | ||||
| 				console.log(this.refnode); | ||||
| 				console.log(this.where); | ||||
| 				axios.post('/api/textnode/',{ | ||||
| 					'text': { 'na_web': this.currentText}, | ||||
| 					'refnode': this.refnode.id, | ||||
| 					'where': this.where | ||||
| 				}).then(response => { | ||||
| 					this.originalText = response.data.text.na_web; | ||||
| 					this.loading = false; | ||||
| 					this.$root.$emit('updateData',"textNode create update"); | ||||
| 					}) | ||||
| 				.catch(e => { | ||||
| 					this.errors.push(e) | ||||
| 				}); | ||||
| 			} else { | ||||
| 				axios.put('/api/textnode/'+this.item.node.id+'/',{ | ||||
| 					'text': { 'na_web': this.currentText}, | ||||
| 					'id': this.item.node.id | ||||
| 				}).then(response => { | ||||
| 					this.originalText = response.data.text.na_web; | ||||
| 				}) | ||||
| 				.catch(e => { | ||||
| 					this.errors.push(e) | ||||
| 				}); | ||||
| 				 | ||||
| 			} | ||||
| 			this.editorShow = false; | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| .changed { | ||||
| 	background-color: 'yellow'; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										161
									
								
								vue_frontend/src/components/TreeNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vue_frontend/src/components/TreeNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,161 @@ | |||
| <template> | ||||
| 
 | ||||
| 	<div :class="editorMode ? 'treenode-org' : 'treenode'"> | ||||
| 	<!--b v-if="v_tematu">v tematu</b> | ||||
| 	<b v-if="visible">visible</b> | ||||
| 	Force visible: {{String(force_visible)}}--> | ||||
| 	<component :is='item.node.polymorphic_ctype.model' :item='item' :key='item.node.id' | ||||
| 								:editorMode="editorMode" | ||||
| 								:debugMode="debugMode"></component> | ||||
| 
 | ||||
| 
 | ||||
| 	<button v-if="debugMode" v-on:click="debugShow = !debugShow" class="nodebug">Ladící data</button> <!-- bude tu nějaký if na class="nodebug", v debug módu bude tlačítko vidět, jinak ne --> | ||||
| 	<div v-if="debugShow"> | ||||
| 		<pre>{{ item.node.polymorphic_ctype.model }}</pre> | ||||
| 		<pre>{{ item }}</pre> | ||||
| 	</div> | ||||
| 
 | ||||
| 	<div v-if="item.children.length === 0"> | ||||
| 		<div v-if="item.appendable_children.length > 0 && editorMode"> | ||||
| 			<b>Vložit jako syna: </b> | ||||
| 			<addnewnode :types="item.appendable_children" :refnode="item.node" where="syn" /> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 
 | ||||
| 	<div v-else :class="editorMode ? 'children-org' : 'children'"> <!-- bude tu nějaký if na class="children" --> | ||||
| 		<div v-if="item.children.length > 0 && item.children[0].appendable_siblings.length > 0 && editorMode"> | ||||
| 			<b>Vložit před: </b> | ||||
| 			<addnewnode :types="item.children[0].appendable_siblings" :refnode="item.children[0].node" where="pred" />  | ||||
| 		</div> | ||||
| 			<div v-if="item.node.polymorphic_ctype.model==='temavcislenode'"> | ||||
| 				<!--Children: {{String(showChildren)}}--> | ||||
| 				<div v-for="(chld, index) in item.children" v-bind:key="chld.nazev" > | ||||
| 					<!--Hide: {{hideNode(chld)}}, v tematu: {{v_tematu}}, force_visible: {{force_visible}}--> | ||||
| 					<div v-if="!hideNode(chld)"> | ||||
| 						<div v-if="chld.node.polymorphic_ctype.model==='ulohazadaninode'"> | ||||
| 							<button v-if="showChildren" v-on:click="showChildren=!showChildren"> Schovat </button> | ||||
| 							<button v-else v-on:click="showChildren=!showChildren"> Rozbalit </button> | ||||
| 							<TreeNode :item="chld" :v_tematu="true" | ||||
| 									:force_visible="showChildren" | ||||
| 									:editorMode="editorMode" | ||||
| 									:debugMode="debugMode"> | ||||
| 							</TreeNode> | ||||
| 						</div> | ||||
| 						<div v-else> | ||||
| 							<TreeNode :item="chld" :v_tematu="true" | ||||
| 									:force_visible="showChildren" | ||||
| 									:editorMode="editorMode" | ||||
| 									:debugMode="debugMode"> | ||||
| 							</TreeNode> | ||||
| 						</div> | ||||
| 						<div v-if="chld.appendable_siblings.length > 0 && editorMode" > | ||||
| 							<b v-if="index < (item.children.length - 1)">Vložit mezi: </b> | ||||
| 							<b v-else>Vložit za: </b> | ||||
| 							<addnewnode :types="chld.appendable_siblings" :refnode="chld.node" where="za" />  | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<button v-if="showChildren" v-on:click="showChildren=!showChildren"> Schovat </button> | ||||
| 				<button v-else v-on:click="showChildren=!showChildren"> Rozbalit </button> | ||||
| 			</div> | ||||
| 			<div v-else> | ||||
| 				<div v-for="(chld, index) in item.children" v-bind:key="chld.nazev" > | ||||
| 					<div v-if="v_tematu && chld.node.polymorphic_ctype.model==='ulohazadaninode'"> | ||||
| 						<div> Tady možná něco je </div> | ||||
| 						<TreeNode :item="chld" :v_tematu="v_tematu" | ||||
| 								:force_visible="force_visible" | ||||
| 								:editorMode="editorMode" | ||||
| 								:debugMode="debugMode"> | ||||
| 						</TreeNode> | ||||
| 					</div> | ||||
| 					<div v-else> | ||||
| 						<TreeNode :item="chld" :v_tematu="v_tematu" | ||||
| 								:force_visible="force_visible" | ||||
| 								:editorMode="editorMode" | ||||
| 								:debugMode="debugMode"> | ||||
| 						</TreeNode> | ||||
| 					</div> | ||||
| 					<div v-if="chld.appendable_siblings.length > 0 && editorMode" > | ||||
| 						<b v-if="index < (item.children.length - 1)">Vložit mezi: </b> | ||||
| 						<b v-else>Vložit za: </b> | ||||
| 						<addnewnode :types="chld.appendable_siblings" :refnode="chld.node" where="za" />  | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 
 | ||||
| 	</div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| 
 | ||||
| import rocniknode from './RocnikNode.vue' | ||||
| import cislonode from './CisloNode.vue' | ||||
| import temavcislenode from './TemaVCisleNode.vue' | ||||
| import castnode from './CastNode.vue' | ||||
| import textnode from './TextNode.vue' | ||||
| import ulohazadaninode from './UlohaZadaniNode.vue' | ||||
| import ulohavzoraknode from './UlohaVzorakNode.vue' | ||||
| import addnewnode from './AddNewNode.vue' | ||||
| 
 | ||||
| export default { | ||||
| 	name: 'TreeNode', | ||||
| 	components: { | ||||
| 		rocniknode, | ||||
| 		cislonode, | ||||
| 		temavcislenode, | ||||
| 		castnode, | ||||
| 		textnode, | ||||
| 		ulohazadaninode, | ||||
| 		ulohavzoraknode, | ||||
| 		addnewnode | ||||
| 	}, | ||||
| 	data: () => ({ | ||||
| 		debugShow: false, | ||||
| 		showChildren: false | ||||
| 	}), | ||||
| 	computed: { | ||||
| 	}, | ||||
| 	props: { | ||||
| 		item: Object, | ||||
| 		force_visible: Boolean, | ||||
| 		v_tematu: Boolean, | ||||
| 		editorMode: Boolean, | ||||
| 		debugMode: Boolean, | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		hideNode: function(chld){ | ||||
| 			if (this.showChildren || this.force_visible){ | ||||
| 				return false; | ||||
| 			} | ||||
| 			if (chld.node.polymorphic_ctype.model === 'ulohazadaninode'){ | ||||
| 				return false; | ||||
| 			} | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| 
 | ||||
| 
 | ||||
| .treenode-org { | ||||
|   padding: 5px; | ||||
|   margin: 5px; | ||||
|   border: #6a0043 2px solid; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .children-org { | ||||
|   padding: 10px; | ||||
|   margin: 5px; | ||||
|   border: #6a0043 2px dashed; | ||||
| } | ||||
| 
 | ||||
| .nodebug { | ||||
| 	/* display: none; */ | ||||
| } | ||||
| 
 | ||||
| </style> | ||||
							
								
								
									
										70
									
								
								vue_frontend/src/components/TreeNodeRoot.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vue_frontend/src/components/TreeNodeRoot.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| <template> | ||||
| 	<div id="app"> | ||||
| 		<div id="loading" v-if="loading"> | ||||
| 		Loading... | ||||
| 		</div> | ||||
| 		<!--pre> | ||||
| 		{{item}} | ||||
| 		</pre--> | ||||
| 		<button v-show="editorMode" v-on:click="editorMode = false">Vypnout editační mód</button> | ||||
| 		<button v-show="!editorMode" v-on:click="editorMode = true">Zapnout editační mód</button> | ||||
| 		<button v-show="debugMode" v-on:click="debugMode = false">Vypnout ladicí mód</button> | ||||
| 		<button v-show="!debugMode" v-on:click="debugMode = true">Zapnout ladicí mód</button> | ||||
| 		<TreeNode :item="item" :editorMode="editorMode" :debugMode="debugMode"/> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import TreeNode from './TreeNode.vue' | ||||
| import axios from 'axios' | ||||
| export default { | ||||
| 	name: 'App', | ||||
| 	components: { | ||||
| 		TreeNode, | ||||
| 	}, | ||||
| 	data: () => ({ | ||||
| 		loading: true, | ||||
| 		item: null, | ||||
| 		tnid: 1, | ||||
| 		editorMode: false, | ||||
| 		debugMode: false, | ||||
| 	}), | ||||
| 	props:{ | ||||
| 		tnid: Number, | ||||
| 		editorMode: Boolean, | ||||
| 		debugMode: Boolean, | ||||
| 	}, | ||||
| 	mounted: function() { | ||||
| 		this.getArticles(); | ||||
| 		this.$root.$on('updateData',(arg) => { | ||||
| 			console.log(arg); | ||||
| 			this.getArticles(); | ||||
| 		}); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		getArticles: function() { | ||||
| 			this.loading = true; | ||||
| 			axios.get('/treenode/'+this.tnid+'/json/') | ||||
| 			.then((response) => { | ||||
| 				this.item = response.data; | ||||
| 				this.loading = false; | ||||
| 				console.log('Data updated'); | ||||
| 				this.$root.$emit('dataUpdated',"dataUpdated"); | ||||
| 			}) | ||||
| 			.catch((err) => { | ||||
| 				this.loading = false; | ||||
| 				console.log(err); | ||||
| 				}) | ||||
| 		} | ||||
| 	}, | ||||
| 	events: { | ||||
| 		updateData: function(){ | ||||
| 			this.getArticles() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| @import '../../../mamweb/static/css/mamweb.css'; | ||||
| </style> | ||||
							
								
								
									
										74
									
								
								vue_frontend/src/components/UlohaVzorakNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								vue_frontend/src/components/UlohaVzorakNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | |||
| <template> | ||||
| 	<div class="ulohavzoraknode"> | ||||
| 		<!--pre>UlohaVzorakNode {{item}} {{typeof(item)}}</pre--> | ||||
| 		<h5>Řešení {{item.node.uloha.cislo_zadani.poradi}}.{{ item.node.uloha.kod }}: {{ item.node.uloha.nazev }}</h5> | ||||
| 		<button v-if="editorMode" v-on:click="showSelect=!showSelect" class="upravit">Upravit</button> | ||||
| 		<div v-if="showSelect"> | ||||
| 			<form class="searchForm" v-on:submit.prevent="submitSearch"> | ||||
| 				<input type="text" v-model="searchQuery" placeholder="Napište název" @keyup="submitSearch"> | ||||
| 			</form> | ||||
| 			<div class="searchResult" v-show="isResult"> | ||||
| 				<ul> | ||||
| 					<li v-for="res in searchResults" :key="res.id" v-on:click="setSelected(res)">{{res.nazev}}</li> | ||||
| 				</ul> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import axios from 'axios' | ||||
| 
 | ||||
| export default { | ||||
| 	name: 'UlohaVzorakNode', | ||||
| 	data: () => { | ||||
| 		return { | ||||
| 			isResult: false, | ||||
| 			searchQuery: '', | ||||
| 			searchResults: [], | ||||
| 			showSelect: false, | ||||
| 			selected: null, | ||||
| 			selected_id: null | ||||
| 		} | ||||
| 	}, | ||||
| 	props: { | ||||
| 		item: Object, | ||||
| 		create: Boolean, | ||||
| 		showSelect: Boolean, | ||||
| 	}, | ||||
| 	mounted: function(){ | ||||
| 		if (this.item.node.uloha === null){ | ||||
| 			console.log("Uloha je null!"); | ||||
| 			console.log(this.item); | ||||
| 		} | ||||
| 		if (this.create){ | ||||
| 			this.showSelect = true; | ||||
| 			console.log('Creating'); | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		submitSearch: function(){ | ||||
| 			if (this.searchQuery.length < 3) { return;} | ||||
| 			var reqURL = "/api/ulohavzoraknode/?nazev="+this.searchQuery; | ||||
| 			axios.get(reqURL).then( (response) => { | ||||
| 				this.searchResults = response.data.results; | ||||
| 				this.isResult = true; | ||||
| 				console.log("Got:"); | ||||
| 				console.log(this.searchResults); | ||||
| 			}).catch( (err) => { /* fail response msg */ | ||||
| 				console.log(err); | ||||
| 			}); | ||||
| 		}, | ||||
| 		setSelected: function(res){ | ||||
| 			this.searchQuery = res.nazev | ||||
| 			this.selected_id = res.id | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .upravit { | ||||
| 	margin-top:-40px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										23
									
								
								vue_frontend/src/components/UlohaZadaniNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vue_frontend/src/components/UlohaZadaniNode.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| <template> | ||||
| 	<div class="ulohazadaninode"> | ||||
| 		<!--pre>UlohaZadaniNode {{item.node.uloha}} {{typeof(item)}}</pre--> | ||||
| 		<h5>Zadání {{item.node.uloha.cislo_zadani.poradi}}.{{ item.node.uloha.kod }}: {{ item.node.uloha.nazev }}</h5> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
| 	name: 'UlohaZadaniNode', | ||||
| 	props: { | ||||
| 		item: Object, | ||||
| 		created: Boolean | ||||
| 	, | ||||
| 	mounted: function(){ | ||||
| 		if (this.item.node.uloha === null){ | ||||
| 			console.log("Uloha je null!"); | ||||
| 			console.log(this.item); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										12
									
								
								vue_frontend/src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vue_frontend/src/main.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import Vue from 'vue' | ||||
| import App from './App.vue' | ||||
| import router from './router' | ||||
| import CKEditor from '@ckeditor/ckeditor5-vue' | ||||
| 
 | ||||
| Vue.config.productionTip = false | ||||
| Vue.use(CKEditor); | ||||
| 
 | ||||
| new Vue({ | ||||
| 	router, | ||||
| 	render: h => h(App), | ||||
| }).$mount('#app') | ||||
							
								
								
									
										28
									
								
								vue_frontend/src/router/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vue_frontend/src/router/index.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| import Vue from 'vue' | ||||
| import Router from 'vue-router' | ||||
| import TreeNodeRoot from '../components/TreeNodeRoot.vue' | ||||
| 
 | ||||
| Vue.use(Router) | ||||
| 
 | ||||
| export default new Router({ | ||||
| 	mode: 'history', | ||||
| 	linkActiveClass: 'active', | ||||
| 	routes: [{ | ||||
| 		path: '/temp/vue', | ||||
| 		name: 'treenodedefault', | ||||
| 		props: {'tnid': 23}, | ||||
| 		component: TreeNodeRoot | ||||
| 	}, { | ||||
| 		path: '/temp/vue/:tnid', | ||||
| 		name: 'treenode', | ||||
| 		props: true, | ||||
| 		component: TreeNodeRoot | ||||
| 	}, { | ||||
| 		path: '/zadani/aktualni', | ||||
| 		name: 'treenode_zadani', | ||||
| 		props: {'tnid': 23}, | ||||
| 		component: TreeNodeRoot | ||||
| 	}, | ||||
| 	] | ||||
| }) | ||||
| 
 | ||||
							
								
								
									
										61
									
								
								vue_frontend/vue.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vue_frontend/vue.config.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| const BundleTracker = require("webpack-bundle-tracker"); | ||||
| 
 | ||||
| const pages = { | ||||
|     'vue_app_01': { | ||||
|         entry: './src/main.js', | ||||
|         chunks: ['chunk-vendors'] | ||||
|     }, | ||||
| /*    'vue_app_02': { | ||||
|         entry: './src/newhampshir.js', | ||||
|         chunks: ['chunk-vendors'] | ||||
|     }, | ||||
| */ | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|     pages: pages, | ||||
|     filenameHashing: false, | ||||
|     productionSourceMap: false, | ||||
|     publicPath: process.env.NODE_ENV === 'production' | ||||
|         ? '/static/seminar/vue/' | ||||
|         : 'http://localhost:8080/', | ||||
|     outputDir: '../seminar/static/seminar/vue/', | ||||
| 
 | ||||
|     chainWebpack: config => { | ||||
| 
 | ||||
|         config.optimization | ||||
|             .splitChunks({ | ||||
|                 cacheGroups: { | ||||
|                     vendor: { | ||||
|                         test: /[\\/]node_modules[\\/]/, | ||||
|                         name: "chunk-vendors", | ||||
|                         chunks: "all", | ||||
|                         priority: 1 | ||||
|                     }, | ||||
|                 }, | ||||
|             }); | ||||
| 
 | ||||
|         Object.keys(pages).forEach(page => { | ||||
|             config.plugins.delete(`html-${page}`); | ||||
|             config.plugins.delete(`preload-${page}`); | ||||
|             config.plugins.delete(`prefetch-${page}`); | ||||
|         }) | ||||
| 
 | ||||
|         config | ||||
|             .plugin('BundleTracker') | ||||
|             .use(BundleTracker, [{filename: '../vue_frontend/webpack-stats.json'}]); | ||||
| 
 | ||||
|         config.resolve.alias | ||||
|             .set('__STATIC__', 'static') | ||||
| 
 | ||||
|         config.devServer | ||||
|             .public('http://localhost:8080') | ||||
|             .host('localhost') | ||||
|             .port(8080) | ||||
|             .hotOnly(true) | ||||
|             .watchOptions({poll: 1000}) | ||||
|             .https(false) | ||||
|             .headers({"Access-Control-Allow-Origin": ["*"]}) | ||||
| 
 | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										8544
									
								
								vue_frontend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8544
									
								
								vue_frontend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
		Reference in a new issue