Merge branch 'data_migrations' into test
This commit is contained in:
		
						commit
						e15425d60a
					
				
					 43 changed files with 5016 additions and 1903 deletions
				
			
		
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -34,8 +34,8 @@ install_web: venv_check | |||
| 	pip install --upgrade setuptools | ||||
| 	# Instalace závislostí webu | ||||
| 	pip install -r requirements.txt --upgrade | ||||
| 	# Po vygenerování testdat spusť  ./manage.py loaddata sitetree_new.json, ať máš menu | ||||
| 	# Pro synchronizaci flatpages spusť make sync_prod_flatpages | ||||
| 	# Po vygenerování testdat spusť  ./manage.py loaddata data/*, ať máš menu a další modely | ||||
| :x | ||||
| 
 | ||||
| install_venv: | ||||
| 	${VENV} ${VENV_PATH} | ||||
|  |  | |||
							
								
								
									
										249
									
								
								data/auth_groups.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								data/auth_groups.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | |||
| [ | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"name": "org", | ||||
| 			"permissions": [ | ||||
| 				23, | ||||
| 				24, | ||||
| 				25, | ||||
| 				1, | ||||
| 				26, | ||||
| 				72, | ||||
| 				73, | ||||
| 				74, | ||||
| 				75, | ||||
| 				76, | ||||
| 				77, | ||||
| 				78, | ||||
| 				79, | ||||
| 				51, | ||||
| 				55, | ||||
| 				52, | ||||
| 				53, | ||||
| 				54, | ||||
| 				56, | ||||
| 				57, | ||||
| 				58, | ||||
| 				59, | ||||
| 				60, | ||||
| 				61, | ||||
| 				62, | ||||
| 				63, | ||||
| 				43, | ||||
| 				44, | ||||
| 				45, | ||||
| 				46, | ||||
| 				228, | ||||
| 				229, | ||||
| 				230, | ||||
| 				231, | ||||
| 				232, | ||||
| 				233, | ||||
| 				234, | ||||
| 				235, | ||||
| 				260, | ||||
| 				261, | ||||
| 				262, | ||||
| 				263, | ||||
| 				264, | ||||
| 				265, | ||||
| 				266, | ||||
| 				267, | ||||
| 				244, | ||||
| 				245, | ||||
| 				246, | ||||
| 				247, | ||||
| 				236, | ||||
| 				237, | ||||
| 				238, | ||||
| 				239, | ||||
| 				240, | ||||
| 				241, | ||||
| 				242, | ||||
| 				243, | ||||
| 				248, | ||||
| 				249, | ||||
| 				250, | ||||
| 				251, | ||||
| 				252, | ||||
| 				253, | ||||
| 				254, | ||||
| 				255, | ||||
| 				256, | ||||
| 				257, | ||||
| 				258, | ||||
| 				259, | ||||
| 				212, | ||||
| 				213, | ||||
| 				214, | ||||
| 				215, | ||||
| 				80, | ||||
| 				81, | ||||
| 				82, | ||||
| 				83, | ||||
| 				180, | ||||
| 				181, | ||||
| 				182, | ||||
| 				183, | ||||
| 				172, | ||||
| 				173, | ||||
| 				174, | ||||
| 				175, | ||||
| 				168, | ||||
| 				169, | ||||
| 				170, | ||||
| 				171, | ||||
| 				132, | ||||
| 				133, | ||||
| 				134, | ||||
| 				135, | ||||
| 				224, | ||||
| 				225, | ||||
| 				226, | ||||
| 				227, | ||||
| 				184, | ||||
| 				185, | ||||
| 				186, | ||||
| 				187, | ||||
| 				112, | ||||
| 				113, | ||||
| 				114, | ||||
| 				115, | ||||
| 				120, | ||||
| 				121, | ||||
| 				122, | ||||
| 				123, | ||||
| 				164, | ||||
| 				165, | ||||
| 				166, | ||||
| 				167, | ||||
| 				124, | ||||
| 				125, | ||||
| 				126, | ||||
| 				127, | ||||
| 				216, | ||||
| 				217, | ||||
| 				218, | ||||
| 				219, | ||||
| 				136, | ||||
| 				137, | ||||
| 				138, | ||||
| 				139, | ||||
| 				152, | ||||
| 				153, | ||||
| 				154, | ||||
| 				155, | ||||
| 				208, | ||||
| 				209, | ||||
| 				210, | ||||
| 				211, | ||||
| 				140, | ||||
| 				141, | ||||
| 				142, | ||||
| 				143, | ||||
| 				108, | ||||
| 				109, | ||||
| 				110, | ||||
| 				111, | ||||
| 				84, | ||||
| 				85, | ||||
| 				86, | ||||
| 				87, | ||||
| 				104, | ||||
| 				105, | ||||
| 				106, | ||||
| 				107, | ||||
| 				160, | ||||
| 				161, | ||||
| 				162, | ||||
| 				163, | ||||
| 				220, | ||||
| 				221, | ||||
| 				222, | ||||
| 				223, | ||||
| 				88, | ||||
| 				89, | ||||
| 				90, | ||||
| 				91, | ||||
| 				92, | ||||
| 				93, | ||||
| 				94, | ||||
| 				95, | ||||
| 				188, | ||||
| 				189, | ||||
| 				190, | ||||
| 				191, | ||||
| 				96, | ||||
| 				97, | ||||
| 				98, | ||||
| 				99, | ||||
| 				100, | ||||
| 				101, | ||||
| 				102, | ||||
| 				103, | ||||
| 				128, | ||||
| 				129, | ||||
| 				130, | ||||
| 				131, | ||||
| 				116, | ||||
| 				117, | ||||
| 				118, | ||||
| 				119, | ||||
| 				156, | ||||
| 				157, | ||||
| 				158, | ||||
| 				159, | ||||
| 				192, | ||||
| 				193, | ||||
| 				194, | ||||
| 				195, | ||||
| 				144, | ||||
| 				145, | ||||
| 				146, | ||||
| 				147, | ||||
| 				196, | ||||
| 				197, | ||||
| 				198, | ||||
| 				199, | ||||
| 				176, | ||||
| 				177, | ||||
| 				178, | ||||
| 				179, | ||||
| 				148, | ||||
| 				149, | ||||
| 				150, | ||||
| 				151, | ||||
| 				200, | ||||
| 				201, | ||||
| 				202, | ||||
| 				203, | ||||
| 				204, | ||||
| 				205, | ||||
| 				206, | ||||
| 				207, | ||||
| 				64, | ||||
| 				65, | ||||
| 				66, | ||||
| 				67, | ||||
| 				68, | ||||
| 				69, | ||||
| 				70, | ||||
| 				71, | ||||
| 				35, | ||||
| 				36, | ||||
| 				37, | ||||
| 				38, | ||||
| 				39, | ||||
| 				40, | ||||
| 				41, | ||||
| 				42, | ||||
| 				47, | ||||
| 				48, | ||||
| 				49, | ||||
| 				50 | ||||
| 			] | ||||
| 		}, | ||||
| 		"model": "auth.group", | ||||
| 		"pk": 1 | ||||
| 	} | ||||
| ] | ||||
							
								
								
									
										2441
									
								
								data/auth_permissions.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2441
									
								
								data/auth_permissions.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										386
									
								
								data/flat.json
									
									
									
									
									
								
							
							
						
						
									
										386
									
								
								data/flat.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										786
									
								
								data/sitetree.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										786
									
								
								data/sitetree.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,786 @@ | |||
