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 | 	pip install --upgrade setuptools | ||||||
| 	# Instalace závislostí webu | 	# Instalace závislostí webu | ||||||
| 	pip install -r requirements.txt --upgrade | 	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: | install_venv: | ||||||
| 	${VENV} ${VENV_PATH} | 	${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) | ||||||
|  | 
 | ||||||
|  | @ -121,6 +121,9 @@ INSTALLED_APPS = ( | ||||||
| 
 | 
 | ||||||
|     'polymorphic', |     'polymorphic', | ||||||
|      |      | ||||||
|  |     'webpack_loader', | ||||||
|  |     'rest_framework', | ||||||
|  | 
 | ||||||
|     # MaMweb |     # MaMweb | ||||||
|     'mamweb', |     'mamweb', | ||||||
|     'seminar', |     'seminar', | ||||||
|  | @ -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 | # 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; | 	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 { | .org-logged-in .mam-text-plugin { | ||||||
|   border: dashed 1px #6a0043; |   border: dashed 1px #6a0043; | ||||||
|  | @ -286,7 +306,11 @@ ul.submenu li>a:hover { | ||||||
| 	color: black; | 	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; | 	color: black; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -367,13 +391,82 @@ input[type="file"] { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .field-with-comment:hover span.field-comment{ | .field-with-comment:hover span.field-comment{ | ||||||
|   display:block; |   display: block; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| input { | input { | ||||||
| 	margin: 5px; | 	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 |  * Zmenšování displeje | ||||||
| ***********************/ | ***********************/ | ||||||
|  | @ -403,42 +496,22 @@ input { | ||||||
| 		width: 100%; | 		width: 100%; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|   div.novinky{ | 	ul.menu { | ||||||
|     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%; | 		font-size: 90%; | ||||||
| 		margin-top: -7px; | 		margin-top: -7px; | ||||||
| } | 	} | ||||||
| 
 | 
 | ||||||
| ul.menu li { | 	ul.menu li { | ||||||
| 		margin-top: 10px; /* posunutí textu hlavního menu níže */ | 		margin-top: 10px; /* posunutí textu hlavního menu níže */ | ||||||
| } | 	} | ||||||
| 
 | 
 | ||||||
| ul.submenu li { | 	ul.submenu li { | ||||||
| 		margin-top: 0px; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ | 		margin-top: 0px; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ | ||||||
| } | 	} | ||||||
| 
 | 
 | ||||||
| ul.submenu { | 	ul.submenu { | ||||||
| 		margin-top: 8px; /* mezera mezi hlavním menu a submenu */ | 		margin-top: 8px; /* mezera mezi hlavním menu a submenu */ | ||||||
| } | 	} | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -470,11 +543,53 @@ ul.submenu { | ||||||
| 		display: inline-grid; | 		display: inline-grid; | ||||||
| 		max-width: 300px; | 		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 */ | /* malý tablet, mobil */ | ||||||
| @media (max-width: 650px) { | @media (max-width: 650px) { | ||||||
| 
 | 
 | ||||||
|  | 	.no-mobile{ | ||||||
|  | 		display: none; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|   #hide-if-small.login-bar-flatpage { |   #hide-if-small.login-bar-flatpage { | ||||||
| 		display: none; | 		display: none; | ||||||
| 	} | 	} | ||||||
|  | @ -611,17 +726,33 @@ ul.submenu { | ||||||
| 		text-align: justify; | 		text-align: justify; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|   div.novinky { | 	table.form td, table.form tr { | ||||||
|     max-width: 100%; |  		display: inherit; | ||||||
|     float: none; |  | ||||||
|  	} |  	} | ||||||
| 
 | 
 | ||||||
| 	div.graf { | 	 /* titulni stranka */ | ||||||
|  | 
 | ||||||
|  | 	.titulnistrana { | ||||||
|  | 		display: block; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	.graf { | ||||||
|  | 		padding-top: 40px; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	.titulnistrana_obsah { | ||||||
| 		width: 100%; | 		width: 100%; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	table.form td, table.form tr { | 	.vitej_titulka, .temata_titulka { | ||||||
|  		display: inherit; | 		width: 100%; | ||||||
|  | 		padding: 10px; | ||||||
|  | 		display: block; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	.titulnistrana_novinky { | ||||||
|  | 		width: 100%; | ||||||
|  | 		padding: 10px; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -704,11 +835,6 @@ div.org_email { | ||||||
|  backface-visibility: hidden; |  backface-visibility: hidden; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Style the front side (fallback if image is missing) */ |  | ||||||
| .flip-card-front { |  | ||||||
|  background-color: #bbb; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| div.flip-card-foto img { | div.flip-card-foto img { | ||||||
| 	width: 100%; | 	width: 100%; | ||||||
| 	height: 100%; | 	height: 100%; | ||||||
|  | @ -724,6 +850,10 @@ div.flip-card-foto img { | ||||||
|  padding-top: 20px; |  padding-top: 20px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #archiv.flip-card-back { | ||||||
|  | 	background-color: white; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* karty archiv */ | /* karty archiv */ | ||||||
| 
 | 
 | ||||||
| div.popis_rocniku { | 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 { | ul.form { | ||||||
| 	list-style-type: none; | 	list-style-type: none; | ||||||
| 	padding-left: 0px; | 	padding-left: 0px; | ||||||
|  | @ -984,3 +1076,20 @@ p.gdpr { | ||||||
| div.gdpr { | div.gdpr { | ||||||
| 	font-size: 6pt; | 	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> |     <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"> |     <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"> |     <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-theme.css' %}" rel="stylesheet"> | ||||||
|     <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> |     <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> | ||||||
|     <link href="{% static 'css/mamweb.css' %}" rel="stylesheet"> |     <link href="{% static 'css/mamweb.css' %}" rel="stylesheet"> | ||||||
|  | @ -56,14 +57,17 @@ | ||||||
| 		<div class='col-md-12'> | 		<div class='col-md-12'> | ||||||
|           <a href='/'> |           <a href='/'> | ||||||
| 	    <div id="title" >M&M - korespondenční seminář a časopis MFF UK</div> | 	    <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 #} |                 {# 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" %} | 		{% sitetree_menu from "main_menu" include "trunk" template "logo.html" %} | ||||||
|  |                 </div> | ||||||
| 		<img class="logo-mobile" src="{% static 'images/logo-mobile.svg' %}" /> | 		<img class="logo-mobile" src="{% static 'images/logo-mobile.svg' %}" /> | ||||||
|             </div> |             </div> | ||||||
|             </a> |             </a> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  | 
 | ||||||
|       <div class='row'> |       <div class='row'> | ||||||
| 	    <div class='col-md-12'> | 	    <div class='col-md-12'> | ||||||
| 
 | 
 | ||||||
|  | @ -73,6 +77,9 @@ | ||||||
| 
 | 
 | ||||||
| {# ========= MENU MOBILE ========== #} | {# ========= MENU MOBILE ========== #} | ||||||
| 
 | 
 | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
| <!--Navbar--> | <!--Navbar--> | ||||||
| <nav class="nav-button"> | <nav class="nav-button"> | ||||||
| 
 | 
 | ||||||
|  | @ -95,13 +102,15 @@ | ||||||
| 
 | 
 | ||||||
| {# ========= END MENU ========== #} | {# ========= END MENU ========== #} | ||||||
| 
 | 
 | ||||||
| 
 |     <div class='row'> | ||||||
| 	<div class='row content'> | 	<div class='row content'> | ||||||
| 	<div class='col-md-12'> | 	<div class='col-md-12'> | ||||||
| 	    {% block content %} | 	    {% block content %} | ||||||
| 	    {% endblock content %} | 	    {% endblock content %} | ||||||
| 	        </div> | 	        </div> | ||||||
|         </div> |         </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
| 	<div class='row'> | 	<div class='row'> | ||||||
| 	  <div class='col-md-12'> | 	  <div class='col-md-12'> | ||||||
| 		<div id="footer"> | 		<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> | 		<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> | 	</div> | ||||||
|  | 
 | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <script src="{% static 'js/bootstrap.js' %}"></script> |     <script src="{% static 'js/bootstrap.js' %}"></script> | ||||||
|  |  | ||||||
|  | @ -6,6 +6,8 @@ from django.views.generic.base import TemplateView | ||||||
| from django import views | from django import views | ||||||
| from django.urls import path # As per docs. | from django.urls import path # As per docs. | ||||||
| 
 | 
 | ||||||
|  | from .routers import router | ||||||
|  | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 
 | 
 | ||||||
| 	# Admin a nastroje | 	# Admin a nastroje | ||||||
|  | @ -25,6 +27,9 @@ urlpatterns = [ | ||||||
| 	path('comments_dj/', include('django_comments.urls')), | 	path('comments_dj/', include('django_comments.urls')), | ||||||
| 	path('comments_fl/', include('fluent_comments.urls')), | 	path('comments_fl/', include('fluent_comments.urls')), | ||||||
| 
 | 
 | ||||||
|  | 	# REST API | ||||||
|  | 	path('api/', include(router.urls)), | ||||||
|  | 
 | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| # This is only needed when using runserver. | # This is only needed when using runserver. | ||||||
|  |  | ||||||
|  | @ -28,6 +28,9 @@ django-imagekit | ||||||
| django-polymorphic | django-polymorphic | ||||||
| django-sitetree | django-sitetree | ||||||
| django_reverse_admin | django_reverse_admin | ||||||
|  | django-rest-framework | ||||||
|  | django-webpack-loader | ||||||
|  | django-rest-polymorphic | ||||||
| 
 | 
 | ||||||
| # Comments | # Comments | ||||||
| akismet==1.0.1 | akismet==1.0.1 | ||||||
|  |  | ||||||
|  | @ -107,6 +107,8 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin): | ||||||
| 		m.PohadkaNode, | 		m.PohadkaNode, | ||||||
| 		m.UlohaVzorakNode, | 		m.UlohaVzorakNode, | ||||||
| 		m.TextNode, | 		m.TextNode, | ||||||
|  | 		m.CastNode, | ||||||
|  | 		m.OrgTextNode, | ||||||
| 		] | 		] | ||||||
| 
 | 
 | ||||||
| 	actions = ['aktualizuj_nazvy'] | 	actions = ['aktualizuj_nazvy'] | ||||||
|  | @ -160,6 +162,17 @@ class TextNodeAdmin(PolymorphicChildModelAdmin): | ||||||
| 	base_model = m.TextNode | 	base_model = m.TextNode | ||||||
| 	show_in_index = True | 	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.Nastaveni, SingletonModelAdmin) | ||||||
| admin.site.register(m.Novinky) | 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 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 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 | from polymorphic.models import PolymorphicModel | ||||||
| 
 | 
 | ||||||
|  | @ -706,6 +705,7 @@ class Problem(SeminarModelBase,PolymorphicModel): | ||||||
| 		(STAV_SMAZANY, 'Smazaný'), | 		(STAV_SMAZANY, 'Smazaný'), | ||||||
| 		] | 		] | ||||||
| 	stav = models.CharField('stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH) | 	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í',  | 	zamereni = TaggableManager(verbose_name='zaměření',  | ||||||
| 		help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True) | 		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): | 	def verejne(self): | ||||||
| 		# FIXME: Tohle se liší podle typu problému, má se udělat polymorfně. | 		# 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é. | 		# 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 | 		return False | ||||||
| 		# FIXME: Tohle je blbost | 		# FIXME: Tohle je blbost | ||||||
| 		return (self.cislo_zadani and self.cislo_zadani.verejne()) | 		return (self.cislo_zadani and self.cislo_zadani.verejne()) | ||||||
|  | @ -843,9 +844,7 @@ class Text(SeminarModelBase): | ||||||
| 			tn.save() | 			tn.save() | ||||||
| 
 | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		parser = FirstTagParser()		 | 		return str(self.na_web)[:20] | ||||||
| 		parser.feed(str(self.na_web)) |  | ||||||
| 		return parser.firstTag |  | ||||||
| 		 | 		 | ||||||
| class Uloha(Problem): | class Uloha(Problem): | ||||||
| 	class Meta: | 	class Meta: | ||||||
|  | @ -1361,6 +1360,7 @@ class MezicisloNode(TreeNode): | ||||||
| 
 | 
 | ||||||
| 	# TODO: Využít TreeLib | 	# TODO: Využít TreeLib | ||||||
| 	def aktualizuj_nazev(self): | 	def aktualizuj_nazev(self): | ||||||
|  | 		from seminar.treelib import safe_pred | ||||||
| 		if safe_pred(self) is not None: | 		if safe_pred(self) is not None: | ||||||
| 			if (self.prev.get_real_instance_class() != CisloNode and | 			if (self.prev.get_real_instance_class() != CisloNode and | ||||||
| 				self.prev.get_real_instance_class() != MezicisloNode): | 				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 %} |     {% endblock %}{% endblock %} | ||||||
|   </h2> |   </h2> | ||||||
| 
 | 
 | ||||||
|   <!-- <div class='nahledy_cisel'> |  | ||||||
|     {% autoescape off %}{{ nahledy }}{% endautoescape %} |  | ||||||
|   </div>--> |  | ||||||
| 
 |  | ||||||
|     {% for rocnik, url_png in object_list.items %} |     {% for rocnik, url_png in object_list.items %} | ||||||
| 
 | 
 | ||||||
|     <div class="rocnik_pole"> |     <div class="rocnik_pole"> | ||||||
|  | @ -33,7 +29,7 @@ | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       </div> |       </div> | ||||||
|        <div class="flip-card-back"> |        <div class="flip-card-back" id="archiv"> | ||||||
|       <div class="popis_rocniku"> |       <div class="popis_rocniku"> | ||||||
|         Jednotlivá čísla: |         Jednotlivá čísla: | ||||||
|         <ul> |         <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 %} |   {% endif %} | ||||||
|   {% if novinka.zverejneno or user.je_org %} |   {% if novinka.zverejneno or user.je_org %} | ||||||
|   {# datum #} |   {# datum #} | ||||||
|   <div><b>{{novinka.datum}}</b></div> |   <div class=novinka_datum>{{novinka.datum}}</div> | ||||||
|   {# text #} |   {# text #} | ||||||
|   {{ novinka.text | safe }} |   {{ novinka.text | safe }} | ||||||
|   {# obrazek #} |   {# obrazek #} | ||||||
|  | @ -25,12 +25,12 @@ | ||||||
|     </div> |     </div> | ||||||
|   {% endif %} |   {% endif %} | ||||||
|   {# autor #} |   {# autor #} | ||||||
|   <div class=novinky_name><p>{{novinka.autor.first_name}} |   <div class=novinka_autor> | ||||||
|     {% if novinka.autor.organizator.prezdivka%} |     {{novinka.autor.osoba.jmeno}} | ||||||
|       „{{novinka.autor.organizator.prezdivka}}“ |     {% if novinka.autor.osoba.prezdivka%} | ||||||
|  |       „{{novinka.autor.osoba.prezdivka}}“ | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     {{novinka.autor.last_name}} |     {{novinka.autor.osoba.prijmeni}} | ||||||
|       </p> |  | ||||||
|   </div> |   </div> | ||||||
|   {% endif %} |   {% endif %} | ||||||
|   {% if not novinka.zverejneno and user.je_org %} |   {% 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 %} | {% 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> | <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 %} | {% endfor %} | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -5,32 +5,53 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% 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í. | {% if nejblizsi_deadline %} | ||||||
| </p> | <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á: | ||||||
| <div class="novinky"> |       {{nejblizsi_deadline|timeuntil}}</big></b></p> | ||||||
|   {% 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> |  | ||||||
| </div> | </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"> | ||||||
| 
 | 
 | ||||||
| <div class="graf-svg"> | <div class="graf-svg"> | ||||||
|  | @ -38,8 +59,24 @@ M&M je korespondenční seminář. Několikrát do roka zdarma vydáváme č | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
|   <span class="zjistit_vic"> |   <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> |   </span> | ||||||
| </div> | </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 %} | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -1,10 +1,12 @@ | ||||||
| {% extends "base.html" %} | {% extends "base.html" %} | ||||||
|  | {% load render_bundle from webpack_loader %} | ||||||
| 
 | 
 | ||||||
| {% load comments %} |  | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% 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 %} | {% 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 %} | {% load treenodes %} | ||||||
| {# <b>{{depth}}</b> #} | {# <b>{{depth}}</b> #} | ||||||
| <div> | <div class="borderized parent"> | ||||||
| {%   if obj.node|isRocnik %} | <div class="node_type"> | ||||||
| <h{{depth}}> Ročník {{obj.node.rocnik}} </h{{depth}}> | 	{{obj.node}} | ||||||
| {% elif obj.node|isCislo %} | 	{{obj.node.id}} | ||||||
| <h{{depth}}> Číslo {{obj.node.cislo}} </h{{depth}}> | 	{% if obj.node|deletable %} | ||||||
| {% elif obj.node|isTemaVCisle %} | 		<button type="submit" formaction="{%url 'treenode_smazat' obj.node.id%}">Smazat</button> | ||||||
| <h{{depth}}> Téma {{obj.node.tema.nazev}} </h{{depth}}> | 	{% endif %} | ||||||
| {% elif obj.node|isUlohaZadani %} | 	{% if obj.parent and  obj.parent|editableSiblings %} | ||||||
| <h{{depth}}>Úloha {{obj.node.uloha.kod_v_rocniku}} ({{obj.node.uloha.max_body}} b)</h{{depth}}> | 		<button type="submit" formaction="{%url 'treenode_odvesitpryc' obj.node.id%}">Odvěsit pryč ze stromu {{obj.parent.node}}</button> | ||||||
| {% elif obj.node|isUlohaVzorak %} | 	{% endif %} | ||||||
| <h{{depth}}>Řešení: {{obj.node.uloha.kod_v_rocniku}}</h{{depth}}> | 	{% if obj|canPodvesitPred %} | ||||||
| {% elif obj.node|isText %} | 		<button type="submit" formaction="{%url 'treenode_podvesit' obj.node.id 'pred'%}">Podvěsit pod předchozí</button> - nejsou testovací data | ||||||
| {{obj.node.text.na_web}} | 	{% endif %} | ||||||
| {% else %} | 	{% if obj|canPodvesitZa %} | ||||||
| Objekt jiného typu {{obj.node}} | 		<button type="submit" formaction="{%url 'treenode_podvesit' obj.node.id 'za'%}">Podvěsit pod následující</button> - nejsou testovací data | ||||||
| {% endif %} | 	{% endif %} | ||||||
|     {%if obj.children %} | 
 | ||||||
| 	<div> | 
 | ||||||
|          {%for ch in obj.children %} | </div> | ||||||
|               {%with obj=ch depth=depth|add:"1" template_name="seminar/treenode_recursive.html" %} | {% if False %} | ||||||
|                    {%include template_name%} | 	<div class="node_move"> | ||||||
|               {%endwith%} | 		FIXME: není zatím implementováno | ||||||
|          {%endfor%} | 		<button>Zvyš úroveň nadpisu</button> - nejsou testovací data | ||||||
| 	</div> | 	</div> | ||||||
|     {%endif%} | {% 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> | </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,8 +1,25 @@ | ||||||
| from django import template | from django import template | ||||||
|  | from enum import Enum | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
| 
 | 
 | ||||||
| register = template.Library() | 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 | @register.filter | ||||||
| def isRocnik(value): | def isRocnik(value): | ||||||
| 	return isinstance(value, m.RocnikNode) | 	return isinstance(value, m.RocnikNode) | ||||||
|  | @ -43,7 +60,184 @@ def isUlohaZadani(value): | ||||||
| def isPohadka(value): | def isPohadka(value): | ||||||
| 	return isinstance(value, m.PohadkaNode) | 	return isinstance(value, m.PohadkaNode) | ||||||
| 
 | 
 | ||||||
| #@register.filter | @register.filter | ||||||
| #def isOtisteneReseniNode(value): | def isReseni(value): | ||||||
|  | 	return False | ||||||
| #	return isinstance(value, m.OtisteneReseniNode) | #	return isinstance(value, m.OtisteneReseniNode) | ||||||
| 
 | 
 | ||||||
|  | @register.filter | ||||||
|  | def isOrgText(value): | ||||||
|  | 	return False | ||||||
|  | #	return isinstance(value, m.OrgTextNode) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ###  | ||||||
|  | 
 | ||||||
|  | #@register.filter | ||||||
|  | #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, | 		na_web = text, | ||||||
| 		do_cisla = text, | 		do_cisla = text, | ||||||
| 	) | 	) | ||||||
| 	zad = TextNode.objects.create(text = text_zadani) | 	zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) | ||||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad) | 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) | ||||||
| 	p.ulohazadaninode = uloha_zadani | 	p.ulohazadaninode = uloha_zadani | ||||||
| 	otec_syn(cisla[poradi_cisla-2-1].cislonode, 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, | 		na_web = obsah, | ||||||
| 		do_cisla = obsah | 		do_cisla = obsah | ||||||
| 	) | 	) | ||||||
| 	vzorak = TextNode.objects.create(text = text_vzoraku) | 	vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||||
| 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak) | 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||||
| 	uloha.ulohavzoraknode = uloha_vzorak | 	uloha.ulohavzoraknode = uloha_vzorak | ||||||
| 
 | 
 | ||||||
| 	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) | 	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) | ||||||
|  | @ -434,7 +434,7 @@ def gen_cisla(rnd, rocniky): | ||||||
| 				datum_deadline=deadline, | 				datum_deadline=deadline, | ||||||
| 				verejne_db=True | 				verejne_db=True | ||||||
| 			) | 			) | ||||||
| 			node2 = CisloNode.objects.create(cislo = cislo, succ = node) | 			node2 = CisloNode.objects.create(cislo = cislo, succ = node, root=rocnik.rocniknode) | ||||||
| 			cislo.save() | 			cislo.save() | ||||||
| 			node = node2 | 			node = node2 | ||||||
| 			if otec: | 			if otec: | ||||||
|  | @ -473,54 +473,54 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | ||||||
| 	for cislo in cisla: | 	for cislo in cisla: | ||||||
| 		# Přidáme TemaVCisleNode do daného čísla | 		# Přidáme TemaVCisleNode do daného čísla | ||||||
| 		cislo_node = cislo.cislonode	 | 		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) | 		insert_last_child(cislo_node, tema_cislo_node) | ||||||
| 		 | 		 | ||||||
| 		# Přidávání obsahu do čísla | 		# 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) | 		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) | 		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) | 		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) | 		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) | 		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) | 		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)	 | 		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) | 		add_first_child(cast_node3, text_node4) | ||||||
| 		 | 		 | ||||||
| 		cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " | 		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.succ = cast_node3a | ||||||
| 		cast_node3.save() | 		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) | 		add_first_child(cast_node3a, text_node3a) | ||||||
| 
 | 
 | ||||||
| 		# Občas přidáme mezičíslo | 		# Občas přidáme mezičíslo | ||||||
| 		if rnd.randint(1, 3) == 1: | 		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 | 			mezicislo_node = cislo_node.succ | ||||||
| 	 | 	 | ||||||
| 			cast_node_mezicislo = m.CastNode.objects.create( | 			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) | 			add_first_child(mezicislo_node, cast_node_mezicislo) | ||||||
| 
 | 
 | ||||||
| 			odstavec = lorem.paragraph() | 			odstavec = lorem.paragraph() | ||||||
| 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) | 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) | ||||||
| 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo) | 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) | ||||||
| 			add_first_child(cast_node_mezicislo, text_node_mezicislo) | 			add_first_child(cast_node_mezicislo, text_node_mezicislo) | ||||||
| 
 | 
 | ||||||
| 	return tema | 	return tema | ||||||
|  | @ -564,7 +564,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | ||||||
| 
 | 
 | ||||||
| 			# Vyrobíme TemaVCisleNody pro obsah | 			# Vyrobíme TemaVCisleNody pro obsah | ||||||
| 			for i in range(zacatek_tematu, konec_tematu+1): | 			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? | 				# FIXME: Není to off-by-one? | ||||||
| 				otec = cisla[i-1].cislonode | 				otec = cisla[i-1].cislonode | ||||||
| 				otec_syn(otec, node) | 				otec_syn(otec, node) | ||||||
|  | @ -621,8 +621,8 @@ def gen_ulohy_tematu(rnd, organizatori, tema, kod, cislo, cislo_se_vzorakem): | ||||||
| 		na_web = obsah, | 		na_web = obsah, | ||||||
| 		do_cisla = obsah, | 		do_cisla = obsah, | ||||||
| 		) | 		) | ||||||
| 	zad = TextNode.objects.create(text = text_zadani) | 	zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) | ||||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad) | 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) | ||||||
| 	uloha.ulohazadaninode = uloha_zadani | 	uloha.ulohazadaninode = uloha_zadani | ||||||
| 
 | 
 | ||||||
| 	return uloha, uloha_zadani | 	return uloha, uloha_zadani | ||||||
|  |  | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| from django.core.exceptions import ObjectDoesNotExist | 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 | # 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: 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: 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. | # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. | ||||||
| def print_tree(node,indent=0): | def print_tree(node,indent=0): | ||||||
|  | @ -12,20 +14,31 @@ def print_tree(node,indent=0): | ||||||
| 	if node.succ: | 	if node.succ: | ||||||
| 		print_tree(node.succ, indent=indent) | 		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 | # Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist | ||||||
| def safe_pred(node): | def safe_pred(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	try: | 	try: | ||||||
| 		return node.prev | 		return node.prev | ||||||
| 	except ObjectDoesNotExist: | 	except ObjectDoesNotExist: | ||||||
| 		return None | 		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): | def first_brother(node): | ||||||
| 	if node is None: | 	if node is None: | ||||||
| 		return None | 		return None | ||||||
|  | @ -34,15 +47,8 @@ def first_brother(node): | ||||||
| 		brother = safe_pred(brother) | 		brother = safe_pred(brother) | ||||||
| 	return 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 | ## Rodinné vztahy | ||||||
|  | # Tohle se teď zrovna k None chová správně, ale je potřeba na to dávat pozor | ||||||
| def get_parent(node): | def get_parent(node): | ||||||
| 	# Nejdřív získáme prvního potomka... | 	# Nejdřív získáme prvního potomka... | ||||||
| 	while safe_pred(node) is not None: | 	while safe_pred(node) is not None: | ||||||
|  | @ -51,14 +57,33 @@ def get_parent(node): | ||||||
| 	return safe_father_of_first(node) | 	return safe_father_of_first(node) | ||||||
| 
 | 
 | ||||||
| def get_last_child(node): | def get_last_child(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	first = node.first_child | 	first = node.first_child | ||||||
| 	if first is None: | 	if first is None: | ||||||
| 		return None | 		return None | ||||||
| 	else: | 	else: | ||||||
| 		return last_brother(first) | 		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é) | # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) | ||||||
| def general_next(node): | def general_next(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	# Máme potomka? | 	# Máme potomka? | ||||||
| 	if node.first_child is not None: | 	if node.first_child is not None: | ||||||
| 		return node.first_child | 		return node.first_child | ||||||
|  | @ -71,6 +96,8 @@ def general_next(node): | ||||||
| 	return node.succ | 	return node.succ | ||||||
| 
 | 
 | ||||||
| def last_brother(node): | def last_brother(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	while node.succ is not None: | 	while node.succ is not None: | ||||||
| 		node = node.succ | 		node = node.succ | ||||||
| 	return node | 	return node | ||||||
|  | @ -78,6 +105,7 @@ def last_brother(node): | ||||||
| def general_prev(node): | def general_prev(node): | ||||||
| 	# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. | 	# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. | ||||||
| 	# Otce vyřešíme nejdřív: | 	# Otce vyřešíme nejdřív: | ||||||
|  | 	# Tady se ošetří node=None samo | ||||||
| 	if safe_pred(node) is None: | 	if safe_pred(node) is None: | ||||||
| 		return safe_father_of_first(node) | 		return safe_father_of_first(node) | ||||||
| 	pred = safe_pred(node) | 	pred = safe_pred(node) | ||||||
|  | @ -95,12 +123,16 @@ def me_and_right_brothers(node): | ||||||
| 		current = current.succ | 		current = current.succ | ||||||
| 
 | 
 | ||||||
| def right_brothers(node): | def right_brothers(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	generator = me_and_right_brothers(node.succ) | 	generator = me_and_right_brothers(node.succ) | ||||||
| 	for item in generator: | 	for item in generator: | ||||||
| 		yield item | 		yield item | ||||||
| 
 | 
 | ||||||
| # Generátor všech sourozenců (vč. sám sebe) | # Generátor všech sourozenců (vč. sám sebe) | ||||||
| def all_brothers(node): | def all_brothers(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	# Najdeme prvního bratra | 	# Najdeme prvního bratra | ||||||
| 	fb = first_brother(node) | 	fb = first_brother(node) | ||||||
| 	marb = me_and_right_brothers(fb) | 	marb = me_and_right_brothers(fb) | ||||||
|  | @ -108,6 +140,8 @@ def all_brothers(node): | ||||||
| 		yield cur | 		yield cur | ||||||
| 	 | 	 | ||||||
| def all_proper_brothers(node): | def all_proper_brothers(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	all = all_brothers(node) | 	all = all_brothers(node) | ||||||
| 	for br in all: | 	for br in all: | ||||||
| 		if br is node: | 		if br is node: | ||||||
|  | @ -116,12 +150,16 @@ def all_proper_brothers(node): | ||||||
| 
 | 
 | ||||||
| def all_children(node): | def all_children(node): | ||||||
| 	""" Generátor všech potomků zadaného Node. """ | 	""" Generátor všech potomků zadaného Node. """ | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	brothers = all_brothers(node.first_child) | 	brothers = all_brothers(node.first_child) | ||||||
| 	for br in brothers: | 	for br in brothers: | ||||||
| 		yield br | 		yield br | ||||||
| 
 | 
 | ||||||
| def all_children_of_type(node, type): | def all_children_of_type(node, type): | ||||||
| 	""" Generuje všechny potomky daného Node a daného typu. """ | 	""" Generuje všechny potomky daného Node a daného typu. """ | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	brothers = all_brothers(node.first_child) | 	brothers = all_brothers(node.first_child) | ||||||
| 	for br in brothers: | 	for br in brothers: | ||||||
| 		if isinstance(br, type): | 		if isinstance(br, type): | ||||||
|  | @ -130,6 +168,8 @@ def all_children_of_type(node, type): | ||||||
| # Generátor následníků v "the-right-order" | # Generátor následníků v "the-right-order" | ||||||
| # Bez tohoto vrcholu | # Bez tohoto vrcholu | ||||||
| def all_following(node): | def all_following(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	current = general_next(node) | 	current = general_next(node) | ||||||
| 	while current is not None: | 	while current is not None: | ||||||
| 		yield current | 		yield current | ||||||
|  | @ -139,12 +179,16 @@ def all_following(node): | ||||||
| # Najdi dalšího bratra nějakého typu, nebo None. | # 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. | # hledá i podtřídy, i.e. get_next_brother_of_type(neco, TreeNode) je prostě succ. | ||||||
| def get_next_brother_of_type(node, type): | def get_next_brother_of_type(node, type): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	for current in right_brothers(node): | 	for current in right_brothers(node): | ||||||
| 		if isinstance(current, type): | 		if isinstance(current, type): | ||||||
| 			return current | 			return current | ||||||
| 	return None | 	return None | ||||||
| 	 | 	 | ||||||
| def get_prev_brother_of_type(node, type): | 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. | 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||||
| 	current = node | 	current = node | ||||||
| 	while safe_pred(current) is not None: | 	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í | # Totéž pro "the-right-order" pořadí | ||||||
| def get_next_node_of_type(node, type): | def get_next_node_of_type(node, type): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	for cur in all_folowing(node): | 	for cur in all_folowing(node): | ||||||
| 		if isinstance(cur, type): | 		if isinstance(cur, type): | ||||||
| 			return cur | 			return cur | ||||||
|  | @ -162,6 +208,8 @@ def get_next_node_of_type(node, type): | ||||||
| 
 | 
 | ||||||
| def get_prev_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. | 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	current = node | 	current = node | ||||||
| 	while general_prev(current) is not None: | 	while general_prev(current) is not None: | ||||||
| 		current = general_prev(current) | 		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: | # Editace stromu: | ||||||
| def create_node_after(predecessor, type, **kwargs): | 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 = type.objects.create(**kwargs) | ||||||
|  | 	new_node.root = predecessor.root | ||||||
| 	new_node.save() | 	new_node.save() | ||||||
| 	succ = predecessor.succ | 	succ = predecessor.succ | ||||||
| 	predecessor.succ = new_node | 	predecessor.succ = new_node | ||||||
| 	predecessor.save() | 	predecessor.save() | ||||||
| 	new_node.succ = succ | 	new_node.succ = succ | ||||||
| 	new_node.save() | 	new_node.save() | ||||||
|  | 	return new_node | ||||||
| 
 | 
 | ||||||
| # Vyrábí prvního syna, ostatní nalepí za (existují-li) | # Vyrábí prvního syna, ostatní nalepí za (existují-li) | ||||||
| def create_child(parent, type, **kwargs): | 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 = type.objects.create(**kwargs) | ||||||
|  | 	new_node.root = parent.root | ||||||
| 	new_node.save() | 	new_node.save() | ||||||
| 	orig_child = parent.first_child | 	orig_child = parent.first_child | ||||||
| 	parent.first_child = new_node | 	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 | 		# Přidáme původního prvního syna jako potomka nového vrcholu | ||||||
| 		new_node.succ = orig_child | 		new_node.succ = orig_child | ||||||
| 		new_node.save() | 		new_node.save() | ||||||
|  | 	return new_node | ||||||
| 
 | 
 | ||||||
| def insert_last_child(parent, 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. """ | 	""" Zadaný Node přidá jako posledního potomka otce. """ | ||||||
| 	last = get_last_child(parent) | 	last = get_last_child(parent) | ||||||
| 	if not is_orphan(node): | 	if not is_orphan(node): | ||||||
|  | @ -213,6 +282,11 @@ def insert_last_child(parent, node): | ||||||
| 		last.save() | 		last.save() | ||||||
| 
 | 
 | ||||||
| def create_node_before(successor, type, **kwargs): | 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: | 	if safe_pred(successor) is not None: | ||||||
| 		# Easy: přidáme za předchůdce | 		# Easy: přidáme za předchůdce | ||||||
| 		create_node_after(successor.prev, type, **kwargs) | 		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) | 		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 | 	# 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 = type.objects.create(**kwargs) | ||||||
|  | 	new.root = successor.root | ||||||
| 	new.succ = successor | 	new.succ = successor | ||||||
| 	new.save() | 	new.save() | ||||||
|  | 	return new | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # ValueError, pokud je (aspoň) jeden parametr None | # ValueError, pokud je (aspoň) jeden parametr None | ||||||
| def swap(node, other): | def swap(node, other): | ||||||
| 	raise NotImplementedError("YAGNI (You aren't gonna need it).") | 	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): | def swap_succ(node): | ||||||
| 	if node is None: | 	if node is None: | ||||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | 		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 | 	post_succ = succ.succ | ||||||
| 
 | 
 | ||||||
| 	if pred is not None: | 	if pred is not None: | ||||||
| 		pred.succ = succ | 		pred.succ = None | ||||||
| 		pred.save() | 		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.succ = node | ||||||
| 	succ.save() | 	succ.save() | ||||||
| 	node.succ = post_succ | 	node.succ = post_succ | ||||||
| 	node.save() | 	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 | # Rotace stromu | ||||||
| # Dokumentace viz wiki: | # Dokumentace viz wiki: | ||||||
| # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) |  | ||||||
| def raise_node(node): | def raise_node(node): | ||||||
| 	if node is None: | 	if node is None: | ||||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | 		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) | 	# 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 | 	D = node | ||||||
| 	C = get_parent(D) | 	C = get_parent(D) | ||||||
| 	E = C.succ | 	if C is None: | ||||||
| 	subtree4_head = D.first_child | 		raise TreeLibError("Nelze povýšit vrchol, jenž nemá otce.") | ||||||
| 	subtree4_tail = last_brother(subtree4_head) | 	E = C.succ	# Může být None a ničemu to nevadí | ||||||
| 	subtree3P_head = D.succ | 	subtree4_head = D.first_child	# Může být None, ale pak se musí z 3P udělat přímo potomek D | ||||||
| 	subtree3L_head = C.first_child | 	subtree4_tail = last_brother(subtree4_head)	# Měl by být None právě když je sub4_head=None | ||||||
| 	subtree3L_tail = safe_pred(D) | 	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... | 	# Prostor pro motlitbu... | ||||||
| 	pass | 	pass | ||||||
| 	 | 	 | ||||||
| 	# Amen. | 	# 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() | 	C.save() | ||||||
| 	D.succ = E | 	D.succ = E	# Nespadne | ||||||
| 	D.save() | 	D.save() | ||||||
|  | 
 | ||||||
|  | 	if subtree3L_tail is not None: | ||||||
| 		subtree3L_tail.succ = None | 		subtree3L_tail.succ = None | ||||||
| 		subtree3L_tail.save() | 		subtree3L_tail.save() | ||||||
| 	subtree4_tail.succ = subtree3P.head | 	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() | 		subtree4_tail.save() | ||||||
|  | 	else: | ||||||
|  | 		D.first_child = subtree3P_head | ||||||
|  | 		D.save() | ||||||
| 
 | 
 | ||||||
| 	# To by mělo být všechno... | 	# 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): | def lower_node(node): | ||||||
| 	if node is None: | 	if node is None: | ||||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | 		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) | 	# 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 | 	C = node | ||||||
| 	D = C.succ | 	D = C.succ	# Může být None a ničemu to nevadí | ||||||
| 	B = safe_pred(C) | 	B = safe_pred(C) | ||||||
| 	subtree2_head = B.first_child | 	if B is None: | ||||||
| 	subtree2_tail = last_brother(subtree2_head) | 		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... | 	# Prostor pro motlitbu... | ||||||
| 	pass | 	pass | ||||||
| 	 | 	 | ||||||
| 	# Amen. | 	# 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() | 	B.save() | ||||||
|  | 	if subtree2_tail is not None: | ||||||
| 		subtree2_tail.succ = C | 		subtree2_tail.succ = C | ||||||
| 		subtree2_tail.save() | 		subtree2_tail.save() | ||||||
|  | 	else: | ||||||
|  | 		assert subtree2_head is None | ||||||
|  | 		B.first_child = C | ||||||
|  | 		B.save() | ||||||
| 
 | 
 | ||||||
| 	# To by mělo být všechno... | 	# 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 django.contrib.auth.decorators import login_required | ||||||
| from . import views, export | from . import views, export | ||||||
| from .utils import org_required, resitel_required | from .utils import org_required, resitel_required | ||||||
|  | @ -17,9 +17,17 @@ urlpatterns = [ | ||||||
| 	path('archiv/temata/', views.ArchivTemataView.as_view()), | 	path('archiv/temata/', views.ArchivTemataView.as_view()), | ||||||
| 
 | 
 | ||||||
| 	path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'), | 	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('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>/', 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'), | 	#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), | ||||||
| 
 | 
 | ||||||
| 	# Soustredeni | 	# Soustredeni | ||||||
|  | @ -50,7 +58,7 @@ urlpatterns = [ | ||||||
| 
 | 
 | ||||||
| 	# Zadani | 	# Zadani | ||||||
| 	path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_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('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), | ||||||
| 	path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), | 	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/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'), | 	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('', views.TitulniStranaView.as_view(), name='titulni_strana'), | ||||||
|  | 	path('jak-resit/', views.JakResitView.as_view(), name='jak-resit'), | ||||||
| 
 | 
 | ||||||
| 	# Ceka na autocomplete v3 | 	# Ceka na autocomplete v3 | ||||||
| 	# path('autocomplete/organizatori/', | 	# path('autocomplete/organizatori/', | ||||||
|  |  | ||||||
|  | @ -1,2 +1,3 @@ | ||||||
| from .views_all import * | from .views_all import * | ||||||
| from .autocomplete 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.models import User, Permission | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
| from django.db import transaction | 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 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 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 .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||||
| from seminar import utils, treelib | from seminar import utils, treelib | ||||||
| from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | ||||||
| import seminar.forms as f | 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 django.utils import timezone | ||||||
| from itertools import groupby | from itertools import groupby | ||||||
| from collections import OrderedDict | from collections import OrderedDict | ||||||
|  | @ -44,14 +49,15 @@ import time | ||||||
| 
 | 
 | ||||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | from seminar.utils import aktivniResitele, resi_v_rocniku | ||||||
| 
 | 
 | ||||||
| 
 | # ze starého modelu | ||||||
| def verejna_temata(rocnik): | #def verejna_temata(rocnik): | ||||||
| 	"""Vrací queryset zveřejněných témat v daném ročníku. | #	""" | ||||||
| 	""" | #	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') | #	""" | ||||||
| 
 | #	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 temata_v_rocniku(rocnik): | ||||||
|  | #	return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) | ||||||
| 
 | 
 | ||||||
| def get_problemy_k_tematu(tema): | def get_problemy_k_tematu(tema): | ||||||
| 	return Problem.objects.filter(nadproblem = tema) | 	return Problem.objects.filter(nadproblem = tema) | ||||||
|  | @ -88,39 +94,279 @@ class ObalkovaniView(generic.ListView): | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
| class TNLData(object): | class TNLData(object): | ||||||
| 	def __init__(self,anode): | 	def __init__(self,anode,parent=None, index=None): | ||||||
| 		self.node = anode | 		self.node = anode | ||||||
|  | 		self.sernode = vr.TreeNodeSerializer(anode) | ||||||
| 		self.children = [] | 		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) | 	@classmethod | ||||||
| 	for ch in treelib.all_children(node): | 	def from_treenode(cls,anode,parent=None,index=None): | ||||||
| 		outitem = treenode_strom_na_seznamy(ch) | 		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.children.append(outitem) | ||||||
|  | 		out.add_edit_options() | ||||||
| 		return out | 		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): | class TreeNodeView(generic.DetailView): | ||||||
| 	model = s.TreeNode | 	model = s.TreeNode | ||||||
| 	template_name = 'seminar/treenode.html' | 	template_name = 'seminar/treenode.html' | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self,**kwargs): | 	def get_context_data(self,**kwargs): | ||||||
| 		context = super().get_context_data(**kwargs) | 		context = super().get_context_data(**kwargs) | ||||||
| 		context['tnldata'] = treenode_strom_na_seznamy(self.object) | 		context['tnldata'] = TNLData.from_treenode(self.object) | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  | 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) | ||||||
|  | 		# 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... | # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | ||||||
| class AktualniZadaniView(TreeNodeView): | #class AktualniZadaniView(TreeNodeView): | ||||||
| 	def get_object(self): | #	def get_object(self): | ||||||
| 		nastaveni = get_object_or_404(Nastaveni) | #		nastaveni = get_object_or_404(Nastaveni) | ||||||
| 		return nastaveni.aktualni_cislo.cislonode | #		return nastaveni.aktualni_cislo.cislonode | ||||||
| 
 | # | ||||||
| 	def get_context_data(self,**kwargs): | #	def get_context_data(self,**kwargs): | ||||||
| 		nastaveni = get_object_or_404(Nastaveni) | #		nastaveni = get_object_or_404(Nastaveni) | ||||||
| 		context = super().get_context_data(**kwargs) | #		context = super().get_context_data(**kwargs) | ||||||
| 		verejne = nastaveni.aktualni_cislo.verejne() | #		verejne = nastaveni.aktualni_cislo.verejne() | ||||||
| 		context['verejne'] = verejne	 | #		context['verejne'] = verejne	 | ||||||
| 		return context | #		return context | ||||||
| 
 | 
 | ||||||
| #def AktualniZadaniView(request): | #def AktualniZadaniView(request): | ||||||
| #	nastaveni = get_object_or_404(Nastaveni) | #	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | @ -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) | #	nastaveni = get_object_or_404(Nastaveni) | ||||||
| #	temata = verejna_temata(nastaveni.aktualni_rocnik) | #	temata = verejna_temata(nastaveni.aktualni_rocnik) | ||||||
| #	for t in temata: | #	for t in temata: | ||||||
|  | @ -242,45 +500,51 @@ def spravne_novinky(request): | ||||||
| 		qs = qs.filter(zverejneno=True) | 		qs = qs.filter(zverejneno=True) | ||||||
| 	return qs.order_by('-datum') | 	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): | class TitulniStranaView(generic.ListView): | ||||||
| 	template_name='seminar/titulnistrana.html' | 	template_name='seminar/titulnistrana.html' | ||||||
| 
 | 
 | ||||||
| 	def get_queryset(self): | 	def get_queryset(self): | ||||||
| 		return spravne_novinky(self.request)[:5] | 		return spravne_novinky(self.request)[:3] | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, **kwargs): | 	def get_context_data(self, **kwargs): | ||||||
| 		context = super(TitulniStranaView, self).get_context_data(**kwargs) | 		context = super(TitulniStranaView, self).get_context_data(**kwargs) | ||||||
| 		nastaveni = get_object_or_404(Nastaveni) | 		nastaveni = get_object_or_404(Nastaveni) | ||||||
| 		 | 		 | ||||||
| 		# zjisteni spravneho terminu | 		deadline_soustredeni = (nastaveni.aktualni_cislo.datum_deadline_soustredeni, "soustredeni") | ||||||
| 		if nastaveni.aktualni_cislo.datum_deadline_soustredeni: | 		preddeadline = (nastaveni.aktualni_cislo.datum_preddeadline, "preddeadline") | ||||||
| 			cas_deadline_soustredeni = nastaveni.aktualni_cislo.\ | 		deadline = (nastaveni.aktualni_cislo.datum_deadline, "deadline") | ||||||
| 				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 |  | ||||||
| 
 | 
 | ||||||
| 		# 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: | 		try: | ||||||
| 				context['dead'] = datetime.combine(cas_deadline, | 			nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0] | ||||||
| 												 datetime.max.time()) | 		except IndexError: | ||||||
| 				context['ted'] = datetime.now() | 			nejblizsi_deadline = (None, None) # neni zadna aktualni deadline | ||||||
| 			except: | 
 | ||||||
| 				context['dead'] = None | 		if nejblizsi_deadline[0] is not None: | ||||||
|  | 			context['nejblizsi_deadline'] = datetime.combine(nejblizsi_deadline[0], datetime.max.time()) | ||||||
| 		else: | 		else: | ||||||
| 			context['dead'] = None | 			context['nejblizsi_deadline'] = None | ||||||
| 			context['deadline_soustredeni'] = deadline_soustredeni | 		 | ||||||
|  | 		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 | 		return context | ||||||
| 
 | 
 | ||||||
| class StareNovinkyView(generic.ListView): | class StareNovinkyView(generic.ListView): | ||||||
|  | @ -343,7 +607,8 @@ class ArchivView(generic.ListView): | ||||||
| ### Výsledky | ### Výsledky | ||||||
| 
 | 
 | ||||||
| def sloupec_s_poradim(setrizene_body): | 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.), | 	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. | 	podle toho, jak jdou za sebou ve výsledkovce. | ||||||
| 	Parametr: | 	Parametr: | ||||||
|  | @ -383,7 +648,8 @@ def sloupec_s_poradim(setrizene_body): | ||||||
| 	return sloupec_s_poradim | 	return sloupec_s_poradim | ||||||
| 
 | 
 | ||||||
| def cisla_rocniku(rocnik, jen_verejne=True): | 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: | 	Parametry: | ||||||
| 		rocnik (Rocnik): ročník semináře | 		rocnik (Rocnik): ročník semináře | ||||||
| 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | ||||||
|  | @ -773,6 +1039,7 @@ def vysledkovka_cisla(cislo, context=None): | ||||||
| 	return context | 	return context | ||||||
| 
 | 
 | ||||||
| class CisloView(generic.DetailView): | class CisloView(generic.DetailView): | ||||||
|  | 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | ||||||
| 	model = Cislo | 	model = Cislo | ||||||
| 	template_name = 'seminar/archiv/cislo.html' | 	template_name = 'seminar/archiv/cislo.html' | ||||||
| 
 | 
 | ||||||
|  | @ -1308,6 +1575,32 @@ class PasswordChangeView(auth_views.PasswordChangeView): | ||||||
| 	#template_name = 'seminar/password_change.html' | 	#template_name = 'seminar/password_change.html' | ||||||
| 	success_url = reverse_lazy('titulni_strana') | 	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 | # Jen hloupé rozhazovátko | ||||||
| def profilView(request): | def profilView(request): | ||||||
|  | @ -1332,3 +1625,10 @@ def formularOKView(request): | ||||||
| 	} | 	} | ||||||
| 	return render(request, template_name, context) | 	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