| [ | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"alias": "main_menu", | ||||
| 			"title": "Hlavní menu" | ||||
| 		}, | ||||
| 		"model": "sitetree.tree", | ||||
| 		"pk": 1 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 1, | ||||
| 			"title": "Co je M&M", | ||||
| 			"tree": 1, | ||||
| 			"url": "/co-je-MaM/uvod/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 1 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 2, | ||||
| 			"title": "Jak řešit", | ||||
| 			"tree": 1, | ||||
| 			"url": "/jak-resit/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 2 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 3, | ||||
| 			"title": "Aktuální<br/> ročník", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_aktualni_zadani", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 3 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 4, | ||||
| 			"title": "Soustředění", | ||||
| 			"tree": 1, | ||||
| 			"url": "/soustredeni/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 4 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 5, | ||||
| 			"title": "Archiv", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_archiv_rocniky", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 5 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": true, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 6, | ||||
| 			"title": "Přihlásit", | ||||
| 			"tree": 1, | ||||
| 			"url": "login", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 6 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 1, | ||||
| 			"sort_order": 7, | ||||
| 			"title": "Úvod", | ||||
| 			"tree": 1, | ||||
| 			"url": "/co-je-MaM/uvod/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 7 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 1, | ||||
| 			"sort_order": 8, | ||||
| 			"title": "Organizátoři", | ||||
| 			"tree": 1, | ||||
| 			"url": "organizatori", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 8 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 1, | ||||
| 			"sort_order": 9, | ||||
| 			"title": "FAQ", | ||||
| 			"tree": 1, | ||||
| 			"url": "/co-je-MaM/FAQ/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 9 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 1, | ||||
| 			"sort_order": 10, | ||||
| 			"title": "Kontakt", | ||||
| 			"tree": 1, | ||||
| 			"url": "/co-je-MaM/kontakt/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 10 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 2, | ||||
| 			"sort_order": 11, | ||||
| 			"title": "Témata", | ||||
| 			"tree": 1, | ||||
| 			"url": "/jak-resit/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 11 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 2, | ||||
| 			"sort_order": 12, | ||||
| 			"title": "Jak psát příspěvek", | ||||
| 			"tree": 1, | ||||
| 			"url": "/jak-resit/jak-psat-prispevek/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 12 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 2, | ||||
| 			"sort_order": 13, | ||||
| 			"title": "Odměny", | ||||
| 			"tree": 1, | ||||
| 			"url": "/co-je-MaM/odmeny/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 13 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 3, | ||||
| 			"sort_order": 33, | ||||
| 			"title": "Výsledková listina", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_aktualni_vysledky", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 16 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 4, | ||||
| 			"sort_order": 18, | ||||
| 			"title": "Úvod", | ||||
| 			"tree": 1, | ||||
| 			"url": "/soustredeni/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 18 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 4, | ||||
| 			"sort_order": 19, | ||||
| 			"title": "Připravujeme", | ||||
| 			"tree": 1, | ||||
| 			"url": "/soustredeni/pripravujeme/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 19 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 4, | ||||
| 			"sort_order": 20, | ||||
| 			"title": "Proběhlo", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_seznam_soustredeni", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 20 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": true, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 21, | ||||
| 			"title": "Profil", | ||||
| 			"tree": 1, | ||||
| 			"url": "profil", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 21 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 23, | ||||
| 			"title": "Osobní údaje", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_resitel_edit", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 22 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [ | ||||
| 				2 | ||||
| 			], | ||||
| 			"access_restricted": true, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 36, | ||||
| 			"title": "Poslat řešení", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_nahraj_reseni", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 23 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 5, | ||||
| 			"sort_order": 35, | ||||
| 			"title": "Témata", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_archiv_temata", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 24 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [ | ||||
| 				1 | ||||
| 			], | ||||
| 			"access_restricted": true, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": null, | ||||
| 			"sort_order": 28, | ||||
| 			"title": "HIDDEN", | ||||
| 			"tree": 1, | ||||
| 			"url": "/korektury/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 28 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 28, | ||||
| 			"sort_order": 30, | ||||
| 			"title": "Aktuální", | ||||
| 			"tree": 1, | ||||
| 			"url": "korektury_list", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 30 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 28, | ||||
| 			"sort_order": 31, | ||||
| 			"title": "Zastaralé", | ||||
| 			"tree": 1, | ||||
| 			"url": "korektury_stare_list", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 31 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 28, | ||||
| 			"sort_order": 32, | ||||
| 			"title": "Nápověda", | ||||
| 			"tree": 1, | ||||
| 			"url": "/korektury/help/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 32 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 3, | ||||
| 			"sort_order": 15, | ||||
| 			"title": "Aktuální číslo", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_aktualni_zadani", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 33 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 5, | ||||
| 			"sort_order": 24, | ||||
| 			"title": "Čísla", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_archiv_rocniky", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 35 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 22, | ||||
| 			"title": "Úvod", | ||||
| 			"tree": 1, | ||||
| 			"url": "profil", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 36 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [ | ||||
| 				1 | ||||
| 			], | ||||
| 			"access_restricted": true, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 37, | ||||
| 			"title": "Odevzdaná řešení", | ||||
| 			"tree": 1, | ||||
| 			"url": "odevzdavatko_tabulka", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 37 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": true, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 38, | ||||
| 			"title": "Odhlásit se", | ||||
| 			"tree": 1, | ||||
| 			"url": "/logout/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 38 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [ | ||||
| 				1 | ||||
| 			], | ||||
| 			"access_restricted": true, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 36, | ||||
| 			"title": "Nahrát řešení", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_vloz_reseni", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 39 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 5, | ||||
| 			"sort_order": 40, | ||||
| 			"title": "Řešitelské články", | ||||
| 			"tree": 1, | ||||
| 			"url": "/archiv/clanky", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 40 | ||||
| 	} | ||||
| ] | ||||
|  | @ -1,816 +0,0 @@ | |||
| [ | ||||
|     { | ||||
|         "model": "sitetree.tree", | ||||
|         "pk": 1, | ||||
|         "fields": { | ||||
|             "title": "Hlavní menu", | ||||
|             "alias": "main_menu" | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 1, | ||||
|         "fields": { | ||||
|             "title": "Co je M&M", | ||||
|             "hint": "", | ||||
|             "url": "/co-je-MaM/uvod/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 1, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 2, | ||||
|         "fields": { | ||||
|             "title": "Jak řešit", | ||||
|             "hint": "", | ||||
|             "url": "/jak-resit/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 2, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 3, | ||||
|         "fields": { | ||||
|             "title": "Aktuální<br/> ročník", | ||||
|             "hint": "", | ||||
|             "url": "seminar_aktualni_zadani", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 3, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 4, | ||||
|         "fields": { | ||||
|             "title": "Soustředění", | ||||
|             "hint": "", | ||||
|             "url": "/soustredeni/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 4, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 5, | ||||
|         "fields": { | ||||
|             "title": "Archiv", | ||||
|             "hint": "", | ||||
|             "url": "seminar_archiv_rocniky", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 5, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 6, | ||||
|         "fields": { | ||||
|             "title": "Přihlásit", | ||||
|             "hint": "", | ||||
|             "url": "login", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": true, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 6, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 7, | ||||
|         "fields": { | ||||
|             "title": "Úvod", | ||||
|             "hint": "", | ||||
|             "url": "/co-je-MaM/uvod/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 1, | ||||
|             "sort_order": 7, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 8, | ||||
|         "fields": { | ||||
|             "title": "Organizátoři", | ||||
|             "hint": "", | ||||
|             "url": "organizatori", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 1, | ||||
|             "sort_order": 8, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 9, | ||||
|         "fields": { | ||||
|             "title": "FAQ", | ||||
|             "hint": "", | ||||
|             "url": "/co-je-MaM/FAQ/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 1, | ||||
|             "sort_order": 9, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 10, | ||||
|         "fields": { | ||||
|             "title": "Kontakt", | ||||
|             "hint": "", | ||||
|             "url": "/co-je-MaM/kontakt/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 1, | ||||
|             "sort_order": 10, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 11, | ||||
|         "fields": { | ||||
|             "title": "Témata", | ||||
|             "hint": "", | ||||
|             "url": "/jak-resit/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 2, | ||||
|             "sort_order": 11, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 12, | ||||
|         "fields": { | ||||
|             "title": "Jak psát příspěvek", | ||||
|             "hint": "", | ||||
|             "url": "/jak-resit/jak-psat-prispevek/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 2, | ||||
|             "sort_order": 12, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 13, | ||||
|         "fields": { | ||||
|             "title": "Odměny", | ||||
|             "hint": "", | ||||
|             "url": "/co-je-MaM/odmeny/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 2, | ||||
|             "sort_order": 13, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 16, | ||||
|         "fields": { | ||||
|             "title": "Výsledková listina", | ||||
|             "hint": "", | ||||
|             "url": "zadani/vysledkova-listina/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 3, | ||||
|             "sort_order": 33, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 17, | ||||
|         "fields": { | ||||
|             "title": "Články", | ||||
|             "hint": "", | ||||
|             "url": "clanky_resitel", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 3, | ||||
|             "sort_order": 34, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 18, | ||||
|         "fields": { | ||||
|             "title": "Úvod", | ||||
|             "hint": "", | ||||
|             "url": "/soustredeni/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 4, | ||||
|             "sort_order": 18, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 19, | ||||
|         "fields": { | ||||
|             "title": "Připravujeme", | ||||
|             "hint": "", | ||||
|             "url": "/soustredeni/pripravujeme/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 4, | ||||
|             "sort_order": 19, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 20, | ||||
|         "fields": { | ||||
|             "title": "Proběhlo", | ||||
|             "hint": "", | ||||
|             "url": "seminar_seznam_soustredeni", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 4, | ||||
|             "sort_order": 20, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 21, | ||||
|         "fields": { | ||||
|             "title": "Profil", | ||||
|             "hint": "", | ||||
|             "url": "profil", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": true, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 21, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 22, | ||||
|         "fields": { | ||||
|             "title": "Osobní údaje", | ||||
|             "hint": "", | ||||
|             "url": "seminar_resitel_edit", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 21, | ||||
|             "sort_order": 23, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 23, | ||||
|         "fields": { | ||||
|             "title": "Poslat řešení", | ||||
|             "hint": "", | ||||
|             "url": "seminar_nahraj_reseni", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 21, | ||||
|             "sort_order": 36, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 24, | ||||
|         "fields": { | ||||
|             "title": "Témata", | ||||
|             "hint": "", | ||||
|             "url": "seminar_temata", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 5, | ||||
|             "sort_order": 35, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 28, | ||||
|         "fields": { | ||||
|             "title": "HIDDEN", | ||||
|             "hint": "", | ||||
|             "url": "/korektury/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": true, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": null, | ||||
|             "sort_order": 28, | ||||
|             "access_permissions": [ | ||||
|                 1 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 30, | ||||
|         "fields": { | ||||
|             "title": "Aktuální", | ||||
|             "hint": "", | ||||
|             "url": "korektury_list", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 28, | ||||
|             "sort_order": 30, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 31, | ||||
|         "fields": { | ||||
|             "title": "Zastaralé", | ||||
|             "hint": "", | ||||
|             "url": "korektury_stare_list", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 28, | ||||
|             "sort_order": 31, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 32, | ||||
|         "fields": { | ||||
|             "title": "Nápověda", | ||||
|             "hint": "", | ||||
|             "url": "/korektury/help/", | ||||
|             "urlaspattern": false, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 28, | ||||
|             "sort_order": 32, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 33, | ||||
|         "fields": { | ||||
|             "title": "Aktuální číslo", | ||||
|             "hint": "", | ||||
|             "url": "seminar_aktualni_zadani", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 3, | ||||
|             "sort_order": 15, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 34, | ||||
|         "fields": { | ||||
|             "title": "Témata", | ||||
|             "hint": "", | ||||
|             "url": "seminar_temata", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 3, | ||||
|             "sort_order": 17, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 35, | ||||
|         "fields": { | ||||
|             "title": "Čísla", | ||||
|             "hint": "", | ||||
|             "url": "seminar_archiv_rocniky", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 5, | ||||
|             "sort_order": 24, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 36, | ||||
|         "fields": { | ||||
|             "title": "Úvod", | ||||
|             "hint": "", | ||||
|             "url": "profil", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": false, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 21, | ||||
|             "sort_order": 22, | ||||
|             "access_permissions": [ | ||||
| 
 | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "model": "sitetree.treeitem", | ||||
|         "pk": 37, | ||||
|         "fields": { | ||||
|             "title": "Odevzdaná řešení", | ||||
|             "hint": "", | ||||
|             "url": "odevzdavatko_tabulka", | ||||
|             "urlaspattern": true, | ||||
|             "tree": 1, | ||||
|             "hidden": false, | ||||
|             "alias": null, | ||||
|             "description": "", | ||||
|             "inmenu": true, | ||||
|             "inbreadcrumbs": true, | ||||
|             "insitetree": true, | ||||
|             "access_loggedin": false, | ||||
|             "access_guest": false, | ||||
|             "access_restricted": true, | ||||
|             "access_perm_type": 1, | ||||
|             "parent": 21, | ||||
|             "sort_order": 37, | ||||
|             "access_permissions": [ | ||||
|                 1 | ||||
|             ] | ||||
|         } | ||||
|     } | ||||
| ] | ||||
							
								
								
									
										12
									
								
								fix_json.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								fix_json.py
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| #!/usr/bin/python3 | ||||
| 
 | ||||
| import json | ||||
| import argparse | ||||
| 
 | ||||
| parser = argparse.ArgumentParser() | ||||
| parser.add_argument("input", type=argparse.FileType('r', encoding='utf-8')) | ||||
| parser.add_argument('output', type=argparse.FileType('w', encoding='utf-8')) | ||||
| args = parser.parse_args() | ||||
| 
 | ||||
| data = json.load(args.input) | ||||
| json.dump(data, args.output, ensure_ascii=False, sort_keys=True, indent='\t') | ||||
|  | @ -186,7 +186,7 @@ class KorekturyView(generic.TemplateView): | |||
| 		if email: | ||||
| 			emails.discard(email) | ||||
| 
 | ||||
| 		if not settings.SEND_EMAIL_NOTIFICATIONS: | ||||
| 		if not settings.POSLI_MAILOVOU_NOTIFIKACI: | ||||
| 			print("Poslal bych upozornění na tyto adresy: ", " ".join(emails)) | ||||
| 			return | ||||
| 
 | ||||
|  |  | |||
|  | @ -308,4 +308,4 @@ CISLO_IMG_DIR = os.path.join('cislo', 'img') | |||
| 
 | ||||
| 
 | ||||
| # E-MAIL NOTIFICATIONS | ||||
| SEND_EMAIL_NOTIFICATIONS = False | ||||
| POSLI_MAILOVOU_NOTIFIKACI = False | ||||
|  |  | |||
|  | @ -66,4 +66,4 @@ LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/ | |||
| 
 | ||||
| 
 | ||||
| # E-MAIL NOTIFICATIONS | ||||
| SEND_EMAIL_NOTIFICATIONS = True | ||||
| POSLI_MAILOVOU_NOTIFIKACI = True | ||||
|  |  | |||
|  | @ -9,10 +9,15 @@ body { | |||
| 	background-color: #fffbf6; | ||||
| 	min-height: 100%; | ||||
| } | ||||
| div.content-wrapper { | ||||
| 	padding-bottom: 200px; /* Footer height */ | ||||
| } | ||||
| 
 | ||||
| div.container { | ||||
| 	width: 970px; | ||||
| 	margin: auto; | ||||
| 	min-height: 100vh; | ||||
| 	position: relative; | ||||
| } | ||||
| 
 | ||||
| .org-logged-in div.container { | ||||
|  | @ -321,11 +326,7 @@ div.novinky_name { | |||
| 	font-style: italic; | ||||
| } | ||||
| 
 | ||||
| div.zadani_azad_termin { | ||||
| 	text-align: center; | ||||
| 	font-size: large; | ||||
| 	font-weight: bold | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /********** | ||||
|  * Footer | ||||
|  | @ -333,6 +334,9 @@ div.zadani_azad_termin { | |||
| 
 | ||||
| 
 | ||||
| #footer { | ||||
| 	position: absolute; | ||||
|   	bottom: 0; | ||||
|     width: 100%; | ||||
| 	background: url("../images/mozaika-footer.svg") no-repeat top center; | ||||
| 	height: 200px; | ||||
| 	background-position: relative; | ||||
|  | @ -353,6 +357,7 @@ div.zadani_azad_termin { | |||
| 
 | ||||
| p.license-mobile { | ||||
| 	display: none; | ||||
| 	margin-bottom: 0px; | ||||
| } | ||||
| 
 | ||||
| /*********************/ | ||||
|  | @ -390,8 +395,8 @@ input[type="file"] { | |||
|   border-width:1px; | ||||
| 	border-radius: 5px; | ||||
|   padding:3px; | ||||
|   top:20px; | ||||
|   left:20px; | ||||
|   top:50px; | ||||
|   left:10px; | ||||
| } | ||||
| 
 | ||||
| .field-with-comment:hover span.field-comment{ | ||||
|  | @ -763,7 +768,7 @@ div.odpocet { | |||
| 
 | ||||
| /*stránka organizátorů*/ | ||||
| 
 | ||||
| div.seznam_orgu, div.rozcestnik_temat { | ||||
| div.seznam_orgu, div.rozcestnik_temat, div.seznam_archiv { | ||||
| 	text-align: center; | ||||
| 	padding-bottom: 10px; | ||||
| } | ||||
|  | @ -897,6 +902,38 @@ div.cislo_odkazy ul { | |||
| 		padding: 0px; | ||||
| } | ||||
| 
 | ||||
| /* aktuální zadání */ | ||||
| .stranka_aktualni_zadani { | ||||
| 	text-align: center; | ||||
| } | ||||
| 
 | ||||
| #azad_obrazek { | ||||
| 	margin-top: 15px; | ||||
| } | ||||
| 
 | ||||
| div.zadani_termin { | ||||
| 	text-align: center; | ||||
| 	font-size: large; | ||||
| 	font-weight: bold; | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 420px) { | ||||
| 	div.zadani_termin { | ||||
| 		font-size: small; | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| div.zadani_termin .datum { | ||||
| 	color:#e84e10; | ||||
| 	margin:0px; | ||||
| } | ||||
| 
 | ||||
| #obrazek_cisla_archiv { | ||||
| 	text-align: center; | ||||
| 	margin: 10px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* galerie */ | ||||
| 
 | ||||
|  | @ -961,8 +998,6 @@ div.cislo_odkazy ul { | |||
| } | ||||
| 
 | ||||
| /* titulní obrázek hlavní galerie soustředění */ | ||||
| .titulni_obrazek { | ||||
| } | ||||
| 
 | ||||
| .galerie_nahledy{ | ||||
|   /*margin: 1em 0;*/ | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 476 KiB | 
|  | @ -31,6 +31,10 @@ | |||
|       src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> | ||||
|     </script> | ||||
| 
 | ||||
|     {# Případné skripty widgetů formulářů #} | ||||
|     {% if form %} | ||||
|         {{form.media}} | ||||
|     {% endif %} | ||||
|     {# script specifický pro stránku #} | ||||
|     {% block script %}{% endblock %} | ||||
| 
 | ||||
|  | @ -52,9 +56,8 @@ | |||
|     {% endif %} | ||||
| 
 | ||||
|    <div class="container"> | ||||
|    <div class="content-wrapper"> | ||||
| 
 | ||||
| 	  <div class='row'> | ||||
| 		<div class='col-md-12'> | ||||
|           <a href='/'> | ||||
| 	    <div id="title" >M&M - korespondenční seminář a časopis MFF UK</div> | ||||
|             <div id="header"> | ||||
|  | @ -65,11 +68,7 @@ | |||
| 		<img class="logo-mobile" src="{% static 'images/logo-mobile.svg' %}" /> | ||||
|             </div> | ||||
|             </a> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class='row'> | ||||
| 	    <div class='col-md-12'> | ||||
| 
 | ||||
| {# ========= MENU ========== #} | ||||
| 
 | ||||
|  | @ -77,8 +76,6 @@ | |||
| 
 | ||||
| {# ========= MENU MOBILE ========== #} | ||||
| 
 | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
| <!--Navbar--> | ||||
| <nav class="nav-button"> | ||||
|  | @ -102,25 +99,23 @@ | |||
| 
 | ||||
| {# ========= END MENU ========== #} | ||||
| 
 | ||||
|     <div class='row'> | ||||
| 	<div class='row content'> | ||||
| 	<div class='col-md-12'> | ||||
| 
 | ||||
| 	<div class='content'> | ||||
| 
 | ||||
| 	    {% block content %} | ||||
| 	    {% endblock content %} | ||||
| 	        </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
| 	<div class='row'> | ||||
| 	  <div class='col-md-12'> | ||||
| 
 | ||||
|    </div> <!-- content-wrapper --> | ||||
| 
 | ||||
| 
 | ||||
| 	   <!-- Credit: https://www.freecodecamp.org/news/how-to-keep-your-footer-where-it-belongs-59c6aa05c59c/ --> | ||||
| 		<div id="footer"> | ||||
| 			<p class="license">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> | ||||
| 		<p class="license-mobile">Korespondenční seminář M&M organizují převážně studenti <a href="https://www.mff.cuni.cz/">MFF UK</a>. Organizaci semináře a vydávání časopisu podporuje <a href="https://jcmf.cz/">Jednota českých matematiků a fyziků</a>. S obsahem webu M&M je možné nakládat dle licence <a href="https://creativecommons.org/licenses/by/3.0/cz/">Creative Commons Attribution 3.0</a>.</p> | ||||
| 	  </div> | ||||
| 	</div> | ||||
|      | ||||
|     </div> | ||||
| 
 | ||||
|     <script src="{% static 'js/bootstrap.js' %}"></script> | ||||
|     <script src="{% static 'js/jquery.jcarousel-core.js' %}" type="text/javascript"></script> | ||||
|  |  | |||
|  | @ -350,7 +350,7 @@ | |||
|     </a> | ||||
|     <a | ||||
|        id="casopis" | ||||
|        href="/archiv/cisla/" | ||||
|        href="/archiv/rocniky" | ||||
|        transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)"> | ||||
|       <g | ||||
|          id="g959"> | ||||
|  | @ -379,7 +379,7 @@ | |||
|       </g> | ||||
|     </a> | ||||
|     <a | ||||
|        href="/clanky/uvod/" | ||||
|        href="/jak-resit" | ||||
|        id="clanky" | ||||
|        transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)"> | ||||
|       <g | ||||
|  |  | |||
| Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB | 
|  | @ -3,7 +3,7 @@ | |||
| {% autoescape off %} | ||||
| <ul class="menu"> | ||||
|     {% for item in sitetree_items %} | ||||
|         <li class="{% if item.has_children %}dropdown{% endif %} {% if item.is_current or item.in_current_branch %}active{% endif %}" | ||||
|         <li class="dropdown {% if item.is_current or item.in_current_branch %}active{% endif %}" | ||||
|             style="{% if item.title == "HIDDEN" %}display:none{% endif %}" | ||||
|         > | ||||
|             <a href="{% sitetree_url for item %}" > | ||||
|  |  | |||
|  | @ -1,11 +1,14 @@ | |||
| from django.contrib import admin | ||||
| from django.contrib.auth.models import Permission | ||||
| from django.contrib.auth.models import Group | ||||
| from django.db import models | ||||
| from django.forms import widgets | ||||
| 
 | ||||
| from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | ||||
| from reversion.admin import VersionAdmin | ||||
| from django_reverse_admin import ReverseModelAdmin | ||||
| from solo.admin import SingletonModelAdmin | ||||
| 
 | ||||
| 
 | ||||
| # Todo: reversion | ||||
| 
 | ||||
| import seminar.models as m | ||||
|  | @ -14,8 +17,6 @@ admin.site.register(m.Skola) | |||
| admin.site.register(m.Prijemce) | ||||
| admin.site.register(m.Rocnik) | ||||
| admin.site.register(m.Cislo) | ||||
| admin.site.register(m.Organizator) | ||||
| admin.site.register(m.Soustredeni) | ||||
| 
 | ||||
| @admin.register(m.Osoba) | ||||
| class OsobaAdmin(admin.ModelAdmin): | ||||
|  | @ -31,18 +32,26 @@ class OsobaAdmin(admin.ModelAdmin): | |||
| 	synchronizuj_maily.short_description = "Synchronizuj vybraným osobám e-maily do uživatelů" | ||||
| 
 | ||||
| 	def udelej_orgem(self,request,queryset): | ||||
| 		org_perm = Permission.objects.filter(codename__exact='org').first() | ||||
| 		org_group = Group.objects.get(name='org') | ||||
| 		print(queryset) | ||||
| 		for o in queryset: | ||||
| 			user = o.user | ||||
| 			user.user_permissions.add(org_perm) | ||||
| 			print(user) | ||||
| 			user.groups.add(org_group) | ||||
| 			user.is_staff = True | ||||
| 			user.save() | ||||
| 			org = m.Organizator.objects.create(osoba=o) | ||||
| 			org.save() | ||||
| 	udelej_orgem.short_description = "Udělej vybraných osob organizátory" | ||||
| 
 | ||||
| @admin.register(m.Organizator) | ||||
| class OrganizatorAdmin(admin.ModelAdmin): | ||||
| 	search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka'] | ||||
| 			 | ||||
| @admin.register(m.Resitel) | ||||
| class ResitelAdmin(admin.ModelAdmin): | ||||
| 	search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka'] | ||||
| 	ordering = ('osoba__jmeno','osoba__prijmeni') | ||||
| 
 | ||||
| @admin.register(m.Problem) | ||||
| class ProblemAdmin(PolymorphicParentModelAdmin): | ||||
|  | @ -53,36 +62,81 @@ class ProblemAdmin(PolymorphicParentModelAdmin): | |||
| 		m.Uloha, | ||||
| 		m.Konfera, | ||||
| 		] | ||||
| 	# Pokud chceme orezavat na aktualni rocnik, musime do modelu pridat odkaz na rocnik. Zatim bere vse. | ||||
| 	search_fields = ['nazev'] | ||||
| 
 | ||||
| # V ProblemAdmin to nejde, protoze se to nepropise do deti | ||||
| class ProblemAdminMixin(object): | ||||
| 	show_in_index = True | ||||
| 	autocomplete_fields = ['nadproblem','autor','garant'] | ||||
| 	filter_horizontal = ['opravovatele'] | ||||
| 
 | ||||
| 
 | ||||
| @admin.register(m.Tema) | ||||
| class TemaAdmin(PolymorphicChildModelAdmin): | ||||
| class TemaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Tema | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.Clanek) | ||||
| class ClanekAdmin(PolymorphicChildModelAdmin): | ||||
| class ClanekAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Clanek | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.Uloha) | ||||
| class UlohaAdmin(PolymorphicChildModelAdmin): | ||||
| class UlohaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Uloha | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.Konfera) | ||||
| class KonferaAdmin(PolymorphicChildModelAdmin): | ||||
| class KonferaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Konfera | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| 
 | ||||
| class TextAdminInline(admin.TabularInline): | ||||
| 	model = m.Text | ||||
| 	formfield_overrides = { | ||||
| 		models.TextField: {'widget': widgets.TextInput} | ||||
| 	} | ||||
| 	exclude = ['text_zkraceny_set','text_zkraceny'] | ||||
| 
 | ||||
| admin.site.register(m.Text) | ||||
| 
 | ||||
| class ResitelInline(admin.TabularInline): | ||||
| 	model = m.Resitel | ||||
| 	extra = 1 | ||||
| admin.site.register(m.Resitel) | ||||
| 
 | ||||
| class SoustredeniUcastniciInline(admin.TabularInline): | ||||
| 	model = m.Soustredeni_Ucastnici | ||||
| 	extra = 1 | ||||
| 	fields = ['resitel','poznamka'] | ||||
| 	autocomplete_fields = ['resitel'] | ||||
| 	ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni'] | ||||
| 	formfield_overrides = { | ||||
| 		models.TextField: {'widget': widgets.TextInput} | ||||
| 	} | ||||
| 
 | ||||
| 	def get_queryset(self,request): | ||||
| 		qs = super().get_queryset(request) | ||||
| 		return qs.select_related('resitel','soustredeni') | ||||
| 
 | ||||
| class SoustredeniOrganizatoriInline(admin.TabularInline): | ||||
| 	model = m.Soustredeni.organizatori.through | ||||
| 	extra = 1 | ||||
| 	fields = ['organizator','poznamka'] | ||||
| 	autocomplete_fields = ['organizator'] | ||||
| 	ordering = ['organizator__osoba__jmeno','organizator__prijmeni'] | ||||
| 	formfield_overrides = { | ||||
| 		models.TextField: {'widget': widgets.TextInput} | ||||
| 	} | ||||
| 
 | ||||
| 	def get_queryset(self,request): | ||||
| 		qs = super().get_queryset(request) | ||||
| 		return qs.select_related('organizator', 'soustredeni') | ||||
| 
 | ||||
| 
 | ||||
| @admin.register(m.Soustredeni) | ||||
| class SoustredeniAdmin(admin.ModelAdmin): | ||||
| 	model = m.Soustredeni | ||||
| 	inline_type = 'tabular' | ||||
| 	inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] | ||||
| 
 | ||||
| 
 | ||||
| class PrilohaReseniInline(admin.TabularInline): | ||||
| 	model = m.PrilohaReseni | ||||
|  | @ -92,6 +146,7 @@ admin.site.register(m.PrilohaReseni) | |||
| class Reseni_ResiteleInline(admin.TabularInline): | ||||
| 	model = m.Reseni_Resitele | ||||
| 
 | ||||
| 
 | ||||
| @admin.register(m.Reseni) | ||||
| class ReseniAdmin(ReverseModelAdmin): | ||||
| 	base_model = m.Reseni | ||||
|  | @ -106,7 +161,6 @@ admin.site.register(m.Hodnoceni) | |||
| admin.site.register(m.Pohadka) | ||||
| admin.site.register(m.Obrazek) | ||||
| 
 | ||||
| 
 | ||||
| # Polymorfismus pro stromy | ||||
| # TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ from django import forms | |||
| from dal import autocomplete | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.contrib.auth.models import User | ||||
| from django.forms import formset_factory | ||||
| from django.forms.models import inlineformset_factory | ||||
| 
 | ||||
| from .models import Skola, Resitel, Osoba, Problem | ||||
|  | @ -184,7 +185,7 @@ class ProfileEditForm(forms.Form): | |||
| 		max_value=date.today().year+8, | ||||
| 		required=True) | ||||
| 	zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True) | ||||
| 	zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=True) | ||||
| 	zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=False) | ||||
| 
 | ||||
| 	spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False) | ||||
| #	def clean_username(self): | ||||
|  | @ -301,3 +302,91 @@ class NahrajObrazekKTreeNoduForm(forms.ModelForm): | |||
| 		model = m.Obrazek | ||||
| 		fields = ('na_web',) | ||||
| 
 | ||||
| 
 | ||||
| class JednoHodnoceniForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
| 		model = m.Hodnoceni | ||||
| 		fields = ('problem', 'body', 'cislo_body') | ||||
| 		widgets = { | ||||
| 			'problem': autocomplete.ModelSelect2( | ||||
| 				url='autocomplete_problem_odevzdatelny',   # FIXME: Dovolit i starší? | ||||
| 				) | ||||
| 			} | ||||
| 
 | ||||
| OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | ||||
| 		extra = 0, | ||||
| 		) | ||||
| 
 | ||||
| # FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat | ||||
| DATE_FORMAT = '%Y-%m-%d' | ||||
| 
 | ||||
| class OdevzdavatkoTabulkaFiltrForm(forms.Form): | ||||
| 	"""Form pro filtrování přehledové odevzdávátkové tabulky | ||||
| 
 | ||||
| 	Inspirováno https://kam.mff.cuni.cz/mffzoom/""" | ||||
| 
 | ||||
| 	# Věci definované níž se importují i ve views pro odevzdávátko (Inspirováno https://docs.djangoproject.com/en/3.1/ref/models/fields/#field-choices) | ||||
| 
 | ||||
| 	RESITELE_RELEVANTNI = 'relevantni' | ||||
| 	RESITELE_LETOSNI = 'letosni' | ||||
| 	RESITELE_CHOICES = [ | ||||
| 		(RESITELE_RELEVANTNI, 'Relevantní řešitelé'), # I.e. nezobrazovat prázdné řádky tabulky | ||||
| 		(RESITELE_LETOSNI, 'Všichni letošní'), | ||||
| 		# Možná: všechny vč. historických? | ||||
| 		] | ||||
| 
 | ||||
| 	PROBLEMY_MOJE = 'moje' | ||||
| 	PROBLEMY_LETOSNI = 'letosni' | ||||
| 	PROBLEMY_CHOICES = [ | ||||
| 		(PROBLEMY_MOJE, 'Moje problémy'), # Letošní problémy, které mají v sobě nebo v nadproblémech přiřazeného daného orga | ||||
| 		(PROBLEMY_LETOSNI, 'Všechny letošní'), | ||||
| 		# TODO: *hlavní problémy, možná všechny... | ||||
| 		# XXX: Chtělo by to i "aktuálně zadané... | ||||
| 		] | ||||
| 
 | ||||
| 	# TODO: Typy problémů (problémy, úlohy, ostatní, všechny)? Jen některá řešení (obodovaná/neobodovaná, víc řešitelů, ...)? | ||||
| 
 | ||||
| 
 | ||||
| 	def gen_terminy(): | ||||
| 		import datetime | ||||
| 		from time import strftime | ||||
| 
 | ||||
| 		aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik | ||||
| 		aktualni_cislo = m.Nastaveni.get_solo().aktualni_cislo | ||||
| 
 | ||||
| 		result = [] | ||||
| 
 | ||||
| 		for cislo in m.Cislo.objects.filter( | ||||
| 				rocnik=aktualni_rocnik, | ||||
| 				poradi__lte=aktualni_cislo.poradi, | ||||
| 				).reverse():	# Standardně se řadí od nejnovějšího čísla | ||||
| 			# Předem je mi líto kohokoliv, kdo tyhle řádky bude číst... | ||||
| 			if cislo.datum_vydani is not None and cislo.datum_vydani <= datetime.date.today(): | ||||
| 				result.append(( | ||||
| 					strftime(DATE_FORMAT, cislo.datum_vydani.timetuple()), | ||||
| 					f"Vydání {cislo.poradi}. čísla")) | ||||
| 			if cislo.datum_preddeadline is not None and cislo.datum_preddeadline <= datetime.date.today(): | ||||
| 				result.append(( | ||||
| 					strftime(DATE_FORMAT, cislo.datum_preddeadline.timetuple()), | ||||
| 					f"Předdeadline {cislo.poradi}. čísla")) | ||||
| 			if cislo.datum_deadline_soustredeni is not None and cislo.datum_deadline_soustredeni <= datetime.date.today(): | ||||
| 				result.append(( | ||||
| 					strftime(DATE_FORMAT, cislo.datum_deadline_soustredeni.timetuple()), | ||||
| 					f"Sous. deadline {cislo.poradi}. čísla")) | ||||
| 			if cislo.datum_deadline is not None and cislo.datum_deadline <= datetime.date.today(): | ||||
| 				result.append(( | ||||
| 					strftime(DATE_FORMAT, cislo.datum_deadline.timetuple()), | ||||
| 					f"Finální deadline {cislo.poradi}. čísla")) | ||||
| 		result.append(( | ||||
| 			strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes")) | ||||
| 
 | ||||
| 		return result | ||||
| 
 | ||||
| 	# NOTE: Initial definuji pro jednotlivé fieldy, aby to bylo tady a nebylo potřeba to řešit ve views... | ||||
| 	resitele = forms.ChoiceField(choices=RESITELE_CHOICES, initial=RESITELE_RELEVANTNI) | ||||
| 	problemy = forms.ChoiceField(choices=PROBLEMY_CHOICES, initial=PROBLEMY_MOJE) | ||||
| 	 | ||||
| 	# choices jako parametr Select widgetu neumí brát callable, jen iterable, takže si pro jednoduchost můžu rovnou uložit výsledek sem... | ||||
| 	terminy = gen_terminy() | ||||
| 	reseni_od = forms.DateField(input_formats=[DATE_FORMAT], widget=forms.Select(choices=terminy), initial=terminy[-2]) | ||||
| 	reseni_do = forms.DateField(input_formats=[DATE_FORMAT], widget=forms.Select(choices=terminy), initial=terminy[-1]) | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ from django.contrib.contenttypes.models import ContentType | |||
| from django.utils.text import get_valid_filename | ||||
| from imagekit.models import ImageSpecField, ProcessedImageField | ||||
| from imagekit.processors import ResizeToFit, Transpose | ||||
| from django.utils.functional import cached_property | ||||
| 
 | ||||
| from django_countries.fields import CountryField | ||||
| from solo.models import SingletonModel | ||||
|  | @ -26,11 +27,14 @@ from taggit.managers import TaggableManager | |||
| from reversion import revisions as reversion | ||||
| 
 | ||||
| from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode | ||||
| from seminar import treelib | ||||
| 
 | ||||
| from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) | ||||
| 
 | ||||
| from polymorphic.models import PolymorphicModel | ||||
| 
 | ||||
| from django.core.mail import EmailMessage | ||||
| from seminar.utils import aktivniResitele | ||||
| 
 | ||||
| class SeminarModelBase(models.Model): | ||||
| 
 | ||||
|  | @ -624,9 +628,43 @@ class Cislo(SeminarModelBase): | |||
| 			return None | ||||
| 		return c | ||||
| 
 | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		super().__init__(*args, **kwargs) | ||||
| 		self.__original_verejne = self.verejne_db | ||||
| 
 | ||||
| 	def posli_cislo_mailem(self): | ||||
| 		# parametry e-mailu | ||||
| 		odkaz = self.get_absolute_url() | ||||
| 
 | ||||
| 		poslat_z_mailu = 'zadani@mam.mff.cuni.cz' | ||||
| 		predmet = 'Vyšlo číslo {}'.format(self.kod()) | ||||
| 		text_mailu = 'Ahoj,\n' \ | ||||
| 			   'na adrese {} najdete nejnovější číslo.\n' \ | ||||
| 			   'Vaše M&M\n'.format(odkaz) | ||||
| 
 | ||||
| 		# Prijemci e-mailu | ||||
| 		emaily = map(lambda r: r.osoba.email, filter(lambda r: r.zasilat_cislo_emailem, aktivniResitele(self))) | ||||
| 
 | ||||
| 		if not settings.POSLI_MAILOVOU_NOTIFIKACI: | ||||
| 			print("Poslal bych upozornění na tyto adresy: ", " ".join(emaily)) | ||||
| 			return | ||||
| 
 | ||||
| 		email = EmailMessage( | ||||
| 			subject=predmet, | ||||
| 			body=text_mailu, | ||||
| 			from_email=poslat_z_mailu, | ||||
| 			bcc=list(emaily) | ||||
| 			#bcc = příjemci skryté kopie | ||||
| 		) | ||||
| 
 | ||||
| 		email.send() | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		self.vygeneruj_nahled() | ||||
| 		# Při zveřejnění pošle mail | ||||
| 		if self.verejne_db and not self.__original_verejne: | ||||
| 			self.posli_cislo_mailem() | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.cislonode.save() | ||||
|  | @ -818,10 +856,11 @@ class Problem(SeminarModelBase,PolymorphicModel): | |||
| 		return self.nazev | ||||
| 
 | ||||
| 	# Implicitini implementace, jednotlivé dědící třídy si přepíšou | ||||
| 	@cached_property | ||||
| 	def kod_v_rocniku(self): | ||||
| 		if self.stav == 'zadany': | ||||
| 			if self.nadproblem: | ||||
| 				return self.nadproblem.kod_v_rocniku()+".{}".format(self.kod) | ||||
| 				return self.nadproblem.kod_v_rocniku+".{}".format(self.kod) | ||||
| 			return str(self.kod) | ||||
| 		return '<Není zadaný>' | ||||
| 
 | ||||
|  | @ -829,17 +868,26 @@ class Problem(SeminarModelBase,PolymorphicModel): | |||
| 		# aktuálně podle stavu problému | ||||
| 		# FIXME pro některé problémy možná chceme override | ||||
| 		# FIXME vrací veřejnost čistě problému, nezávisle na čísle, ve kterém je.  | ||||
| 		# Je to tak správně? | ||||
| 		# Je to tak správně? Podle aktuální představy ano. | ||||
| 		stav_verejny = False | ||||
| 		if self.stav == 'zadany' or self.stav == 'vyreseny': | ||||
| 			stav_verejny = True | ||||
| 			print("stav_verejny: {}".format(stav_verejny))		 | ||||
| 
 | ||||
| 		cislo_verejne = False | ||||
| 		cislonode = self.cislo_node() | ||||
| 		if cislonode is None: | ||||
| 			# problém nemá vlastní node, veřejnost posuzujeme jen podle stavu | ||||
| 			print("empty node")		 | ||||
| 			return stav_verejny | ||||
| 		 | ||||
| 		#cislo_verejne = False | ||||
| 		#if (self.cislo_zadani and self.cislo_zadani.verejne()): | ||||
| 		#	cislo_verejne = True | ||||
| 		 | ||||
| 		#return (stav_verejny and cislo_verejne) | ||||
| 		else:	 | ||||
| 			cislo_zadani = cislonode.cislo | ||||
| 			if (cislo_zadani and cislo_zadani.verejne()): | ||||
| 				print("cislo: {}".format(cislo_zadani)) | ||||
| 				cislo_verejne = True | ||||
| 			print("stav_verejny: {}".format(stav_verejny))		 | ||||
| 			print("cislo_verejne: {}".format(cislo_verejne))		 | ||||
| 			return (stav_verejny and cislo_verejne) | ||||
| 	verejne.boolean = True | ||||
| 
 | ||||
| 	def verejne_url(self): | ||||
|  | @ -875,16 +923,17 @@ class Tema(Problem): | |||
| 	tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES,  | ||||
| 		blank=False, default=TEMA_TEMA) | ||||
| 
 | ||||
| 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',blank=True, null=True, | ||||
| 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',related_name='temata',blank=True, null=True, | ||||
| 		on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	abstrakt = models.TextField('Abstrakt na rozcestník', blank=True) | ||||
| 	obrazek = models.ImageField('Obrázek na rozcestník', null=True) | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def kod_v_rocniku(self): | ||||
| 		if self.stav == 'zadany': | ||||
| 			if self.nadproblem: | ||||
| 				return self.nadproblem.kod_v_rocniku()+".t{}".format(self.kod) | ||||
| 				return self.nadproblem.kod_v_rocniku+".t{}".format(self.kod) | ||||
| 			return "t{}".format(self.kod) | ||||
| 		return '<Není zadaný>' | ||||
| 
 | ||||
|  | @ -894,6 +943,16 @@ class Tema(Problem): | |||
| 		for tvcn in self.temavcislenode_set.all(): | ||||
| 			tvcn.save() | ||||
| 
 | ||||
| 	def cislo_node(self): | ||||
| 		tema_node_set = self.temavcislenode_set.all() | ||||
| 		tema_cisla_vyskyt = [] | ||||
| 		for tn in tema_node_set: | ||||
| 			tema_cisla_vyskyt.append( | ||||
| 				treelib.get_upper_node_of_type(tn, CisloNode).cislo) | ||||
| 		tema_cisla_vyskyt.sort(key=lambda x:x.datum_vydani) | ||||
| 		prvni_zadani = tema_cisla_vyskyt[0] | ||||
| 		return prvni_zadani.cislonode | ||||
| 
 | ||||
| class Clanek(Problem): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_clanky' | ||||
|  | @ -907,10 +966,13 @@ class Clanek(Problem): | |||
| 		if self.stav == 'zadany': | ||||
| # Nemělo by být potřeba | ||||
| #			if self.nadproblem: | ||||
| #				return self.nadproblem.kod_v_rocniku()+".c{}".format(self.kod) | ||||
| #				return self.nadproblem.kod_v_rocniku+".c{}".format(self.kod) | ||||
| 			return "c{}".format(self.kod) | ||||
| 		return '<Není zadaný>' | ||||
| 	 | ||||
| 	def node(self): | ||||
| 		return None | ||||
| 
 | ||||
| class Text(SeminarModelBase): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_texty' | ||||
|  | @ -961,11 +1023,12 @@ class Uloha(Problem): | |||
| 	# UlohaZadaniNode | ||||
| 	# UlohaVzorakNode | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def kod_v_rocniku(self): | ||||
| 		if self.stav == 'zadany': | ||||
| 			name="{}.u{}".format(self.cislo_zadani.poradi,self.kod) | ||||
| 			if self.nadproblem: | ||||
| 				return self.nadproblem.kod_v_rocniku()+name | ||||
| 				return self.nadproblem.kod_v_rocniku+name | ||||
| 			return name | ||||
| 		return '<Není zadaný>' | ||||
| 
 | ||||
|  | @ -983,6 +1046,9 @@ class Uloha(Problem): | |||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| 	def cislo_node(self): | ||||
| 		zadani_node = self.ulohazadaninode  | ||||
| 		return treelib.get_upper_node_of_type(zadani_node, CisloNode) | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Reseni(SeminarModelBase): | ||||
|  | @ -1273,6 +1339,8 @@ class Konfera(Problem): | |||
| 	def __str__(self): | ||||
| 		return "{}: ({})".format(self.nazev, self.soustredeni) | ||||
| 
 | ||||
| 	def cislo_node(self): | ||||
| 		return None | ||||
| 
 | ||||
| # Vazebna tabulka. Mozna se generuje automaticky. | ||||
| @reversion.register(ignore_duplicates=True) | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								seminar/static/images/no-picture.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								seminar/static/images/no-picture.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 86 KiB | 
|  | @ -1,12 +1,13 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div> | ||||
| 
 | ||||
|   <h2> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       Archiv čísel | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h2> | ||||
|   <div class="seznam_archiv"> | ||||
| 
 | ||||
|     {% for rocnik, url_png in object_list.items %} | ||||
| 
 | ||||
|  | @ -54,6 +55,7 @@ | |||
|     {% empty %} | ||||
|       Nejsou žádné ročníky | ||||
|     {% endfor %} | ||||
|     </div> | ||||
| 
 | ||||
| 
 | ||||
| </div> | ||||
| {% endblock content %} | ||||
|  |  | |||
|  | @ -9,11 +9,7 @@ | |||
|       Číslo {{ cislo }} | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h1> | ||||
| 
 | ||||
|   {% if cislo.pdf %} | ||||
|     <p><a href='{{ cislo.pdf.url }}'>Číslo v pdf</a> | ||||
|   {% endif %} | ||||
|   <p><a href='{{ cislo.rocnik.verejne_url }}'>Ročník {{ cislo.rocnik }}</a> | ||||
|   <a href='{{ cislo.rocnik.verejne_url }}'>Zpět na ročník {{ cislo.rocnik }}</a> | ||||
| 
 | ||||
|   {% if v_cisle_zadane %} | ||||
|     <h2>Zadané problémy</h2> | ||||
|  | @ -50,12 +46,26 @@ | |||
|       </div> | ||||
|   {% endif %} | ||||
| 
 | ||||
| 
 | ||||
|   {% if cislo.titulka_nahled %} | ||||
|  <hr> | ||||
|  <div id="obrazek_cisla_archiv"> | ||||
| 		    <a href="{{ cislo.pdf.url }}"><img src="{{ cislo.titulka_nahled.url }}" alt=Titulní strana {{ cislo.poradi }}. čísla></a> | ||||
|   {% elif cislo.pdf %} | ||||
|     <a href='{{ cislo.pdf.url }}'>Číslo v pdf</a>   | ||||
|   </div> | ||||
|   <hr>  | ||||
|   {% endif %} | ||||
| 
 | ||||
| 
 | ||||
| {% comment %} | ||||
|   <script id="vuedata" type="application/json">{"treenode":{{cislo.cislonode.id}}}</script> | ||||
|   <div id="app"> | ||||
| 	  <app></app> | ||||
|   </div> | ||||
|   {% render_bundle 'chunk-vendors' %} | ||||
|   {% render_bundle 'vue_app_01' %} | ||||
| {% endcomment %} | ||||
| 
 | ||||
| 
 | ||||
|   {% if cislo.verejna_vysledkovka %} | ||||
|  | @ -74,9 +84,24 @@ | |||
|         <th class='border-r'># | ||||
|         <th class='border-r'>Jméno | ||||
|         {% for p in problemy %} | ||||
|         <th class='border-r'><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a> | ||||
|         <th class='border-r' id="problem{{ forloop.counter }}"><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a> | ||||
| 
 | ||||
|             {# TODELETE #} | ||||
|                 {% for podproblemy in podproblemy_iter.next %} | ||||
|                     <th class='border-r podproblem{{ forloop.parentloop.counter }}'><a href="{{ podproblemy.verejne_url }}">{{ podproblemy.kod_v_rocniku }}</a> | ||||
|                 {% endfor %} | ||||
|             {# TODELETE #} | ||||
| 
 | ||||
|         {% endfor %} | ||||
|         {% if ostatni %}<th class='border-r'>Ostatní {% endif %} | ||||
| 
 | ||||
|             {# TODELETE #} | ||||
|                 {% for podproblemy in podproblemy_iter.next %} | ||||
|                     <th class='border-r podproblem{{ problemy.len }}'><a href="{{ podproblemy.verejne_url }}">{{ podproblemy.kod_v_rocniku }}</a> | ||||
|                 {% endfor %} | ||||
|             {# TODELETE #} | ||||
| 
 | ||||
| 
 | ||||
|         <th class='border-r'>Za číslo | ||||
|         <th class='border-r'>Za ročník | ||||
|         <th class='border-r'>Odjakživa | ||||
|  | @ -90,6 +115,13 @@ | |||
|             {{ rv.resitel.osoba.plne_jmeno }} | ||||
|         {% for b in rv.body_problemy_sezn %} | ||||
|         <td class='border-r'>{{ b }} | ||||
| 
 | ||||
|             {# TODELETE #} | ||||
|                 {% for body_podproblemu in rv.body_podproblemy_iter.next %} | ||||
|                     <td class='border-r podproblem{{ forloop.parentloop.counter }}'>{{ body_podproblemu }} | ||||
|                 {% endfor %} | ||||
|             {# TODELETE #} | ||||
| 
 | ||||
|         {% endfor %} | ||||
|         <td class='border-r'>{{ rv.body_cislo }} | ||||
|         <td class='border-r'><b>{{ rv.body_rocnik }}</b> | ||||
|  | @ -97,6 +129,23 @@ | |||
|       </tr> | ||||
|     {% endfor %} | ||||
|     </table> | ||||
| 
 | ||||
|       {# TODELETE #} | ||||
|         <script> | ||||
|           {% for p in problemy %} | ||||
|             $(".podproblem{{ forloop.counter }}").css("display", "none") | ||||
|             $("#problem{{ forloop.counter }}")[0].addEventListener('mouseover', podproblem{{ forloop.counter }}) | ||||
|             $("#problem{{ forloop.counter }}")[0].addEventListener('mouseout', podproblem{{ forloop.counter }}end) | ||||
|             function podproblem{{ forloop.counter }}(event) { | ||||
|                 $(".podproblem{{ forloop.counter }}").css("display", "") | ||||
|             } | ||||
|             function podproblem{{ forloop.counter }}end(event) { | ||||
|                 $(".podproblem{{ forloop.counter }}").css("display", "none") | ||||
|             } | ||||
|           {% endfor %} | ||||
|         </script> | ||||
|       {# TODELETE #} | ||||
| 
 | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if not cislo.verejna_vysledkovka and user.je_org %} | ||||
|  |  | |||
|  | @ -8,15 +8,17 @@ | |||
|     {% endblock %}{% endblock %} | ||||
|   </h2> | ||||
| 
 | ||||
|   {% if temata_v_rocniku %} | ||||
|   {% if rocnik.temata %} | ||||
|     <h2>Témata</h2> | ||||
|     <ul> | ||||
|     {% for tema in temata_v_rocniku %} | ||||
|       <li>{% if tema.text_zadani %}<a href="{{ tema.verejne_url }}">{% endif %}{{ tema.kod_v_rocniku }}: {{ tema.nazev }}{% if tema.text_zadani %}</a>{% endif %} | ||||
|     {% for tema in rocnik.temata.all %} | ||||
|       <li>{#<a href="{{ tema.verejne_url }}">#}{{ tema.nazev }}{#</a>#} | ||||
|     {% endfor %} | ||||
|     </ul> | ||||
|   {% endif %} | ||||
| 
 | ||||
| <h2>Čísla</h2> | ||||
| 
 | ||||
| <div class="cisla-v-rocniku"> | ||||
|   {% for c in rocnik.verejna_cisla %} | ||||
|     <div class="cislo_pole"> | ||||
|  | @ -32,7 +34,7 @@ | |||
|         {% if c.titulka_nahled %} | ||||
|         <img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px> | ||||
|         {% else %} | ||||
|         <img src="" alt="no image" height=180px> | ||||
|         {% load static %} <img src="{% static 'images/no-picture.png' %}" height=180px alt="no-picture"> | ||||
|         {% endif %} | ||||
|       </div> | ||||
| 
 | ||||
|  | @ -61,6 +63,7 @@ | |||
|   {% endfor %} | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
|   {% if vysledkovka %} | ||||
|     {% if user.je_org %} | ||||
|       <div class='mam-org-only'> | ||||
|  |  | |||
|  | @ -8,10 +8,10 @@ | |||
|   </h1> | ||||
| 
 | ||||
|   {% for rocnik, temata in rocniky.items %} | ||||
|     <h2>Ročník {{ rocnik }}</h2> | ||||
|     <h2><a href="{{ rocnik.verejne_url }}">Ročník {{ rocnik }}</a></h2> | ||||
|     <ul> | ||||
|     {% for tema in temata %} | ||||
|     <li><a href="{{ tema.verejne_url }}"> {{ tema.kod_v_rocniku }}: {{ tema.nazev }} </a></li> | ||||
|     <li>{# <a href="{{ tema.verejne_url }}"> #}{{ tema.nazev }}{# </a> #}</li> | ||||
|     {% endfor %} | ||||
|     </ul> | ||||
|   {% endfor %} | ||||
|  |  | |||
|  | @ -9,15 +9,28 @@ | |||
| </h1> | ||||
| 
 | ||||
| {% for clanek in object_list %} | ||||
| {% with clanek.cislo.rocnik.rocnik as rocnik %} | ||||
| 	{% with clanek.cislo.rocnik.rocnik as rocnik %} | ||||
| 		{% ifchanged rocnik %} | ||||
| 			{% if not forloop.first %}</ul>{% endif %} | ||||
| 			<h2>{{ rocnik }}. ročník</h2> | ||||
| 			<ul> | ||||
| 		{% endifchanged %} | ||||
| 		<li> | ||||
|         <a href="{{ clanek.verejne_url }}">{{ clanek.nazev }}</a> | ||||
| {% endwith %} | ||||
| 			{% if clanek.cislo.pdf %} | ||||
| 			<a href="{{ clanek.cislo.pdf.url }}"> | ||||
| 				{{ clanek.nazev }} | ||||
| 				({% for r in clanek.reseni_set.first.resitele.all %}{{r}}{% if not forloop.last %}, {% endif %}{% endfor %}) | ||||
| 			</a> | ||||
| 			 | ||||
| 			{% else %} | ||||
| 				{{ clanek.nazev }}  | ||||
| 				{% for r in clanek.reseni_set.first.resitele.all %} | ||||
| 					{{r}}, | ||||
| 				{% endfor %} | ||||
| 				(vyšlo v čísle {{clanek.cislo}}) | ||||
| 			{% endif %} | ||||
| 		</li> | ||||
| 	{% endwith %} | ||||
| {% endfor %} | ||||
| </ul> | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,59 @@ | |||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| {# FIXME: Necopypastovat! Tohle je zkopírované ze static/seminar/dynamic_formsets.js #} | ||||
| <script type='text/javascript'> | ||||
| // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 | ||||
| function updateElementIndex(el, prefix, ndx) { | ||||
| 	var id_regex = new RegExp('(' + prefix + '-\\d+)'); | ||||
| 	var replacement = prefix + '-' + ndx; | ||||
| 	if ($(el).attr("for")) { | ||||
| 		$(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); | ||||
| 	} | ||||
| 	if (el.id) { | ||||
| 		el.id = el.id.replace(id_regex, replacement); | ||||
| 	} | ||||
| 	if (el.name) { | ||||
| 		el.name = el.name.replace(id_regex, replacement); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 | ||||
| function deleteForm(prefix, btn) { | ||||
|     var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); | ||||
|     if (total >= 1){ | ||||
|         btn.closest('tr').remove(); | ||||
|         var forms = $('.hodnoceni'); | ||||
|         $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); | ||||
|         for (var i=0, formCount=forms.length; i<formCount; i++) { | ||||
|             $(forms.get(i)).find(':input').each(function() { | ||||
|                 updateElementIndex(this, prefix, i); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/ | ||||
| $(document).ready(function(){ | ||||
| 	$('#pridat_hodnoceni').click(function() { | ||||
| 		var form_idx = $('#id_form-TOTAL_FORMS').val(); | ||||
| 		var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx); | ||||
| 		$('#form_set').append(new_form); | ||||
| 		// Newly created form has not the binding between remove button and remove function | ||||
| 		// We need to add it manually | ||||
| 		$('.smazat_hodnoceni').click(function(){ | ||||
| 			deleteForm("form",this); | ||||
| 		}); | ||||
| 		$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); | ||||
| 	}); | ||||
| 	$('.smazat_hodnoceni').click(function(){ | ||||
| 		deleteForm("form",this); | ||||
| 	}); | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <p>Řešené problémy: {{ object.problem.all | join:", " }}</p> | ||||
| 
 | ||||
| <p>Řešitelé: {{ object.resitele.all | join:", " }}</p> | ||||
|  | @ -27,21 +80,33 @@ | |||
| {% endif %} | ||||
| 
 | ||||
| {# Hodnocení: #} | ||||
| {# FIXME: Udělat jako formulář #} | ||||
| <h3>Hodnocení:</h3> | ||||
| {% if object.hodnoceni_set.all %} | ||||
| <table> | ||||
| <form method=post><table> | ||||
| {% csrf_token %} | ||||
| {{ form.management_form }} | ||||
| <table id="form_set"> | ||||
| <tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr> | ||||
| {% for h in object.hodnoceni_set.all %} | ||||
| <tr> | ||||
| 	<td>{{ h.problem }}</a></td> | ||||
| 	<td>{{ h.body }}</td> | ||||
| 	<td>{{ h.cislo_body }}</td></tr> | ||||
| {% for subform in form %} | ||||
| 	<tr class="hodnoceni"> | ||||
| 		<td>{{ subform.problem }}</td> | ||||
| 		<td>{{ subform.body }}</td> | ||||
| 		<td>{{ subform.cislo_body }}</td> | ||||
| 		<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{subform.prefix}}-jsremove"></td> | ||||
| 	</tr> | ||||
| {% endfor %} | ||||
| </table> | ||||
| {% else %} | ||||
| <p>Ještě nebylo hodnoceno</p> | ||||
| {% endif %} | ||||
| 
 | ||||
| 
 | ||||
| <input type=button id="pridat_hodnoceni" value="Přidat hodnocení"> | ||||
| <input type=submit></form> | ||||
| 
 | ||||
| <table id="empty_form" style="display: none;"> | ||||
| 	<tr class="hodnoceni"> | ||||
| 		<td>{{ form.empty_form.problem }}</td> | ||||
| 		<td>{{ form.empty_form.body }}</td> | ||||
| 		<td>{{ form.empty_form.cislo_body }}</td> | ||||
| 		<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{form.empty_form.prefix}}-jsremove"></td> | ||||
| 	</tr> | ||||
| </table> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  |  | |||
|  | @ -4,6 +4,14 @@ | |||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| <form method=get action=.> | ||||
| {{ filtr.resitele }} | ||||
| {{ filtr.problemy }} | ||||
| Od: {{ filtr.reseni_od }} | ||||
| Do: {{ filtr.reseni_do }} | ||||
| <input type=submit value="→"> | ||||
| </form> | ||||
| 
 | ||||
| <table> | ||||
| 	<tr> | ||||
| 		<td></td> {# Prázdná buňka v levém horním rohu #} | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% extends "base.html" %} | ||||
| {% load staticfiles %} | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|  |  | |||
|  | @ -20,13 +20,14 @@ | |||
| <h2><strong>Tvorba čísla</strong></h2> | ||||
| 
 | ||||
| <ul> | ||||
| 	<li><a href="/admin/seminar/problemnavrh/add/"><strong>přidat téma</strong></a></li> | ||||
| 	<li><a href="/admin/seminar/problem/add/"><strong>přidat téma</strong></a></li> | ||||
| 	<li><strong>korektury</strong> | ||||
| 	<ul> | ||||
| 		<li><a href="/korektury/">korekturování</a></li> | ||||
| 		<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li> | ||||
| 	</ul> | ||||
| 	</li> | ||||
| 	<li><a href="{% url 'odevzdavatko_tabulka' %}"><strong>zadávání bodů</strong></a></li> | ||||
| 	<li><a href='{{ posledni_cislo_url }}'><strong>poslední vydané číslo </strong></a></li> | ||||
| </ul> | ||||
| <hr /> | ||||
|  | @ -58,6 +59,7 @@ | |||
| <h2><strong>Soustředění</strong></h2> | ||||
| 
 | ||||
| <ul> | ||||
| 	<li><a href="/admin/seminar/soustredeni/add/">přidat soustředění</a></li> | ||||
| 	<li><strong>přednášky</strong> | ||||
| 
 | ||||
| 	<ul> | ||||
|  |  | |||
|  | @ -2,8 +2,6 @@ | |||
| {% load staticfiles %} | ||||
| 
 | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|     {{form.media}} | ||||
|     <script src="{% static 'seminar/prihlaska.js' %}"></script> | ||||
| {% endblock %} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,6 @@ | |||
| {% extends "base.html" %} | ||||
| {% load staticfiles %} | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|     {{form.media}} | ||||
|     <script src="{% static 'seminar/dynamic_formsets.js' %}"></script> | ||||
| {% endblock %} | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,8 +2,6 @@ | |||
| {% load staticfiles %} | ||||
| 
 | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|     {{form.media}} | ||||
|     <script src="{% static 'seminar/prihlaska.js' %}"></script> | ||||
| {% endblock %} | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
| <div class="tema_pole"> | ||||
| 
 | ||||
|   <h3> | ||||
|     <a href='{{ rocnik.verejne_url }}'>Téma {{ tematko.nazev }}</a> | ||||
|     <a href='{{ tematko.verejne_url }}'>Téma {{ tematko.nazev }}</a> | ||||
|   </h3> | ||||
| 
 | ||||
|   <div class="flip-card" id="tema-rozcestnik"> | ||||
|  |  | |||
|  | @ -1,3 +1,12 @@ | |||
| <script> | ||||
| function preddeadline() { | ||||
|   alert("Řešení, která nám přijdou do tohoto deadlinu, se pokusíme opravit co nejdříve, abyste měli ještě šanci si je ještě opravit před definitivním deadlinem čísla."); | ||||
| } | ||||
| function sousdeadline() { | ||||
|   alert("Body za řešení, která nám přijdou do tohoto deadlinu, se ještě započítají pro účast na připravovaném soustředění."); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| {% extends 'base.html' %} | ||||
| 
 | ||||
| {% load humanize %} | ||||
|  | @ -7,10 +16,22 @@ | |||
| {% block content %} | ||||
| 
 | ||||
| {% if nejblizsi_deadline %} | ||||
| <hr> | ||||
| <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á: | ||||
| 	<p><b><big>Do | ||||
|   {% if typ_deadline == 'soustredeni' %} | ||||
|   <a href="" onClick="sousdeadline()" | ||||
|      title="Body za řešení, která nám přijdou do tohoto deadlinu, se ještě započítají pro účast na připravovaném soustředění."> | ||||
|      deadlinu</a> odeslání <a href="/zadani/aktualni/">řešení | ||||
|   </a> pro účast na soustředění | ||||
| 
 | ||||
|   {% elif typ_deadline == 'preddeadline' %} <a href="" onClick="preddeadline()" | ||||
|   title="Řešení, která nám přijdou do tohoto deadlinu, se pokusíme opravit co nejdříve, abyste měli ještě šanci si je ještě opravit před definitivním deadlinem čísla.">1. deadlinu</a> aktuálního <a href="/zadani/aktualni/">čísla</a>  | ||||
|   {% else %} deadlinu aktuálního <a href="/zadani/aktualni/">čísla</a>  | ||||
|   {% endif %}zbývá: | ||||
|       {{nejblizsi_deadline|timeuntil}}</big></b></p> | ||||
| </div> | ||||
| <hr> | ||||
| {% endif %} | ||||
| 
 | ||||
| <div class=titulnistrana> | ||||
|  | @ -29,7 +50,7 @@ | |||
|   <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 #} | ||||
|     <a href="prihlaska/?"> <div class="button"> Zaregistruj se! </div> </a> | ||||
|     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 #} | ||||
|  |  | |||
|  | @ -5,11 +5,11 @@ | |||
| 
 | ||||
|   <h1> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       Průběžné výsledky {{ vysledkovka.rocnik }}. ročníku | ||||
|       Průběžné výsledky {{ rocnik.rocnik }}. ročníku | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h1> | ||||
| 
 | ||||
|   {% if vysledkovka %} | ||||
|   {% if radky_vysledkovky %} | ||||
|     {% include "seminar/vysledkovka_rocnik.html" %} | ||||
|   {% else %} | ||||
|     <p>V tomto ročníku zatím žádné výsledky nejsou.</p> | ||||
|  | @ -23,7 +23,7 @@ | |||
|   {% if user.je_org and vysledkovka_s_neverejnymi %} | ||||
|     <div class='mam-org-only'> | ||||
|     <h1>Výsledky včetně neveřejných</h1> | ||||
|     {% with vysledkovka_s_neverejnymi as vysledkovka %} | ||||
|     {% with vysledkovka_s_neverejnymi as radky_vysledkovky %} | ||||
|       {% include "seminar/vysledkovka_rocnik.html" %} | ||||
|     {% endwith %} | ||||
|     </div> | ||||
|  |  | |||
|  | @ -5,79 +5,47 @@ | |||
| {% endblock %}{% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div> | ||||
| <div class="stranka_aktualni_zadani"> | ||||
|    | ||||
| {% with nastaveni.aktualni_cislo as ac %} | ||||
|   | ||||
| {# Zobrazovani neverejnych zadani jen organizatorum #} | ||||
| {% if user.je_org or verejne %} | ||||
| {% if user.je_org and not verejne %}<div class="mam-org-only">{% endif %} | ||||
| 	{% if user.je_org and not verejne %}<div class="mam-org-only">{% endif %} | ||||
| 
 | ||||
| 	<hr> | ||||
| 	<div class="zadani_termin"> | ||||
| 	Termíny pro odeslání řešení {{ac.poradi}}. série:<br> | ||||
| 	 | ||||
|   {% if ac.zadane_problemy.all %} | ||||
| 	{% if ac.datum_deadline_soustredeni %}	 | ||||
|    	  <div class="zadani_azad_termin"> | ||||
| 	  	  Termín odeslání {{ac.cislo}}. série pro účast na soustředění: | ||||
|         {{ac.datum_deadline_soustredeni}} | ||||
|   	  </div> | ||||
|     {% endif %} | ||||
|   {% endif %} | ||||
|   {% if ac.zadane_problemy.all %} | ||||
| 	  <div class="zadani_azad_termin"> | ||||
| 		  Termín odeslání {{ac.cislo}}. série: {{ac.datum_deadline}} | ||||
| 	  </div> | ||||
| 			<span class="datum">{{ac.datum_deadline_soustredeni}}</span> pro účast na soustředění<br> | ||||
| 	{% endif %} | ||||
| 
 | ||||
| 	{% if ac.datum_preddeadline %} | ||||
| 			<span class="datum">{{ac.datum_preddeadline}}</span> pro otištění v dalším čísle<br> | ||||
| 	{% endif %} | ||||
| 
 | ||||
| 	{% if ac.datum_deadline %} | ||||
| 			<span class="datum">{{ac.datum_deadline}}</span> definitivní deadline<br> | ||||
| 	{% endif %} | ||||
| 
 | ||||
| 	</div> | ||||
| 	<hr> | ||||
| 
 | ||||
| 	{% if ac.titulka_nahled and ac.pdf %} | ||||
| 		<a href="{{ac.pdf.url}}"><img id="azad_obrazek" src="{{ac.titulka_nahled.url}}" alt=Titulní strana {{ac.poradi}}. čísla></a> | ||||
| 	{% endif %} | ||||
| 
 | ||||
| 	{% if ac.pdf %} | ||||
| 		<h3>Aktuální témata najdete v <a href="{{ac.pdf.url}}">aktuálním čísle v PDF</a>.</h3> | ||||
| 	{% endif %} | ||||
| 		<!--Toto jsem zakomentoval, aby se tam nezobrazovala temata, ale text, že vše najdou pouze v PDF--> | ||||
| 		{% if False %}	 | ||||
| 			{% for sada in jednorazove_problemy %} | ||||
| 				{# podnadpisy, kdyz neni zakomentuje se nadpis #} | ||||
| 				{% if not sada %}<!--{% endif %} | ||||
| 				<h2>{% cycle 'Úlohy' 'Seriál' %}</h2> | ||||
| 				{% if not sada %}-->{% endif %} | ||||
| 				{# publikace jednotlivych zadani #} | ||||
| 				{% for problem in sada %} | ||||
| 					{% for tag in problem.zamereni.names %} | ||||
| 						 <a name="zam_{{tag}}"></a> | ||||
| 					{% endfor %} | ||||
| 
 | ||||
| 					{# TODO použít {{problem.kod_v_rocniku}} ? vrací 4.u1 místo 4.1 #} | ||||
| 					<h3>{{problem.cislo_zadani.cislo}}.{{problem.kod}} {{problem.nazev}} {{ problem.body_v_zavorce }}</h3> | ||||
| 					{% autoescape off %}{{problem.text_zadani}}{% endautoescape %} | ||||
| 					<hr> | ||||
| 				{% endfor %} | ||||
| 			{% empty %} | ||||
| 				Aktuálně nejsou zadané žádné úlohy k řešení. | ||||
| 			{% endfor %} | ||||
| 		{% endif %} | ||||
| 	{% if user.je_org and not verejne%}</div>{% endif %} | ||||
| 
 | ||||
| {% if user.je_org and not verejne%}</div>{% endif %} | ||||
| {% else %} | ||||
| 	<h2>Aktuálně nejsou zveřejněny žádné úlohy</h2> | ||||
| {% endif %} | ||||
| 
 | ||||
| 
 | ||||
| 	{% if False %}	 | ||||
|     <h2>Témata</h2> | ||||
|       <ul> | ||||
|       {% for problem in temata %} | ||||
|         {# TODO použít {{problem.kod_v_rocniku}} ? vrací t4 místo 4 #} | ||||
| 	<li> | ||||
|         <a href="{{problem.verejne_url}}">Téma {{problem.kod}}: {{problem.nazev}}</a> | ||||
| 	</li> | ||||
|       {% empty %} | ||||
| 				{% if ac.pdf %} | ||||
| 					<p>Aktuální témata najdete v <a href="{{ac.pdf.url}}">aktuálním čísle v PDF</a>.</p> | ||||
| 				{% else %} | ||||
| 					<p>Aktuálně nemáme žádná témata.</p> | ||||
| 				{% endif %} | ||||
|       {% endfor %} | ||||
|       </ul> | ||||
| 	{% endif %} | ||||
| 
 | ||||
| {% endwith %} | ||||
| 
 | ||||
| </div> | ||||
|  |  | |||
|  | @ -581,7 +581,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | |||
| 		rocnik_temata.append(letosni_temata) | ||||
| 	return rocnik_temata | ||||
| 
 | ||||
| def gen_ulohy_tematu(rnd, organizatori, tema, kod, cislo, cislo_se_vzorakem): | ||||
| def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzorakem): | ||||
| 	""" Generování úlohy k danému tématu. """ | ||||
| 	 | ||||
| 	# Proměnné pro náhodné generování názvů a zadání. | ||||
|  | @ -627,10 +627,14 @@ def gen_ulohy_tematu(rnd, organizatori, tema, kod, cislo, cislo_se_vzorakem): | |||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) | ||||
| 	uloha.ulohazadaninode = uloha_zadani | ||||
| 
 | ||||
| 	# Generování řešení a hodnocení k úloze | ||||
| 	gen_reseni_ulohy(rnd, [cislo], uloha, len(resitele)//4, 1, | ||||
| 					 resitele, resitele) | ||||
| 
 | ||||
| 	return uloha, uloha_zadani | ||||
| 
 | ||||
| 
 | ||||
| def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori): | ||||
| def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele): | ||||
| 	logger.info('Generuji úlohy k tématům...') | ||||
| 
 | ||||
| 	# Ke každému ročníku si vezmeme příslušná čísla a témata | ||||
|  | @ -663,7 +667,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) | |||
| 					 | ||||
| 				# Generujeme 1 až 4 úložky k tomuto témátku do tohoto čísla. | ||||
| 				for kod in range(1, rnd.randint(1, 4)): | ||||
| 					u, uz = gen_ulohy_tematu(rnd, organizatori, tema, kod,  | ||||
| 					u, uz = gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, | ||||
| 						cislo, cislo_se_vzorakem) | ||||
| 
 | ||||
| 					insert_last_child(tema_node, uz) | ||||
|  | @ -860,7 +864,7 @@ def create_test_data(size = 6, rnd = None): | |||
| 		"MFI", 8) | ||||
| 
 | ||||
| 	# generování úloh k tématům ve všech číslech | ||||
| 	gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) | ||||
| 	gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele) | ||||
| 
 | ||||
| 	#generování soustředění | ||||
| 	soustredeni = gen_soustredeni(rnd, resitele, organizatori) | ||||
|  |  | |||
|  | @ -217,6 +217,17 @@ def get_prev_node_of_type(node, type): | |||
| 			return current | ||||
| 	return None | ||||
| 
 | ||||
| def get_upper_node_of_type(node, type): | ||||
| 	# vrací první vyšší node daného typu (ignoruje sourozence) | ||||
| 	if node is None: | ||||
| 		return | ||||
| 	current = node | ||||
| 	while get_parent(current) is not None: | ||||
| 		current = get_parent(current) | ||||
| 		if isinstance(current, type): | ||||
| 			return current | ||||
| 	return None | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # Exception, kterou některé metody při špatném použití mohou házet | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| from django.urls import path, include, re_path | ||||
| from django.contrib.auth.decorators import login_required | ||||
| from . import views, export | ||||
| from .utils import org_required, resitel_required | ||||
| from .utils import org_required, resitel_required, viewMethodSwitch | ||||
| from django.views.generic.base import RedirectView | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|  | @ -9,25 +9,25 @@ urlpatterns = [ | |||
| #	path('<int:rocnik>/t<int:tematko>/', views.TematkoView), | ||||
| 
 | ||||
| 	# Organizatori | ||||
| 	path('co-je-MaM/organizatori/', views.CojemamOrganizatoriView.as_view(), name='organizatori'), | ||||
| 	path('co-je-MaM/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'), | ||||
| 	path('o-nas/organizatori/', views.CojemamOrganizatoriView.as_view(), name='organizatori'), | ||||
| 	path('o-nas/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'), | ||||
| 
 | ||||
| 	# Archiv | ||||
| 	path('archiv/rocniky/', views.ArchivView.as_view(), name="seninar_archiv_rocniky"), | ||||
| 	path('archiv/temata/', views.ArchivTemataView.as_view(), name="seninar_archiv_temata"), | ||||
| 	path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"), | ||||
| 	path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"), | ||||
| 
 | ||||
| 	path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'), | ||||
| 	path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), | ||||
| 	path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'), | ||||
| 	path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'), | ||||
| 	path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), | ||||
| 	path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='seminar_textnode_web'), | ||||
| 	path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'), | ||||
| 	path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), | ||||
| 	path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), | ||||
| 	path('treenode/editor/podvesit/<int:pk>/<str:kam>/', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), | ||||
| 	path('treenode/editor/prohodit/<int:pk>/', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), | ||||
| 	path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), | ||||
| 	#path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'), | ||||
| 	#path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), | ||||
| 	#path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='seminar_textnode_web'), | ||||
| 	#path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'), | ||||
| 	#path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), | ||||
| 	#path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), | ||||
| 	#path('treenode/editor/podvesit/<int:pk>/<str:kam>/', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), | ||||
| 	#path('treenode/editor/prohodit/<int:pk>/', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), | ||||
| 	#path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), | ||||
| 	#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), | ||||
| 
 | ||||
| 	# Soustredeni | ||||
|  | @ -57,13 +57,14 @@ urlpatterns = [ | |||
| 	), | ||||
| 
 | ||||
| 	# Zadani | ||||
| 	path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), | ||||
| 	path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), | ||||
| 	#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), | ||||
| #	path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), | ||||
| 	path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), | ||||
| 	#path('aktualni/temata/', views.ZadaniTemataView, name='seminar_temata'), | ||||
| 	path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_aktualni_vysledky'), | ||||
| 	path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), | ||||
| 
 | ||||
| 	# Clanky | ||||
| 	path('clanky/resitel/', views.ClankyResitelView.as_view(), name='clanky_resitel'), | ||||
| 	path('archiv/clanky/', views.ClankyResitelView.as_view(), name='clanky_resitel'), | ||||
| 	#path('clanky/org/', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'), | ||||
| 
 | ||||
| 	# Aesop | ||||
|  | @ -123,11 +124,6 @@ urlpatterns = [ | |||
| 		org_required(views.soustredeniObalkyView), | ||||
| 		name='seminar_soustredeni_obalky' | ||||
| 	), | ||||
| 	path( | ||||
| 		'org/vloz_body/<int:tema>/', | ||||
| 		org_required(views.VlozBodyView.as_view()), | ||||
| 		name='seminar_org_vlozbody' | ||||
| 	), | ||||
| 	# příprava na nestatický orgorozcestník | ||||
| 	path( | ||||
| 		'org/rozcestnik/', | ||||
|  | @ -136,16 +132,16 @@ urlpatterns = [ | |||
| 	), | ||||
| 
 | ||||
| 	path('prihlaska/',views.prihlaskaView, name='seminar_prihlaska'), | ||||
| 	path('login/', views.LoginView.as_view(), name='login'), | ||||
| 	path('logout/', views.LogoutView.as_view(), name='logout'), | ||||
| 	path('prihlasit/', views.LoginView.as_view(), name='login'), | ||||
| 	path('odhlasit/', views.LogoutView.as_view(), name='logout'), | ||||
| 	path('resitel/', resitel_required(views.ResitelView.as_view()), name='seminar_resitel'), | ||||
| 	path('reset_password/', views.PasswordResetView.as_view(), name='reset_password'), | ||||
| 	path('change_password/', views.PasswordChangeView.as_view(), name='change_password'), | ||||
| 	path('reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), | ||||
| 	path('reset_password_confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), | ||||
| 	path('reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'), | ||||
| 	path('reset-hesla/', views.PasswordResetView.as_view(), name='reset_password'), | ||||
| 	path('zmena-hesla/', views.PasswordChangeView.as_view(), name='change_password'), | ||||
| 	path('reset-hesla/2/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), | ||||
| 	path('reset-hesla/potvrzeni/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), | ||||
| 	path('reset-hesla/hotovo/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'), | ||||
| 	path( | ||||
| 		'resitel_edit', | ||||
| 		'resitel/osobni-udaje/', | ||||
| 		login_required(views.resitelEditView, login_url='/login/'), | ||||
| 		name='seminar_resitel_edit' | ||||
| 	), | ||||
|  | @ -154,9 +150,9 @@ urlpatterns = [ | |||
| 	path('profil/', views.profilView, name='profil'), | ||||
| 
 | ||||
| 	# Autocomplete | ||||
| 	path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), | ||||
| 	path('autocomplete/resitel/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'), | ||||
| 	path('autocomplete/problem/odevzdatelny',views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), | ||||
| 	path('api/autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), | ||||
| 	path('api/autocomplete/resitel/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'), | ||||
| 	path('api/autocomplete/problem/odevzdatelny',views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), | ||||
| 
 | ||||
| 	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'), | ||||
|  | @ -174,8 +170,7 @@ urlpatterns = [ | |||
| 
 | ||||
| 	path('temp/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), | ||||
| 	path('temp/reseni/<int:problem>/<int:resitel>/', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'), | ||||
| 	path('temp/reseni/<int:pk>', org_required(views.DetailReseniView.as_view()), name='odevzdavatko_detail_reseni'), | ||||
| 	path('temp/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), | ||||
| 	path('temp/reseni/all', org_required(views.SeznamReseniView.as_view())), | ||||
| 	path('temp/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), | ||||
| 
 | ||||
| ] | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import datetime | |||
| from django.contrib.auth import get_user_model | ||||
| from django.contrib.auth.decorators import permission_required | ||||
| from html.parser import HTMLParser | ||||
| from django import views as DjangoViews | ||||
| 
 | ||||
| from django.contrib.auth.models import AnonymousUser | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
|  | @ -16,10 +17,11 @@ import seminar.treelib as t | |||
| org_required = permission_required('auth.org', raise_exception=True) | ||||
| resitel_required = permission_required('auth.resitel', raise_exception=True) | ||||
| User = get_user_model() | ||||
| User.je_org = lambda self: self.has_perm('auth.org') | ||||
| User.je_resitel = lambda self: self.has_perm('auth.resitel') | ||||
| AnonymousUser.je_org = lambda self: False | ||||
| AnonymousUser.je_resitel = lambda self: False | ||||
| # Není to úplně hezké, ale budeme doufat, že to je funkční... | ||||
| User.je_org = property(lambda self: self.has_perm('auth.org')) | ||||
| User.je_resitel = property(lambda self: self.has_perm('auth.resitel')) | ||||
| AnonymousUser.je_org = False | ||||
| AnonymousUser.je_resitel = False | ||||
| 
 | ||||
| 
 | ||||
| class FirstTagParser(HTMLParser): | ||||
|  | @ -191,3 +193,92 @@ def aktivniResitele(cislo, pouze_letosni=False): | |||
| 	else: | ||||
| 		# spojíme querysety s řešiteli loni a letos do daného čísla | ||||
| 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct() | ||||
| 
 | ||||
| def viewMethodSwitch(get, post): | ||||
| 	""" | ||||
| 	Vrátí view, který zavolá různé jiné views podle toho, kterou metodou je zavolán. | ||||
| 
 | ||||
| 	Inspirováno https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#an-alternative-better-solution, jen jsem to udělal genericky. | ||||
| 
 | ||||
| 	Parametry: | ||||
| 		post	view pro metodu POST | ||||
| 		get	view pro metodu GET | ||||
| 	 | ||||
| 	V obou případech se míní už view jakožto funkce, takže u class-based views se už má použít .as_view() | ||||
| 
 | ||||
| 	TODO: Podpora i pro metodu HEAD? A možná i pro FILES? | ||||
| 	""" | ||||
| 
 | ||||
| 	theGetView = get | ||||
| 	thePostView = post | ||||
| 
 | ||||
| 	class NewView(DjangoViews.View): | ||||
| 		def get(self, request, *args, **kwargs): | ||||
| 			return theGetView(request, *args, **kwargs) | ||||
| 		def post(self, request, *args, **kwargs): | ||||
| 			return thePostView(request, *args, **kwargs) | ||||
| 	 | ||||
| 	return NewView.as_view() | ||||
| 
 | ||||
| def cisla_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" | ||||
| 	Vrátí všechna čísla daného ročníku. | ||||
| 	Parametry: | ||||
| 		rocnik (Rocnik): ročník semináře | ||||
| 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | ||||
| 	Vrátí: | ||||
| 		seznam objektů typu Cislo | ||||
| 	"""	 | ||||
| 	if jen_verejne: | ||||
| 		return rocnik.verejna_cisla() | ||||
| 	else: | ||||
| 		return rocnik.cisla.all() | ||||
| 
 | ||||
| def hlavni_problem(problem): | ||||
| 	""" Pro daný problém vrátí jeho nejvyšší nadproblém.""" | ||||
| 	while not(problem.nadproblem == None): | ||||
| 		problem = problem.nadproblem | ||||
| 	return problem | ||||
| 
 | ||||
| def problemy_rocniku(rocnik, jen_verejne=True): | ||||
| 	return  m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(cislo_body__in = cisla_rocniku(rocnik, jen_verejne))).distinct().select_related('nadproblem').select_related('nadproblem__nadproblem') | ||||
| 
 | ||||
| def problemy_cisla(cislo): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle. """ | ||||
| 	return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(cislo_body = cislo)).distinct().select_related('nadproblem').select_related('nadproblem__nadproblem') | ||||
| 
 | ||||
| 
 | ||||
| def hlavni_problemy_f(problemy=None): | ||||
| 	""" Vrátí seznam všech problémů, které již nemají nadproblém. """ | ||||
| 	# hlavní problémy čísla  | ||||
| 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
| 	hlavni_problemy = set() | ||||
| 	for p in problemy: | ||||
| 		hlavni_problemy.add(hlavni_problem(p)) | ||||
| 		 | ||||
| 	# zunikátnění | ||||
| 	hlavni_problemy = list(hlavni_problemy) | ||||
| 	hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku) # setřídit podle t1, t2, c3, ... | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| 
 | ||||
| def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle v poli 'indexovaném' tématy. """ | ||||
| 	if problemy is None: | ||||
| 		problemy = problemy_cisla(cislo) | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_f(problemy) | ||||
| 
 | ||||
| 	podproblemy = dict((hp.id, []) for hp in hlavni_problemy) | ||||
| 	hlavni_problemy = set(hlavni_problemy) | ||||
| 	podproblemy[-1] = [] | ||||
| 
 | ||||
| 	for problem in problemy: | ||||
| 		h_problem = hlavni_problem(problem) | ||||
| 		if h_problem in hlavni_problemy: | ||||
| 			podproblemy[h_problem.id].append(problem) | ||||
| 		else: | ||||
| 			podproblemy[-1].append(problem) | ||||
| 
 | ||||
| 	return podproblemy | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| from dal import autocomplete | ||||
| from django.shortcuts import get_object_or_404 | ||||
| from django.db.models import Q | ||||
| 
 | ||||
| import seminar.models as m | ||||
| from .helpers import LoginRequiredAjaxMixin | ||||
|  |  | |||
|  | @ -1,12 +1,22 @@ | |||
| from django.views.generic import ListView, DetailView | ||||
| from django.views.generic.base import TemplateView | ||||
| from django.views.generic import ListView, DetailView, FormView | ||||
| from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin | ||||
| from django.views.generic.base import View | ||||
| from django.views.generic.detail import SingleObjectMixin | ||||
| from django.shortcuts import redirect, get_object_or_404 | ||||
| from django.urls import reverse | ||||
| from django.db import transaction | ||||
| 
 | ||||
| from dataclasses import dataclass | ||||
| import datetime | ||||
| import logging | ||||
| 
 | ||||
| import seminar.models as m | ||||
| import seminar.forms as f | ||||
| from seminar.forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| # Co chceme? | ||||
| # - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení | ||||
| # 	- TabulkaOdevzdanychReseniView | ||||
|  | @ -30,26 +40,60 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 	template_name = 'seminar/odevzdavatko/tabulka.html' | ||||
| 	model = m.Hodnoceni | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení. | ||||
| 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | ||||
| 		self.resitele = resi_v_rocniku(self.akt_rocnik) | ||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | ||||
| 		self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() | ||||
| 	def inicializuj_osy_tabulky(self): | ||||
| 		"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" | ||||
| 		# FIXME: jméno metody není vypovídající... | ||||
| 		# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat | ||||
| 		# TODO: Prefetches, Select related, ... | ||||
| 		self.resitele = m.Resitel.objects.all() | ||||
| 		self.problemy = m.Problem.objects.all() | ||||
| 		self.reseni = m.Reseni.objects.all() | ||||
| 
 | ||||
| 		form = FiltrForm(self.request.GET) | ||||
| 		if form.is_valid(): | ||||
| 			fcd = form.cleaned_data | ||||
| 			resitele = fcd["resitele"] | ||||
| 			problemy = fcd["problemy"] | ||||
| 			reseni_od = fcd["reseni_od"] | ||||
| 			reseni_do = fcd["reseni_do"] | ||||
| 		else: | ||||
| 			resitele = FiltrForm.get_initial_for_field(FormFiltr.resitele, "resitele") | ||||
| 			problemy = FiltrForm.get_initial_for_field(FormFiltr.problemy, "problemy") | ||||
| 			resitele_od = FiltrForm.get_initial_for_field(FormFiltr.resitele_od, "resitele_od") | ||||
| 			resitele_do = FiltrForm.get_initial_for_field(FormFiltr.resitele_do, "resitele_do") | ||||
| 			 | ||||
| 
 | ||||
| 		# Filtrujeme! | ||||
| 		aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci | ||||
| 		if resitele == FiltrForm.RESITELE_RELEVANTNI: | ||||
| 			logger.warning("Někdo chtěl v tabulce jen relevantní řešitele a měl smůlu :-(") | ||||
| 			resitele = FiltrForm.RESITELE_LETOSNI # Fall-through | ||||
| 		elif resitele == FiltrForm.RESITELE_LETOSNI: | ||||
| 			self.resitele = resi_v_rocniku(aktualni_rocnik) | ||||
| 
 | ||||
| 		if problemy == FiltrForm.PROBLEMY_MOJE: | ||||
| 			org = m.Organizator.objects.get(osoba__user=self.request.user) | ||||
| 			from django.db.models import Q | ||||
| 			self.problemy = self.problemy.filter(Q(autor=org)|Q(garant=org)|Q(opravovatele=org), stav=m.Problem.STAV_ZADANY) | ||||
| 		elif problemy == FiltrForm.PROBLEMY_LETOSNI: | ||||
| 			self.problemy = self.problemy.filter(stav=m.Problem.STAV_ZADANY) | ||||
| 			#self.problemy = list(filter(lambda problem: problem.rocnik() == aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník.... | ||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | ||||
| 		self.problemy = self.problemy.non_polymorphic() | ||||
| 
 | ||||
| 		self.reseni = self.reseni.filter(cas_doruceni__date__gte=reseni_od, cas_doruceni__date__lte=reseni_do) | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		self.inicializuj_osy_tabulky() | ||||
| 		qs = super().get_queryset() | ||||
| 		qs = qs.filter(problem__in=self.zadane_problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') | ||||
| 		qs = qs.filter(problem__in=self.problemy, reseni__in=self.reseni, reseni__resitele__in=self.resitele).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') | ||||
| 		return qs | ||||
| 
 | ||||
| 	def get_context_data(self, *args, **kwargs): | ||||
| 		# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení. | ||||
| 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | ||||
| 		self.resitele = resi_v_rocniku(self.akt_rocnik) | ||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | ||||
| 		self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() | ||||
| 		# self.resitele, self.reseni a self.problemy jsou již nastavené | ||||
| 
 | ||||
| 		ctx = super().get_context_data(*args, **kwargs) | ||||
| 		ctx['problemy'] = self.zadane_problemy | ||||
| 		ctx['problemy'] = self.problemy | ||||
| 		ctx['resitele'] = self.resitele | ||||
| 		tabulka = dict() | ||||
| 
 | ||||
|  | @ -76,7 +120,7 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 		hodnoty = [] | ||||
| 		for resitel in self.resitele: | ||||
| 			resiteluv_radek = [] | ||||
| 			for problem in self.zadane_problemy: | ||||
| 			for problem in self.problemy: | ||||
| 				if problem in tabulka and resitel in tabulka[problem]: | ||||
| 					resiteluv_radek.append(tabulka[problem][resitel]) | ||||
| 				else: | ||||
|  | @ -84,9 +128,14 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 			hodnoty.append(resiteluv_radek) | ||||
| 		ctx['radky'] = list(zip(self.resitele, hodnoty)) | ||||
| 
 | ||||
| 		ctx['filtr'] = FiltrForm(initial=self.request.GET) | ||||
| 		# Pro použití hacku na automatické {{form.media}} v template: | ||||
| 		ctx['form'] = ctx['filtr'] | ||||
| 
 | ||||
| 		return ctx | ||||
| 
 | ||||
| class ReseniProblemuView(ListView): | ||||
| # Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? | ||||
| class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View): | ||||
| 	model = m.Reseni | ||||
| 	template_name = 'seminar/odevzdavatko/seznam.html' | ||||
| 	 | ||||
|  | @ -107,12 +156,73 @@ class ReseniProblemuView(ListView): | |||
| 			) | ||||
| 		return qs | ||||
| 	 | ||||
| 	def get(self, request, *args, **kwargs): | ||||
| 		self.object_list = self.get_queryset() | ||||
| 		if self.object_list.count() == 1: | ||||
| 			jedine_reseni = self.object_list.first() | ||||
| 			return redirect(reverse("odevzdavatko_detail_reseni", kwargs={"pk": jedine_reseni.id})) | ||||
| 		context = self.get_context_data() | ||||
| 		return self.render_to_response(context) | ||||
| 	# Kontext automaticky? | ||||
| 
 | ||||
| ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex | ||||
| class DetailReseniView(DetailView): | ||||
| 	model = m.Reseni | ||||
| 	template_name = 'seminar/odevzdavatko/detail.html' | ||||
| 	# To je všechno? Najde se to podle pk... | ||||
| 	 | ||||
| 	def aktualni_hodnoceni(self): | ||||
| 		reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | ||||
| 		result = [] # Slovníky s klíči problem, body, cislo_body -- initial data pro f.OhodnoceniReseniFormSet | ||||
| 		for hodn in m.Hodnoceni.objects.filter(reseni=reseni): | ||||
| 			result.append( | ||||
| 				{"problem": hodn.problem,  | ||||
| 				"body": hodn.body, | ||||
| 				"cislo_body": hodn.cislo_body, | ||||
| 				}) | ||||
| 		return result | ||||
| 
 | ||||
| 	def get_context_data(self, **kw): | ||||
| 		ctx = super().get_context_data(**kw) | ||||
| 		ctx['form'] = f.OhodnoceniReseniFormSet( | ||||
| 				initial = self.aktualni_hodnoceni() | ||||
| 				) | ||||
| 		return ctx | ||||
| 
 | ||||
| 
 | ||||
| def hodnoceniReseniView(request, pk, *args, **kwargs): | ||||
| 	reseni = get_object_or_404(m.Reseni, pk=pk) | ||||
| 	template_name = 'seminar/odevzdavatko/detail.html' | ||||
| 	form_class = f.OhodnoceniReseniFormSet | ||||
| 	success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) | ||||
| 
 | ||||
| 	# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově | ||||
| 	# Also: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#django.forms.ModelForm | ||||
| 	formset = f.OhodnoceniReseniFormSet(request.POST) | ||||
| 	# TODO: Napsat validaci formuláře a formsetu | ||||
| 	# TODO: Implementovat větev, kdy formulář validní není. | ||||
| 	if formset.is_valid(): | ||||
| 		with transaction.atomic(): | ||||
| 			# Smažeme všechna dosavadní hodnocení tohoto řešení | ||||
| 			qs = m.Hodnoceni.objects.filter(reseni=reseni) | ||||
| 			logger.info(f"Will delete {qs.count()} objects: {qs}") | ||||
| 			qs.delete() | ||||
| 			 | ||||
| 			# Vyrobíme nová podle formsetu | ||||
| 			for form in formset: | ||||
| 				problem = form.cleaned_data['problem'] | ||||
| 				body = form.cleaned_data['body'] | ||||
| 				cislo_body = form.cleaned_data['cislo_body'] | ||||
| 				hodnoceni = m.Hodnoceni( | ||||
| 					problem=problem, | ||||
| 					body=body, | ||||
| 					cislo_body=cislo_body, | ||||
| 					reseni=reseni, | ||||
| 					) | ||||
| 				logger.info(f"Creating Hodnoceni: {hodnoceni}") | ||||
| 				hodnoceni.save() | ||||
| 
 | ||||
| 	return redirect(success_url) | ||||
| 
 | ||||
| 
 | ||||
| # Přehled všech řešení kvůli debugování | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | |||
| import seminar.forms as f | ||||
| import seminar.templatetags.treenodes as tnltt | ||||
| import seminar.views.views_rest as vr | ||||
| from seminar.views.vysledkovka import vysledkovka_rocniku, vysledkovka_cisla, body_resitelu | ||||
| 
 | ||||
| from datetime import timedelta, date, datetime, MAXYEAR | ||||
| from django.utils import timezone | ||||
|  | @ -48,7 +49,7 @@ import csv | |||
| import logging | ||||
| import time | ||||
| 
 | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku, problemy_rocniku, cisla_rocniku, hlavni_problemy_f | ||||
| 
 | ||||
| # ze starého modelu | ||||
| #def verejna_temata(rocnik): | ||||
|  | @ -63,20 +64,6 @@ from seminar.utils import aktivniResitele, resi_v_rocniku | |||
| def get_problemy_k_tematu(tema): | ||||
| 	return Problem.objects.filter(nadproblem = tema) | ||||
| 
 | ||||
| 
 | ||||
| class VlozBodyView(generic.ListView): | ||||
| 	template_name = 'seminar/org/vloz_body.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		self.tema = get_object_or_404(Problem,id=self.kwargs['tema']) | ||||
| 		print(self.tema) | ||||
| 		self.problemy = Problem.objects.filter(nadproblem = self.tema) | ||||
| 		print(self.problemy) | ||||
| 		self.reseni = Reseni.objects.filter(problem__in=self.problemy)	 | ||||
| 		print(self.reseni) | ||||
| 		return self.reseni | ||||
| 
 | ||||
| 
 | ||||
| class ObalkovaniView(generic.ListView): | ||||
| 	template_name = 'seminar/org/obalkovani.html' | ||||
| 
 | ||||
|  | @ -198,7 +185,7 @@ class TNLData(object): | |||
| 			return [cls.from_treenode(treenode)] | ||||
| 		else: | ||||
| 			found = [] | ||||
| 			for tn in all_children(treenode): | ||||
| 			for tn in treelib.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: | ||||
|  | @ -397,8 +384,8 @@ class ProblemView(generic.DetailView): | |||
| 		return context | ||||
| 
 | ||||
| 
 | ||||
| class AktualniZadaniView(generic.TemplateView): | ||||
| 	template_name = 'seminar/treenode.html' | ||||
| #class AktualniZadaniView(generic.TemplateView): | ||||
| #	template_name = 'seminar/treenode.html' | ||||
| 
 | ||||
| # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | ||||
| #class AktualniZadaniView(TreeNodeView): | ||||
|  | @ -413,21 +400,15 @@ class AktualniZadaniView(generic.TemplateView): | |||
| #		context['verejne'] = verejne	 | ||||
| #		return context | ||||
| 
 | ||||
| #def AktualniZadaniView(request): | ||||
| #	nastaveni = get_object_or_404(Nastaveni) | ||||
| #	verejne = nastaveni.aktualni_cislo.verejne() | ||||
| #	problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany') | ||||
| #	ulohy = problemy.filter(typ = 'uloha').order_by('kod') | ||||
| #	serialy = problemy.filter(typ = 'serial').order_by('kod') | ||||
| #	jednorazove_problemy = [ulohy, serialy] | ||||
| #	return render(request, 'seminar/zadani/AktualniZadani.html', | ||||
| #			{'nastaveni': nastaveni, | ||||
| #			 'jednorazove_problemy': jednorazove_problemy, | ||||
| #			 'temata': verejna_temata(nastaveni.aktualni_rocnik), | ||||
| #			 'verejne': verejne, | ||||
| #				}, | ||||
| #			) | ||||
| # | ||||
| def AktualniZadaniView(request): | ||||
| 	nastaveni = get_object_or_404(Nastaveni) | ||||
| 	verejne = nastaveni.aktualni_cislo.verejne() | ||||
| 	return render(request, 'seminar/zadani/AktualniZadani.html', | ||||
| 			{'nastaveni': nastaveni, | ||||
| 			 'verejne': verejne, | ||||
| 				}, | ||||
| 			) | ||||
| 
 | ||||
| def ZadaniTemataView(request): | ||||
| 	nastaveni = get_object_or_404(Nastaveni) | ||||
| 	verejne = nastaveni.aktualni_cislo.verejne() | ||||
|  | @ -506,29 +487,32 @@ def ZadaniTemataView(request): | |||
| #	return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) | ||||
| #	 | ||||
| 
 | ||||
| #def ZadaniAktualniVysledkovkaView(request): | ||||
| #	nastaveni = get_object_or_404(Nastaveni) | ||||
| #	# Aktualni verejna vysledkovka | ||||
| #	vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik) | ||||
| #	# kdyz neni verejna vysledkovka, tak zobraz starou | ||||
| #	if not vysledkovka: | ||||
| #		try: | ||||
| #			minuly_rocnik = Rocnik.objects.get( | ||||
| #				prvni_rok=(nastaveni.aktualni_rocnik.prvni_rok-1)) | ||||
| #			vysledkovka = vysledkovka_rocniku(minuly_rocnik) | ||||
| #		except ObjectDoesNotExist: | ||||
| #			pass | ||||
| #	# vysledkovka s neverejnyma vysledkama | ||||
| #	vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False) | ||||
| #	return render( | ||||
| #		request, | ||||
| #		'seminar/zadani/AktualniVysledkovka.html', | ||||
| #		{ | ||||
| #			'nastaveni': nastaveni, | ||||
| #			'vysledkovka': vysledkovka, | ||||
| #			'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi, | ||||
| #		} | ||||
| #	) | ||||
| def ZadaniAktualniVysledkovkaView(request): | ||||
| 	nastaveni = get_object_or_404(Nastaveni) | ||||
| 	# Aktualni verejna vysledkovka | ||||
| 	vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik) | ||||
| 	cisla = cisla_rocniku(nastaveni.aktualni_rocnik) | ||||
| 	# kdyz neni verejna vysledkovka, tak zobraz starou | ||||
| 	if not vysledkovka: | ||||
| 		try: | ||||
| 			minuly_rocnik = Rocnik.objects.get( | ||||
| 				prvni_rok=(nastaveni.aktualni_rocnik.prvni_rok-1)) | ||||
| 			vysledkovka = vysledkovka_rocniku(minuly_rocnik) | ||||
| 			cisla = cisla_rocniku(minuly_rocnik) | ||||
| 		except ObjectDoesNotExist: | ||||
| 			pass | ||||
| 	# vysledkovka s neverejnyma vysledkama | ||||
| 	vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False) | ||||
| 	return render( | ||||
| 		request, | ||||
| 		'seminar/zadani/AktualniVysledkovka.html', | ||||
| 		{ | ||||
| 			'nastaveni': nastaveni, | ||||
| 			'radky_vysledkovky': vysledkovka, | ||||
| 			'cisla': cisla, | ||||
| 			'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi, | ||||
| 		} | ||||
| 	) | ||||
| 
 | ||||
| 
 | ||||
| ### Titulni strana | ||||
|  | @ -568,6 +552,8 @@ class TitulniStranaView(generic.ListView): | |||
| 
 | ||||
| 		try: | ||||
| 			nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0] | ||||
| 			if nejblizsi_deadline[0] == deadline_soustredeni[0]: | ||||
| 				nejblizsi_deadline = deadline_soustredeni | ||||
| 		except IndexError: | ||||
| 			nejblizsi_deadline = (None, None) # neni zadna aktualni deadline | ||||
| 
 | ||||
|  | @ -649,235 +635,8 @@ class ArchivView(generic.ListView): | |||
| 		 | ||||
| 		return context | ||||
| 
 | ||||
| ### Výsledky | ||||
| 
 | ||||
| def sloupec_s_poradim(setrizene_body): | ||||
| 	""" | ||||
| 	Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
| 	vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.), | ||||
| 	podle toho, jak jdou za sebou ve výsledkovce. | ||||
| 	Parametr: | ||||
| 		setrizene_body (seznam integerů): sestupně setřízená čísla | ||||
| 
 | ||||
| 	Výstup: | ||||
| 		sloupec_s_poradim (seznam stringů) | ||||
| 	""" | ||||
| 
 | ||||
| 	# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím	 | ||||
| 	aktualni_poradi = 1 | ||||
| 	sloupec_s_poradim = [] | ||||
| 
 | ||||
| 	# seskupíme seznam všech bodů podle hodnot | ||||
| 	for index in range(0, len(setrizene_body)): | ||||
| 		# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme | ||||
| 		# vypsat už jen prázdné místo, než dojdeme na správný řádek | ||||
| 		if (index + 1) < aktualni_poradi: | ||||
| 			sloupec_s_poradim.append("") | ||||
| 			continue | ||||
| 		velikost_skupiny = 0 | ||||
| 		# zjistíme počet po sobě jdoucích stejných hodnot | ||||
| 		while setrizene_body[index] == setrizene_body[index + velikost_skupiny]: | ||||
| 			velikost_skupiny = velikost_skupiny + 1 | ||||
| 			# na konci musíme ošetřit přetečení seznamu | ||||
| 			if (index + velikost_skupiny) > len(setrizene_body) - 1: | ||||
| 				break | ||||
| 		# pokud je velikost skupiny 1, vypíšu pořadí | ||||
| 		if velikost_skupiny == 1: | ||||
| 			sloupec_s_poradim.append("{}.".format(aktualni_poradi)) | ||||
| 		# pokud je skupina větší, vypíšu rozsah | ||||
| 		else: | ||||
| 			sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,  | ||||
| 						aktualni_poradi+velikost_skupiny-1)) | ||||
| 		# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno | ||||
| 		aktualni_poradi = aktualni_poradi + velikost_skupiny | ||||
| 	return sloupec_s_poradim | ||||
| 
 | ||||
| def cisla_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" | ||||
| 	Vrátí všechna čísla daného ročníku. | ||||
| 	Parametry: | ||||
| 		rocnik (Rocnik): ročník semináře | ||||
| 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | ||||
| 	Vrátí: | ||||
| 		seznam objektů typu Cislo | ||||
| 	"""	 | ||||
| 	if jen_verejne: | ||||
| 		return rocnik.verejna_cisla() | ||||
| 	else: | ||||
| 		return rocnik.cisla.all() | ||||
| 
 | ||||
| def hlavni_problem(problem): | ||||
| 	""" Pro daný problém vrátí jeho nejvyšší nadproblém.""" | ||||
| 	while not(problem.nadproblem == None): | ||||
| 		problem = problem.nadproblem | ||||
| 	return problem | ||||
| 
 | ||||
| def hlavni_problemy_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Pro zadaný ročník vrátí hlavní problémy ročníku, | ||||
| 	tj. ty, které už nemají nadproblém.""" | ||||
| 	hlavni_problemy = [] | ||||
| 	for cislo in cisla_rocniku(rocnik, jen_verejne): | ||||
| 		for problem in hlavni_problemy_cisla(cislo): | ||||
| 			hlavni_problemy.append(problem) | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| def hlavni_problemy_cisla(cislo): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """ | ||||
| 	hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all()	 | ||||
| 	# hodnocení, která se vážou k danému číslu | ||||
| 	 | ||||
| 	reseni = [h.reseni for h in hodnoceni] | ||||
| 	problemy = [h.problem for h in hodnoceni] | ||||
| 	problemy_set = set(problemy)	# chceme každý problém unikátně, | ||||
| 	problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí | ||||
| 
 | ||||
| 	# hlavní problémy čísla  | ||||
| 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
| 	hlavni_problemy = [] | ||||
| 	for p in problemy: | ||||
| 		hlavni_problemy.append(hlavni_problem(p)) | ||||
| 		 | ||||
| 	# zunikátnění | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ... | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| def body_resitelu(resitele, za, odjakziva=True): | ||||
| 	""" Funkce počítající počty bodů pro zadané řešitele,  | ||||
| 	buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. | ||||
| 	Parametry: | ||||
| 		resitele (seznam obsahující položky typu Resitel): aktivní řešitelé | ||||
| 		za (Rocnik/Cislo): za co se mají počítat body  | ||||
| 			(generování starších výsledkovek) | ||||
| 		odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník | ||||
| 			zadané v "za" | ||||
| 	Výstup: | ||||
| 		slovník (Resitel.id):body | ||||
| 	"""		 | ||||
| 	resitele_id = [r.id for r in resitele] | ||||
| 	# Zjistíme, typ objektu v parametru "za" | ||||
| 	if isinstance(za, Rocnik): | ||||
| 		cislo = None | ||||
| 		rocnik = za | ||||
| 		rok = rocnik.prvni_rok | ||||
| 	elif isinstance(za, Cislo): | ||||
| 		cislo = za | ||||
| 		rocnik = None | ||||
| 		rok = cislo.rocnik.prvni_rok | ||||
| 	else: | ||||
| 		assert True, "body_resitelu: za není ani číslo ani ročník."	 | ||||
| 	 | ||||
| 
 | ||||
| 	# Kvůli rychlosti používáme sčítáme body už v databázi, viz  | ||||
| 	# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,  | ||||
| 	# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky | ||||
| 	# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i  | ||||
| 	# za historická čísla. | ||||
| 
 | ||||
| 	# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,  | ||||
| 	# který se použije ve výsledném dotazu. | ||||
| 	if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla. | ||||
| 		# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,  | ||||
| 		# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen | ||||
| 		# pro čísla s pořadím nejvýše stejným, jako má zadané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) | | ||||
| 				 Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 				   reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif cislo and not odjakziva: # Body se sčítají za dané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 					reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.	 | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok)) | ||||
| 	elif rocnik and not odjakziva: # Spočítáme body za daný ročník. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) | ||||
| 	else:  | ||||
| 		assert True, "body_resitelu: Neplatná kombinace za a odjakživa." | ||||
| 
 | ||||
| 	# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů | ||||
| 	resitele_s_body = Resitel.objects.filter(id__in=resitele_id).annotate( | ||||
| 		body=body_k_zapocteni) | ||||
| 	# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník  | ||||
| 	# indexovaný řešitelským id obsahující body.  | ||||
| 	# Pokud jsou body None, nahradíme za 0. | ||||
| 	slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body} | ||||
| 	return slovnik | ||||
| 
 | ||||
| class RadekVysledkovkyRocniku(object): | ||||
| 	""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): | ||||
| 		self.poradi = poradi | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.body_cisla_sezn = body_cisla_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 
 | ||||
| def setrid_resitele_a_body(slov_resitel_body): | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] | ||||
| 	setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||
| 	setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] | ||||
| 	return setrizeni_resitele_id, setrizeni_resitele, setrizene_body | ||||
| 
 | ||||
| def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||
| 	formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | ||||
| 	""" | ||||
| 	 | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(resi_v_rocniku(rocnik)) | ||||
| 	cisla = cisla_rocniku(rocnik, jen_verejne) | ||||
| 	body_cisla_slov = {} | ||||
| 	for cislo in cisla: | ||||
| 		# získáme body za číslo | ||||
| 		_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) | ||||
| 		body_cisla_slov[cislo.id] = cislobody | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) | ||||
| 
 | ||||
| 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | ||||
| 	setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | ||||
| 	poradi = sloupec_s_poradim(setrizene_body) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# seznam počtu bodů daného řešitele pro jednotlivá čísla | ||||
| 		body_cisla_sezn = [] | ||||
| 		for cislo in cisla: | ||||
| 			body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) | ||||
| 
 | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyRocniku( | ||||
| 			poradi[i], # pořadí | ||||
| 			Resitel.objects.get(id=ar_id), # řešitel (z id) | ||||
| 			body_cisla_sezn, # seznam bodů za čísla | ||||
| 			setrizene_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			rocnik) # ročník semináře pro získání ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	return radky_vysledkovky | ||||
| 
 | ||||
| 
 | ||||
| class RocnikView(generic.DetailView): | ||||
|  | @ -888,29 +647,25 @@ class RocnikView(generic.DetailView): | |||
| 	def get_object(self, queryset=None): | ||||
| 		if queryset is None: | ||||
| 			queryset = self.get_queryset() | ||||
| 		rocnik_arg = self.kwargs.get('rocnik') | ||||
| 		queryset = queryset.filter(rocnik=rocnik_arg) | ||||
| 
 | ||||
| 		try: | ||||
| 			obj = queryset.get() | ||||
| 		except queryset.model.DoesNotExist: | ||||
| 			raise Http404(_("No %(verbose_name)s found matching the query") % | ||||
| 						{'verbose_name': queryset.model._meta.verbose_name}) | ||||
| 		return obj | ||||
| 		return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik')) | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		start = time.time() | ||||
| 		context = super(RocnikView, self).get_context_data(**kwargs) | ||||
| 
 | ||||
| 		# vysledkovka = True zajistí vykreslení, | ||||
| 		# zkontrolovat, kdy se má a nemá vykreslovat | ||||
| 		context['vysledkovka'] = True | ||||
| 		if self.request.user.je_org: | ||||
| 			context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False) | ||||
| 			context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) | ||||
| 			context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_f(problemy_rocniku(context["rocnik"], jen_verejne=False)) | ||||
| 		context['cisla'] = cisla_rocniku(context["rocnik"]) | ||||
| 		context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"]) | ||||
| 		context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku( | ||||
| 			context["rocnik"], jen_verejne=False) | ||||
| 		context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"]) | ||||
| 		context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False) | ||||
| 		context['hlavni_problemy_v_rocniku'] = hlavni_problemy_f(problemy_rocniku(context["rocnik"])) | ||||
| 		end = time.time() | ||||
| 		print("Kontext:", end-start) | ||||
| 
 | ||||
| 		return context | ||||
| 
 | ||||
|  | @ -940,179 +695,6 @@ class ProblemView(generic.DetailView): | |||
| 		return context | ||||
| 
 | ||||
| 
 | ||||
| class RadekVysledkovkyCisla(object): | ||||
| 	"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_problemy_sezn,  | ||||
| 				body_cislo, body_rocnik, body_odjakziva, rok): | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_cislo = body_cislo | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.poradi = poradi | ||||
| 		self.body_problemy_sezn = body_problemy_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 	 | ||||
| 
 | ||||
| def pricti_body(slovnik, resitel, body): | ||||
| 	""" Přiřazuje danému řešiteli body do slovníku. """ | ||||
| 	# testujeme na None (""), pokud je to první řešení  | ||||
| 	# daného řešitele, předěláme na 0 | ||||
| 	# (v dalším kroku přičteme reálný počet bodů), | ||||
| 	# rozlišujeme tím mezi 0 a neodevzdaným řešením | ||||
| 	if slovnik[resitel.id] == "": | ||||
| 		slovnik[resitel.id] = 0 | ||||
| 	 | ||||
| 	slovnik[resitel.id] += body | ||||
| 
 | ||||
| def secti_body_za_rocnik(za, aktivni_resitele): | ||||
| 	""" Spočítá body za ročník (celý nebo do daného čísla),  | ||||
| 		setřídí je sestupně a vrátí jako seznam. | ||||
| 	Parametry: | ||||
| 		za (typu Rocnik nebo Cislo)	spočítá za ročník, nebo za ročník až do  | ||||
| 						daného čísla | ||||
| 	""" | ||||
| 	# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa) | ||||
| 	resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False) | ||||
| 	# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně | ||||
| 	resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), | ||||
| 					key = lambda x: x[1], reverse = True) | ||||
| 	return resitel_rocnikbody_sezn | ||||
| 
 | ||||
| def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): | ||||
| 	""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata).""" | ||||
| 	# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé | ||||
| 	# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém  | ||||
| 	# pro jednotlivé řešitele (slovník slovníků hlavních problémů) | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	def cosi(problem): | ||||
| 		return problem.id | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik = {} | ||||
| 	for hp in temata_a_spol: | ||||
| 		hlavni_problemy_slovnik[hp.id] = {} | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik[-1] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	cislobody = {} | ||||
| 	for ar in aktivni_resitele: | ||||
| 		# řešitele převedeme na řetězec pomocí unikátního id | ||||
| 		cislobody[ar.id] = "" | ||||
| 		for hp in temata_a_spol: | ||||
| 			slovnik = hlavni_problemy_slovnik[hp.id] | ||||
| 			slovnik[ar.id] = "" | ||||
| 
 | ||||
| 		hlavni_problemy_slovnik[-1][ar.id] = "" | ||||
| 	 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele',  | ||||
| 				'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 	 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 		 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in list(reseni.problem.all()): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if ne_clanek_ne_konfera(nadproblem): | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[-1] | ||||
| 			 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in list(reseni.hodnoceni_set.all()): | ||||
| 				body = hodn.body	 | ||||
| 			 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in list(reseni.resitele.all()): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(cislobody, resitel, body) | ||||
| 					pricti_body(nadproblem_slovnik, resitel, body) | ||||
| 	return hlavni_problemy_slovnik, cislobody | ||||
| 
 | ||||
| def vysledkovka_cisla(cislo, context=None): | ||||
| 	if context is None: | ||||
| 		context = {} | ||||
| 	 | ||||
| 	hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo)) | ||||
| 
 | ||||
| 	# získáme body za číslo | ||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo) | ||||
| 
 | ||||
| 	# řešitelé setřídění podle bodů za číslo sestupně | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||
| 			 | ||||
| 	# spočítáme pořadí řešitelů | ||||
| 	setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	poradi = sloupec_s_poradim(setrizeni_resitele_body) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	# def not_empty(value): | ||||
| 	# 	return value != '' | ||||
| 	# | ||||
| 	# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 | ||||
| 
 | ||||
| 	je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 | ||||
| 
 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# získáme seznam bodů za problémy pro daného řešitele | ||||
| 		problemy = [] 		 | ||||
| 		for hp in temata_a_spol: | ||||
| 			problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) | ||||
| 		if je_nejake_ostatni: | ||||
| 			problemy.append(hlavni_problemy_slovnik[-1][ar_id]) | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyCisla( | ||||
| 			poradi[i], # pořadí | ||||
| 			Resitel.objects.get(id=ar_id), # řešitel (z id) | ||||
| 			problemy, # seznam bodů za hlavní problémy čísla | ||||
| 			cislobody[ar_id], # body za číslo | ||||
| 			setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			cislo.rocnik) # ročník semináře pro zjištění ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	# vytahané informace předáváme do kontextu | ||||
| 	context['cislo'] = cislo | ||||
| 	context['radky_vysledkovky'] = radky_vysledkovky | ||||
| 	context['problemy'] = temata_a_spol | ||||
| 	context['ostatni'] = je_nejake_ostatni | ||||
| 	#context['v_cisle_zadane'] = TODO | ||||
| 	#context['resene_problemy'] = resene_problemy | ||||
| 	return context | ||||
| 
 | ||||
| class CisloView(generic.DetailView): | ||||
| 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | ||||
|  | @ -1386,7 +968,7 @@ class ClankyResitelView(generic.ListView): | |||
| 
 | ||||
| 	# FIXME: QuerySet není pole! | ||||
| 	def get_queryset(self): | ||||
| 		clanky = Clanek.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik') | ||||
| 		clanky = Clanek.objects.filter(stav=Problem.STAV_VYRESENY).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik') | ||||
| 		queryset = [] | ||||
| 		skupiny_clanku = group_by_rocnik(clanky) | ||||
| 		for skupina in skupiny_clanku: | ||||
|  | @ -1476,36 +1058,6 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 
 | ||||
| 		return HttpResponseRedirect(self.get_success_url()) | ||||
| 
 | ||||
| def resetPasswordView(request): | ||||
| 	pass | ||||
| 
 | ||||
| def loginView(request): | ||||
| 	if request.method == 'POST': | ||||
| 		form = LoginForm(request.POST) | ||||
| 		if form.is_valid(): | ||||
| 			user = authenticate(request,  | ||||
| 				username=form.cleaned_data['username'], | ||||
| 				password=form.cleaned_data['password']) | ||||
| 			print(form.cleaned_data) | ||||
| 			if user is not None: | ||||
| 				login(request,user) | ||||
| 				return HttpResponseRedirect('/') | ||||
| 			else: | ||||
| 				return render(request,  | ||||
| 					'seminar/profil/login.html', | ||||
| 					{'form': form, 'login_error': 'Neplatné jméno nebo heslo'}) | ||||
| 
 | ||||
| 	else: | ||||
| 		form = LoginForm() | ||||
| 	return render(request, 'seminar/profil/login.html', {'form': form}) | ||||
| 
 | ||||
| def logoutView(request): | ||||
| 	form = LoginForm() | ||||
| 	if request.user.is_authenticated: | ||||
| 		logout(request) | ||||
| 		return render(request, 'seminar/profil/login.html', {'form': form, 'login_error': 'Byli jste úspěšně odhlášeni'}) | ||||
| 	return render(request, 'seminar/profil/login.html', {'form': form}) | ||||
| 
 | ||||
| 
 | ||||
| def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data): | ||||
| 	msg = "{}, form_hash:{}".format(msg,hash(frozenset(form_data.items))) | ||||
|  | @ -1518,13 +1070,17 @@ def resitelEditView(request): | |||
| 	## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli | ||||
| 	u = request.user | ||||
| 	osoba_edit = Osoba.objects.get(user=u) | ||||
| 	if hasattr(osoba_edit,'resitel'): | ||||
| 		resitel_edit = osoba_edit.resitel | ||||
| 	else: | ||||
| 		resitel_edit = None | ||||
| 	user_edit = osoba_edit.user | ||||
| 	## Vytvoření slovníku, kterým předvyplním formulář  | ||||
| 	prefill_1=model_to_dict(user_edit) | ||||
| 	if resitel_edit: | ||||
| 		prefill_2=model_to_dict(resitel_edit) | ||||
| 	prefill_3=model_to_dict(osoba_edit) | ||||
| 		prefill_1.update(prefill_2) | ||||
| 	prefill_3=model_to_dict(osoba_edit) | ||||
| 	prefill_1.update(prefill_3) | ||||
| 	form = ProfileEditForm(initial=prefill_1) | ||||
| 	## Změna údajů a jejich uložení | ||||
|  | @ -1550,6 +1106,7 @@ def resitelEditView(request): | |||
| 				## Neznámá země | ||||
| 				msg = "Unknown country {}".format(fcd['stat_text']) | ||||
| 
 | ||||
| 			if resitel_edit: | ||||
| 				## Změny v řešiteli | ||||
| 				resitel_edit.skola = fcd['skola'] | ||||
| 				resitel_edit.rok_maturity = fcd['rok_maturity'] | ||||
|  | @ -1649,7 +1206,7 @@ class LoginView(auth_views.LoginView): | |||
| 	# Přesměrovací URL má být v kontextu: | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		ctx = super().get_context_data(**kwargs) | ||||
| 		ctx['next'] = reverse('titulni_strana') | ||||
| 		ctx['next'] = reverse('profil') | ||||
| 		return ctx | ||||
| 
 | ||||
| class LogoutView(auth_views.LogoutView): | ||||
|  |  | |||
							
								
								
									
										456
									
								
								seminar/views/vysledkovka.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								seminar/views/vysledkovka.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,456 @@ | |||
| import seminar.models as m | ||||
| from django.db.models import Q, Sum, Count | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problem, hlavni_problemy_f, problemy_cisla, podproblemy_v_cislu | ||||
| import time | ||||
| ### Výsledky | ||||
| 
 | ||||
| def sloupec_s_poradim(setrizene_body): | ||||
| 	""" | ||||
| 	Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
| 	vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.), | ||||
| 	podle toho, jak jdou za sebou ve výsledkovce. | ||||
| 	Parametr: | ||||
| 		setrizene_body (seznam integerů): sestupně setřízená čísla | ||||
| 
 | ||||
| 	Výstup: | ||||
| 		sloupec_s_poradim (seznam stringů) | ||||
| 	""" | ||||
| 
 | ||||
| 	# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím	 | ||||
| 	aktualni_poradi = 1 | ||||
| 	sloupec_s_poradim = [] | ||||
| 
 | ||||
| 	# seskupíme seznam všech bodů podle hodnot | ||||
| 	for index in range(0, len(setrizene_body)): | ||||
| 		# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme | ||||
| 		# vypsat už jen prázdné místo, než dojdeme na správný řádek | ||||
| 		if (index + 1) < aktualni_poradi: | ||||
| 			sloupec_s_poradim.append("") | ||||
| 			continue | ||||
| 		velikost_skupiny = 0 | ||||
| 		# zjistíme počet po sobě jdoucích stejných hodnot | ||||
| 		while setrizene_body[index] == setrizene_body[index + velikost_skupiny]: | ||||
| 			velikost_skupiny = velikost_skupiny + 1 | ||||
| 			# na konci musíme ošetřit přetečení seznamu | ||||
| 			if (index + velikost_skupiny) > len(setrizene_body) - 1: | ||||
| 				break | ||||
| 		# pokud je velikost skupiny 1, vypíšu pořadí | ||||
| 		if velikost_skupiny == 1: | ||||
| 			sloupec_s_poradim.append("{}.".format(aktualni_poradi)) | ||||
| 		# pokud je skupina větší, vypíšu rozsah | ||||
| 		else: | ||||
| 			sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,  | ||||
| 						aktualni_poradi+velikost_skupiny-1)) | ||||
| 		# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno | ||||
| 		aktualni_poradi = aktualni_poradi + velikost_skupiny | ||||
| 	return sloupec_s_poradim | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def body_resitelu(resitele, za, odjakziva=True): | ||||
| 	""" Funkce počítající počty bodů pro zadané řešitele,  | ||||
| 	buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. | ||||
| 	Parametry: | ||||
| 		resitele (seznam obsahující položky typu Resitel): aktivní řešitelé | ||||
| 		za (Rocnik/Cislo): za co se mají počítat body  | ||||
| 			(generování starších výsledkovek) | ||||
| 		odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník | ||||
| 			zadané v "za" | ||||
| 	Výstup: | ||||
| 		slovník (Resitel.id):body | ||||
| 	"""		 | ||||
| 	resitele_id = [r.id for r in resitele] | ||||
| 	# Zjistíme, typ objektu v parametru "za" | ||||
| 	if isinstance(za, m.Rocnik): | ||||
| 		cislo = None | ||||
| 		rocnik = za | ||||
| 		rok = rocnik.prvni_rok | ||||
| 	elif isinstance(za, m.Cislo): | ||||
| 		cislo = za | ||||
| 		rocnik = None | ||||
| 		rok = cislo.rocnik.prvni_rok | ||||
| 	else: | ||||
| 		assert True, "body_resitelu: za není ani číslo ani ročník."	 | ||||
| 
 | ||||
| 
 | ||||
| 	# Kvůli rychlosti používáme sčítáme body už v databázi, viz  | ||||
| 	# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,  | ||||
| 	# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky | ||||
| 	# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i  | ||||
| 	# za historická čísla. | ||||
| 
 | ||||
| 	# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,  | ||||
| 	# který se použije ve výsledném dotazu. | ||||
| 	if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla. | ||||
| 		# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,  | ||||
| 		# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen | ||||
| 		# pro čísla s pořadím nejvýše stejným, jako má zadané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) | | ||||
| 				 Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 				   reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif cislo and not odjakziva: # Body se sčítají za dané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 					reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.	 | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok)) | ||||
| 	elif rocnik and not odjakziva: # Spočítáme body za daný ročník. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) | ||||
| 	else:  | ||||
| 		assert True, "body_resitelu: Neplatná kombinace za a odjakživa." | ||||
| 
 | ||||
| 	# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů | ||||
| 	resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate( | ||||
| 		body=body_k_zapocteni) | ||||
| 	# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník  | ||||
| 	# indexovaný řešitelským id obsahující body.  | ||||
| 	# Pokud jsou body None, nahradíme za 0. | ||||
| 	slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body} | ||||
| 	return slovnik | ||||
| 
 | ||||
| class RadekVysledkovkyRocniku(object): | ||||
| 	""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): | ||||
| 		self.poradi = poradi | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.body_cisla_sezn = body_cisla_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 
 | ||||
| def setrid_resitele_a_body(slov_resitel_body): | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] | ||||
| 	setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] | ||||
| 	return setrizeni_resitele_id,  setrizene_body | ||||
| 
 | ||||
| def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||
| 	formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | ||||
| 	""" | ||||
| 	 | ||||
| 	start = time.time() | ||||
| 
 | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(resi_v_rocniku(rocnik)) | ||||
| 	cisla = cisla_rocniku(rocnik, jen_verejne) | ||||
| 	body_cisla_slov = {} | ||||
| 	for cislo in cisla: | ||||
| 		# získáme body za číslo | ||||
| 		_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) | ||||
| 		body_cisla_slov[cislo.id] = cislobody | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) | ||||
| 
 | ||||
| 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | ||||
| 	setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | ||||
| 	poradi = sloupec_s_poradim(setrizene_body) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 	setrizeni_resitele_dict = {} # Tento slovnik se vyrab | ||||
| 	for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'): | ||||
| 		setrizeni_resitele_dict[r.id] = r | ||||
| 	 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# seznam počtu bodů daného řešitele pro jednotlivá čísla | ||||
| 		body_cisla_sezn = [] | ||||
| 		for cislo in cisla: | ||||
| 			body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) | ||||
| 
 | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyRocniku( | ||||
| 			poradi[i], # pořadí | ||||
| 			setrizeni_resitele_dict[ar_id], # řešitel (z id) | ||||
| 			body_cisla_sezn, # seznam bodů za čísla | ||||
| 			setrizene_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			rocnik) # ročník semináře pro získání ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	end = time.time() | ||||
| 	print("Vysledkovka rocniku",end-start) | ||||
| 
 | ||||
| 	return radky_vysledkovky | ||||
| 
 | ||||
| class RadekVysledkovkyCisla(object): | ||||
| 	"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_problemy_sezn, | ||||
| 				body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter): | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_cislo = body_cislo | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.poradi = poradi | ||||
| 		self.body_problemy_sezn = body_problemy_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 		self.body_podproblemy = body_podproblemy | ||||
| 		self.body_podproblemy_iter = body_podproblemy_iter  # TODELETE | ||||
| 
 | ||||
| 
 | ||||
| def pricti_body(slovnik, resitel, body): | ||||
| 	""" Přiřazuje danému řešiteli body do slovníku. """ | ||||
| 	# testujeme na None (""), pokud je to první řešení  | ||||
| 	# daného řešitele, předěláme na 0 | ||||
| 	# (v dalším kroku přičteme reálný počet bodů), | ||||
| 	# rozlišujeme tím mezi 0 a neodevzdaným řešením | ||||
| 	if slovnik[resitel.id] == "": | ||||
| 		slovnik[resitel.id] = 0 | ||||
| 	 | ||||
| 	slovnik[resitel.id] += body | ||||
| 
 | ||||
| def secti_body_za_rocnik(za, aktivni_resitele): | ||||
| 	""" Spočítá body za ročník (celý nebo do daného čísla),  | ||||
| 		setřídí je sestupně a vrátí jako seznam. | ||||
| 	Parametry: | ||||
| 		za (typu Rocnik nebo Cislo)	spočítá za ročník, nebo za ročník až do  | ||||
| 						daného čísla | ||||
| 	""" | ||||
| 	# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa) | ||||
| 	resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False) | ||||
| 	# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně | ||||
| 	resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), | ||||
| 					key = lambda x: x[1], reverse = True) | ||||
| 	return resitel_rocnikbody_sezn | ||||
| 
 | ||||
| def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): | ||||
| 	""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata).""" | ||||
| 	# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé | ||||
| 	# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém  | ||||
| 	# pro jednotlivé řešitele (slovník slovníků hlavních problémů) | ||||
| 
 | ||||
| 	print("Scitam cislo",cislo) | ||||
| 
 | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_f(problemy_cisla(cislo)) | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		inst = problem.get_real_instance() | ||||
| 		return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik = {} | ||||
| 	for hp in temata_a_spol: | ||||
| 		hlavni_problemy_slovnik[hp.id] = {} | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik[-1] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	cislobody = {} | ||||
| 	for ar in aktivni_resitele: | ||||
| 		# řešitele převedeme na řetězec pomocí unikátního id | ||||
| 		cislobody[ar.id] = "" | ||||
| 		for hp in temata_a_spol: | ||||
| 			slovnik = hlavni_problemy_slovnik[hp.id] | ||||
| 			slovnik[ar.id] = "" | ||||
| 
 | ||||
| 		hlavni_problemy_slovnik[-1][ar.id] = "" | ||||
| 	 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele',  | ||||
| 				'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 	 | ||||
| 	start = time.time()	 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 		 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in list(reseni.problem.all()): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if ne_clanek_ne_konfera(nadproblem): | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[-1] | ||||
| 			 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in list(reseni.hodnoceni_set.all()): | ||||
| 				body = hodn.body	 | ||||
| 			 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in list(reseni.resitele.all()): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(cislobody, resitel, body) | ||||
| 					pricti_body(nadproblem_slovnik, resitel, body) | ||||
| 	end = time.time() | ||||
| 	print("for cykly:", end-start) | ||||
| 	return hlavni_problemy_slovnik, cislobody | ||||
| 
 | ||||
| 
 | ||||
| def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None): | ||||
| 	""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata).""" | ||||
| 	if temata is None: | ||||
| 		temata = hlavni_problemy_f(problemy_cisla(cislo)) | ||||
| 
 | ||||
| 	if podproblemy is None: | ||||
| 		podproblemy_v_cislu(cislo, hlavni_problemy=temata) | ||||
| 
 | ||||
| 	body_slovnik = {} | ||||
| 	for tema in temata: | ||||
| 		body_slovnik[tema.id] = {} | ||||
| 		for problem in podproblemy[tema.id]: | ||||
| 			body_slovnik[tema.id][problem.id] = {} | ||||
| 	body_slovnik[-1] = {} | ||||
| 	for problem in podproblemy[-1]: | ||||
| 		body_slovnik[-1][problem.id] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	for ar in aktivni_resitele: | ||||
| 		for tema in temata: | ||||
| 			for problem in podproblemy[tema.id]: | ||||
| 				body_slovnik[tema.id][problem.id][ar.id] = "" | ||||
| 
 | ||||
| 		for problem in podproblemy[-1]: | ||||
| 			body_slovnik[-1][problem.id][ar.id] = "" | ||||
| 
 | ||||
| 	temata = set(t.id for t in temata) | ||||
| 
 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele', | ||||
| 		'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 
 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 
 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in reseni.problem.all(): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if nadproblem.id in temata: | ||||
| 				nadproblem_slovnik = body_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = body_slovnik[-1] | ||||
| 
 | ||||
| 			problem_slovnik = nadproblem_slovnik[prob.id] | ||||
| 
 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in reseni.hodnoceni_set.all(): | ||||
| 				body = hodn.body | ||||
| 
 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in reseni.resitele.all(): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(problem_slovnik, resitel, body) | ||||
| 	return body_slovnik | ||||
| 
 | ||||
| 
 | ||||
| # TODELETE | ||||
| class FixedIterator: | ||||
| 	def next(self): | ||||
| 		return self.niter.__next__() | ||||
| 
 | ||||
| 	def __init__(self, niter): | ||||
| 		self.niter = niter | ||||
| # TODELETE | ||||
| 
 | ||||
| 
 | ||||
| def vysledkovka_cisla(cislo, context=None): | ||||
| 	if context is None: | ||||
| 		context = {} | ||||
| 	problemy = problemy_cisla(cislo) | ||||
| 	hlavni_problemy = hlavni_problemy_f(problemy) | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo)) | ||||
| 
 | ||||
| 	# získáme body za číslo | ||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo) | ||||
| 
 | ||||
| 	# řešitelé setřídění podle bodů za číslo sestupně | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | ||||
| 			 | ||||
| 	# spočítáme pořadí řešitelů | ||||
| 	setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	poradi = sloupec_s_poradim(setrizeni_resitele_body) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		 | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	# získáme body u jednotlivých témat | ||||
| 	podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) | ||||
| 	problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol) | ||||
| 
 | ||||
| 	# def not_empty(value): | ||||
| 	# 	return value != '' | ||||
| 	# | ||||
| 	# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 | ||||
| 
 | ||||
| 	je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 | ||||
| 
 | ||||
| 	setrizeni_resitele_slovnik = {} | ||||
| 	setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba') | ||||
| 	for r in setrizeni_resitele: | ||||
| 		setrizeni_resitele_slovnik[r.id] = r | ||||
| 
 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# získáme seznam bodů za problémy pro daného řešitele | ||||
| 		body_problemy = [] | ||||
| 		body_podproblemy = [] | ||||
| 		for hp in temata_a_spol: | ||||
| 			body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) | ||||
| 			body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]]) | ||||
| 		if je_nejake_ostatni: | ||||
| 			body_problemy.append(hlavni_problemy_slovnik[-1][ar_id]) | ||||
| 			body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]]) | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyCisla( | ||||
| 			poradi[i], # pořadí | ||||
| 			setrizeni_resitele_slovnik[ar_id], # řešitel (z id) | ||||
| 			body_problemy, # seznam bodů za hlavní problémy čísla | ||||
| 			cislobody[ar_id], # body za číslo | ||||
| 			setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			cislo.rocnik, | ||||
| 			body_podproblemy,  # body všech podproblémů | ||||
| 			FixedIterator(body_podproblemy.__iter__())  # TODELETE | ||||
| 		)  # ročník semináře pro zjištění ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	# vytahané informace předáváme do kontextu | ||||
| 	context['cislo'] = cislo | ||||
| 	context['radky_vysledkovky'] = radky_vysledkovky | ||||
| 	context['problemy'] = temata_a_spol | ||||
| 	context['ostatni'] = je_nejake_ostatni | ||||
| 	pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]] | ||||
| 	context['podproblemy'] = pt | ||||
| 	context['podproblemy_iter'] = FixedIterator(pt.__iter__())  # TODELETE | ||||
| 	#context['v_cisle_zadane'] = TODO | ||||
| 	#context['resene_problemy'] = resene_problemy | ||||
| 	return context | ||||
		Loading…
	
		Reference in a new issue
	
	 Pavel "LEdoian" Turinsky
						Pavel "LEdoian" Turinsky