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
|
pip install --upgrade setuptools
|
||||||
# Instalace závislostí webu
|
# Instalace závislostí webu
|
||||||
pip install -r requirements.txt --upgrade
|
pip install -r requirements.txt --upgrade
|
||||||
# Po vygenerování testdat spusť ./manage.py loaddata sitetree_new.json, ať máš menu
|
# Po vygenerování testdat spusť ./manage.py loaddata data/*, ať máš menu a další modely
|
||||||
# Pro synchronizaci flatpages spusť make sync_prod_flatpages
|
:x
|
||||||
|
|
||||||
install_venv:
|
install_venv:
|
||||||
${VENV} ${VENV_PATH}
|
${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
512
data/flat.json
512
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:
|
if email:
|
||||||
emails.discard(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))
|
print("Poslal bych upozornění na tyto adresy: ", " ".join(emails))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -308,4 +308,4 @@ CISLO_IMG_DIR = os.path.join('cislo', 'img')
|
||||||
|
|
||||||
|
|
||||||
# E-MAIL NOTIFICATIONS
|
# 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
|
# E-MAIL NOTIFICATIONS
|
||||||
SEND_EMAIL_NOTIFICATIONS = True
|
POSLI_MAILOVOU_NOTIFIKACI = True
|
||||||
|
|
|
@ -9,10 +9,15 @@ body {
|
||||||
background-color: #fffbf6;
|
background-color: #fffbf6;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
div.content-wrapper {
|
||||||
|
padding-bottom: 200px; /* Footer height */
|
||||||
|
}
|
||||||
|
|
||||||
div.container {
|
div.container {
|
||||||
width: 970px;
|
width: 970px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.org-logged-in div.container {
|
.org-logged-in div.container {
|
||||||
|
@ -321,11 +326,7 @@ div.novinky_name {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.zadani_azad_termin {
|
|
||||||
text-align: center;
|
|
||||||
font-size: large;
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********
|
/**********
|
||||||
* Footer
|
* Footer
|
||||||
|
@ -333,6 +334,9 @@ div.zadani_azad_termin {
|
||||||
|
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
background: url("../images/mozaika-footer.svg") no-repeat top center;
|
background: url("../images/mozaika-footer.svg") no-repeat top center;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
background-position: relative;
|
background-position: relative;
|
||||||
|
@ -353,6 +357,7 @@ div.zadani_azad_termin {
|
||||||
|
|
||||||
p.license-mobile {
|
p.license-mobile {
|
||||||
display: none;
|
display: none;
|
||||||
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************/
|
/*********************/
|
||||||
|
@ -390,8 +395,8 @@ input[type="file"] {
|
||||||
border-width:1px;
|
border-width:1px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding:3px;
|
padding:3px;
|
||||||
top:20px;
|
top:50px;
|
||||||
left:20px;
|
left:10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-with-comment:hover span.field-comment{
|
.field-with-comment:hover span.field-comment{
|
||||||
|
@ -763,7 +768,7 @@ div.odpocet {
|
||||||
|
|
||||||
/*stránka organizátorů*/
|
/*stránka organizátorů*/
|
||||||
|
|
||||||
div.seznam_orgu, div.rozcestnik_temat {
|
div.seznam_orgu, div.rozcestnik_temat, div.seznam_archiv {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
@ -897,6 +902,38 @@ div.cislo_odkazy ul {
|
||||||
padding: 0px;
|
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 */
|
/* galerie */
|
||||||
|
|
||||||
|
@ -961,8 +998,6 @@ div.cislo_odkazy ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* titulní obrázek hlavní galerie soustředění */
|
/* titulní obrázek hlavní galerie soustředění */
|
||||||
.titulni_obrazek {
|
|
||||||
}
|
|
||||||
|
|
||||||
.galerie_nahledy{
|
.galerie_nahledy{
|
||||||
/*margin: 1em 0;*/
|
/*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">
|
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{# Případné skripty widgetů formulářů #}
|
||||||
|
{% if form %}
|
||||||
|
{{form.media}}
|
||||||
|
{% endif %}
|
||||||
{# script specifický pro stránku #}
|
{# script specifický pro stránku #}
|
||||||
{% block script %}{% endblock %}
|
{% block script %}{% endblock %}
|
||||||
|
|
||||||
|
@ -52,9 +56,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<div class='row'>
|
|
||||||
<div class='col-md-12'>
|
|
||||||
<a href='/'>
|
<a href='/'>
|
||||||
<div id="title" >M&M - korespondenční seminář a časopis MFF UK</div>
|
<div id="title" >M&M - korespondenční seminář a časopis MFF UK</div>
|
||||||
<div id="header">
|
<div id="header">
|
||||||
|
@ -65,11 +68,7 @@
|
||||||
<img class="logo-mobile" src="{% static 'images/logo-mobile.svg' %}" />
|
<img class="logo-mobile" src="{% static 'images/logo-mobile.svg' %}" />
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='row'>
|
|
||||||
<div class='col-md-12'>
|
|
||||||
|
|
||||||
{# ========= MENU ========== #}
|
{# ========= MENU ========== #}
|
||||||
|
|
||||||
|
@ -77,8 +76,6 @@
|
||||||
|
|
||||||
{# ========= MENU MOBILE ========== #}
|
{# ========= MENU MOBILE ========== #}
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--Navbar-->
|
<!--Navbar-->
|
||||||
<nav class="nav-button">
|
<nav class="nav-button">
|
||||||
|
@ -102,25 +99,23 @@
|
||||||
|
|
||||||
{# ========= END MENU ========== #}
|
{# ========= END MENU ========== #}
|
||||||
|
|
||||||
<div class='row'>
|
|
||||||
<div class='row content'>
|
<div class='content'>
|
||||||
<div class='col-md-12'>
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
</div>
|
</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">
|
<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>
|
<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>
|
</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>
|
||||||
|
|
||||||
|
|
||||||
<script src="{% static 'js/bootstrap.js' %}"></script>
|
<script src="{% static 'js/bootstrap.js' %}"></script>
|
||||||
<script src="{% static 'js/jquery.jcarousel-core.js' %}" type="text/javascript"></script>
|
<script src="{% static 'js/jquery.jcarousel-core.js' %}" type="text/javascript"></script>
|
||||||
|
|
|
@ -350,7 +350,7 @@
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
id="casopis"
|
id="casopis"
|
||||||
href="/archiv/cisla/"
|
href="/archiv/rocniky"
|
||||||
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
|
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
|
||||||
<g
|
<g
|
||||||
id="g959">
|
id="g959">
|
||||||
|
@ -379,7 +379,7 @@
|
||||||
</g>
|
</g>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/clanky/uvod/"
|
href="/jak-resit"
|
||||||
id="clanky"
|
id="clanky"
|
||||||
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
|
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
|
||||||
<g
|
<g
|
||||||
|
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
@ -3,7 +3,7 @@
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
{% for item in sitetree_items %}
|
{% 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 %}"
|
style="{% if item.title == "HIDDEN" %}display:none{% endif %}"
|
||||||
>
|
>
|
||||||
<a href="{% sitetree_url for item %}" >
|
<a href="{% sitetree_url for item %}" >
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
from django.contrib import admin
|
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 polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
from django_reverse_admin import ReverseModelAdmin
|
from django_reverse_admin import ReverseModelAdmin
|
||||||
from solo.admin import SingletonModelAdmin
|
from solo.admin import SingletonModelAdmin
|
||||||
|
|
||||||
|
|
||||||
# Todo: reversion
|
# Todo: reversion
|
||||||
|
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
|
@ -14,8 +17,6 @@ admin.site.register(m.Skola)
|
||||||
admin.site.register(m.Prijemce)
|
admin.site.register(m.Prijemce)
|
||||||
admin.site.register(m.Rocnik)
|
admin.site.register(m.Rocnik)
|
||||||
admin.site.register(m.Cislo)
|
admin.site.register(m.Cislo)
|
||||||
admin.site.register(m.Organizator)
|
|
||||||
admin.site.register(m.Soustredeni)
|
|
||||||
|
|
||||||
@admin.register(m.Osoba)
|
@admin.register(m.Osoba)
|
||||||
class OsobaAdmin(admin.ModelAdmin):
|
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ů"
|
synchronizuj_maily.short_description = "Synchronizuj vybraným osobám e-maily do uživatelů"
|
||||||
|
|
||||||
def udelej_orgem(self,request,queryset):
|
def udelej_orgem(self,request,queryset):
|
||||||
org_perm = Permission.objects.filter(codename__exact='org').first()
|
org_group = Group.objects.get(name='org')
|
||||||
print(queryset)
|
print(queryset)
|
||||||
for o in queryset:
|
for o in queryset:
|
||||||
user = o.user
|
user = o.user
|
||||||
user.user_permissions.add(org_perm)
|
print(user)
|
||||||
|
user.groups.add(org_group)
|
||||||
user.is_staff = True
|
user.is_staff = True
|
||||||
user.save()
|
user.save()
|
||||||
org = m.Organizator.objects.create(osoba=o)
|
org = m.Organizator.objects.create(osoba=o)
|
||||||
org.save()
|
org.save()
|
||||||
udelej_orgem.short_description = "Udělej vybraných osob organizátory"
|
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)
|
@admin.register(m.Problem)
|
||||||
class ProblemAdmin(PolymorphicParentModelAdmin):
|
class ProblemAdmin(PolymorphicParentModelAdmin):
|
||||||
|
@ -53,36 +62,81 @@ class ProblemAdmin(PolymorphicParentModelAdmin):
|
||||||
m.Uloha,
|
m.Uloha,
|
||||||
m.Konfera,
|
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)
|
@admin.register(m.Tema)
|
||||||
class TemaAdmin(PolymorphicChildModelAdmin):
|
class TemaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
|
||||||
base_model = m.Tema
|
base_model = m.Tema
|
||||||
show_in_index = True
|
|
||||||
|
|
||||||
@admin.register(m.Clanek)
|
@admin.register(m.Clanek)
|
||||||
class ClanekAdmin(PolymorphicChildModelAdmin):
|
class ClanekAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
|
||||||
base_model = m.Clanek
|
base_model = m.Clanek
|
||||||
show_in_index = True
|
|
||||||
|
|
||||||
@admin.register(m.Uloha)
|
@admin.register(m.Uloha)
|
||||||
class UlohaAdmin(PolymorphicChildModelAdmin):
|
class UlohaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
|
||||||
base_model = m.Uloha
|
base_model = m.Uloha
|
||||||
show_in_index = True
|
|
||||||
|
|
||||||
@admin.register(m.Konfera)
|
@admin.register(m.Konfera)
|
||||||
class KonferaAdmin(PolymorphicChildModelAdmin):
|
class KonferaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
|
||||||
base_model = m.Konfera
|
base_model = m.Konfera
|
||||||
show_in_index = True
|
|
||||||
|
|
||||||
class TextAdminInline(admin.TabularInline):
|
class TextAdminInline(admin.TabularInline):
|
||||||
model = m.Text
|
model = m.Text
|
||||||
|
formfield_overrides = {
|
||||||
|
models.TextField: {'widget': widgets.TextInput}
|
||||||
|
}
|
||||||
exclude = ['text_zkraceny_set','text_zkraceny']
|
exclude = ['text_zkraceny_set','text_zkraceny']
|
||||||
|
|
||||||
admin.site.register(m.Text)
|
admin.site.register(m.Text)
|
||||||
|
|
||||||
class ResitelInline(admin.TabularInline):
|
class ResitelInline(admin.TabularInline):
|
||||||
model = m.Resitel
|
model = m.Resitel
|
||||||
extra = 1
|
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):
|
class PrilohaReseniInline(admin.TabularInline):
|
||||||
model = m.PrilohaReseni
|
model = m.PrilohaReseni
|
||||||
|
@ -92,6 +146,7 @@ admin.site.register(m.PrilohaReseni)
|
||||||
class Reseni_ResiteleInline(admin.TabularInline):
|
class Reseni_ResiteleInline(admin.TabularInline):
|
||||||
model = m.Reseni_Resitele
|
model = m.Reseni_Resitele
|
||||||
|
|
||||||
|
|
||||||
@admin.register(m.Reseni)
|
@admin.register(m.Reseni)
|
||||||
class ReseniAdmin(ReverseModelAdmin):
|
class ReseniAdmin(ReverseModelAdmin):
|
||||||
base_model = m.Reseni
|
base_model = m.Reseni
|
||||||
|
@ -106,7 +161,6 @@ admin.site.register(m.Hodnoceni)
|
||||||
admin.site.register(m.Pohadka)
|
admin.site.register(m.Pohadka)
|
||||||
admin.site.register(m.Obrazek)
|
admin.site.register(m.Obrazek)
|
||||||
|
|
||||||
|
|
||||||
# Polymorfismus pro stromy
|
# Polymorfismus pro stromy
|
||||||
# TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html
|
# 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 dal import autocomplete
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.forms import formset_factory
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
|
|
||||||
from .models import Skola, Resitel, Osoba, Problem
|
from .models import Skola, Resitel, Osoba, Problem
|
||||||
|
@ -184,7 +185,7 @@ class ProfileEditForm(forms.Form):
|
||||||
max_value=date.today().year+8,
|
max_value=date.today().year+8,
|
||||||
required=True)
|
required=True)
|
||||||
zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, 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)
|
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
|
||||||
# def clean_username(self):
|
# def clean_username(self):
|
||||||
|
@ -301,3 +302,91 @@ class NahrajObrazekKTreeNoduForm(forms.ModelForm):
|
||||||
model = m.Obrazek
|
model = m.Obrazek
|
||||||
fields = ('na_web',)
|
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 django.utils.text import get_valid_filename
|
||||||
from imagekit.models import ImageSpecField, ProcessedImageField
|
from imagekit.models import ImageSpecField, ProcessedImageField
|
||||||
from imagekit.processors import ResizeToFit, Transpose
|
from imagekit.processors import ResizeToFit, Transpose
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from django_countries.fields import CountryField
|
from django_countries.fields import CountryField
|
||||||
from solo.models import SingletonModel
|
from solo.models import SingletonModel
|
||||||
|
@ -26,11 +27,14 @@ from taggit.managers import TaggableManager
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode
|
from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode
|
||||||
|
from 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 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 polymorphic.models import PolymorphicModel
|
||||||
|
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
|
from seminar.utils import aktivniResitele
|
||||||
|
|
||||||
class SeminarModelBase(models.Model):
|
class SeminarModelBase(models.Model):
|
||||||
|
|
||||||
|
@ -624,9 +628,43 @@ class Cislo(SeminarModelBase):
|
||||||
return None
|
return None
|
||||||
return c
|
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):
|
def save(self, *args, **kwargs):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
self.vygeneruj_nahled()
|
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.
|
# *Node.save() aktualizuje název *Nodu.
|
||||||
try:
|
try:
|
||||||
self.cislonode.save()
|
self.cislonode.save()
|
||||||
|
@ -818,10 +856,11 @@ class Problem(SeminarModelBase,PolymorphicModel):
|
||||||
return self.nazev
|
return self.nazev
|
||||||
|
|
||||||
# Implicitini implementace, jednotlivé dědící třídy si přepíšou
|
# Implicitini implementace, jednotlivé dědící třídy si přepíšou
|
||||||
|
@cached_property
|
||||||
def kod_v_rocniku(self):
|
def kod_v_rocniku(self):
|
||||||
if self.stav == 'zadany':
|
if self.stav == 'zadany':
|
||||||
if self.nadproblem:
|
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 str(self.kod)
|
||||||
return '<Není zadaný>'
|
return '<Není zadaný>'
|
||||||
|
|
||||||
|
@ -829,17 +868,26 @@ class Problem(SeminarModelBase,PolymorphicModel):
|
||||||
# aktuálně podle stavu problému
|
# aktuálně podle stavu problému
|
||||||
# FIXME pro některé problémy možná chceme override
|
# 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.
|
# 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
|
stav_verejny = False
|
||||||
if self.stav == 'zadany' or self.stav == 'vyreseny':
|
if self.stav == 'zadany' or self.stav == 'vyreseny':
|
||||||
stav_verejny = True
|
stav_verejny = True
|
||||||
return stav_verejny
|
print("stav_verejny: {}".format(stav_verejny))
|
||||||
|
|
||||||
#cislo_verejne = False
|
cislo_verejne = False
|
||||||
#if (self.cislo_zadani and self.cislo_zadani.verejne()):
|
cislonode = self.cislo_node()
|
||||||
# cislo_verejne = True
|
if cislonode is None:
|
||||||
|
# problém nemá vlastní node, veřejnost posuzujeme jen podle stavu
|
||||||
#return (stav_verejny and cislo_verejne)
|
print("empty node")
|
||||||
|
return stav_verejny
|
||||||
|
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
|
verejne.boolean = True
|
||||||
|
|
||||||
def verejne_url(self):
|
def verejne_url(self):
|
||||||
|
@ -875,16 +923,17 @@ class Tema(Problem):
|
||||||
tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES,
|
tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES,
|
||||||
blank=False, default=TEMA_TEMA)
|
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)
|
on_delete=models.PROTECT)
|
||||||
|
|
||||||
abstrakt = models.TextField('Abstrakt na rozcestník', blank=True)
|
abstrakt = models.TextField('Abstrakt na rozcestník', blank=True)
|
||||||
obrazek = models.ImageField('Obrázek na rozcestník', null=True)
|
obrazek = models.ImageField('Obrázek na rozcestník', null=True)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
def kod_v_rocniku(self):
|
def kod_v_rocniku(self):
|
||||||
if self.stav == 'zadany':
|
if self.stav == 'zadany':
|
||||||
if self.nadproblem:
|
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 "t{}".format(self.kod)
|
||||||
return '<Není zadaný>'
|
return '<Není zadaný>'
|
||||||
|
|
||||||
|
@ -894,6 +943,16 @@ class Tema(Problem):
|
||||||
for tvcn in self.temavcislenode_set.all():
|
for tvcn in self.temavcislenode_set.all():
|
||||||
tvcn.save()
|
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 Clanek(Problem):
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = 'seminar_clanky'
|
db_table = 'seminar_clanky'
|
||||||
|
@ -907,9 +966,12 @@ class Clanek(Problem):
|
||||||
if self.stav == 'zadany':
|
if self.stav == 'zadany':
|
||||||
# Nemělo by být potřeba
|
# Nemělo by být potřeba
|
||||||
# if self.nadproblem:
|
# 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 "c{}".format(self.kod)
|
||||||
return '<Není zadaný>'
|
return '<Není zadaný>'
|
||||||
|
|
||||||
|
def node(self):
|
||||||
|
return None
|
||||||
|
|
||||||
class Text(SeminarModelBase):
|
class Text(SeminarModelBase):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -961,11 +1023,12 @@ class Uloha(Problem):
|
||||||
# UlohaZadaniNode
|
# UlohaZadaniNode
|
||||||
# UlohaVzorakNode
|
# UlohaVzorakNode
|
||||||
|
|
||||||
|
@cached_property
|
||||||
def kod_v_rocniku(self):
|
def kod_v_rocniku(self):
|
||||||
if self.stav == 'zadany':
|
if self.stav == 'zadany':
|
||||||
name="{}.u{}".format(self.cislo_zadani.poradi,self.kod)
|
name="{}.u{}".format(self.cislo_zadani.poradi,self.kod)
|
||||||
if self.nadproblem:
|
if self.nadproblem:
|
||||||
return self.nadproblem.kod_v_rocniku()+name
|
return self.nadproblem.kod_v_rocniku+name
|
||||||
return name
|
return name
|
||||||
return '<Není zadaný>'
|
return '<Není zadaný>'
|
||||||
|
|
||||||
|
@ -983,6 +1046,9 @@ class Uloha(Problem):
|
||||||
# Neexistující *Node nemá smysl aktualizovat.
|
# Neexistující *Node nemá smysl aktualizovat.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def cislo_node(self):
|
||||||
|
zadani_node = self.ulohazadaninode
|
||||||
|
return treelib.get_upper_node_of_type(zadani_node, CisloNode)
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
@reversion.register(ignore_duplicates=True)
|
||||||
class Reseni(SeminarModelBase):
|
class Reseni(SeminarModelBase):
|
||||||
|
@ -1273,6 +1339,8 @@ class Konfera(Problem):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{}: ({})".format(self.nazev, self.soustredeni)
|
return "{}: ({})".format(self.nazev, self.soustredeni)
|
||||||
|
|
||||||
|
def cislo_node(self):
|
||||||
|
return None
|
||||||
|
|
||||||
# Vazebna tabulka. Mozna se generuje automaticky.
|
# Vazebna tabulka. Mozna se generuje automaticky.
|
||||||
@reversion.register(ignore_duplicates=True)
|
@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" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
|
||||||
<h2>
|
<h2>
|
||||||
{% block nadpis1a %}{% block nadpis1b %}
|
{% block nadpis1a %}{% block nadpis1b %}
|
||||||
Archiv čísel
|
Archiv čísel
|
||||||
{% endblock %}{% endblock %}
|
{% endblock %}{% endblock %}
|
||||||
</h2>
|
</h2>
|
||||||
|
<div class="seznam_archiv">
|
||||||
|
|
||||||
{% for rocnik, url_png in object_list.items %}
|
{% for rocnik, url_png in object_list.items %}
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@
|
||||||
{% empty %}
|
{% empty %}
|
||||||
Nejsou žádné ročníky
|
Nejsou žádné ročníky
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
|
@ -9,11 +9,7 @@
|
||||||
Číslo {{ cislo }}
|
Číslo {{ cislo }}
|
||||||
{% endblock %}{% endblock %}
|
{% endblock %}{% endblock %}
|
||||||
</h1>
|
</h1>
|
||||||
|
<a href='{{ cislo.rocnik.verejne_url }}'>Zpět na ročník {{ cislo.rocnik }}</a>
|
||||||
{% 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>
|
|
||||||
|
|
||||||
{% if v_cisle_zadane %}
|
{% if v_cisle_zadane %}
|
||||||
<h2>Zadané problémy</h2>
|
<h2>Zadané problémy</h2>
|
||||||
|
@ -50,12 +46,26 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
<script id="vuedata" type="application/json">{"treenode":{{cislo.cislonode.id}}}</script>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<app></app>
|
<app></app>
|
||||||
</div>
|
</div>
|
||||||
{% render_bundle 'chunk-vendors' %}
|
{% render_bundle 'chunk-vendors' %}
|
||||||
{% render_bundle 'vue_app_01' %}
|
{% render_bundle 'vue_app_01' %}
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
|
||||||
{% if cislo.verejna_vysledkovka %}
|
{% if cislo.verejna_vysledkovka %}
|
||||||
|
@ -74,9 +84,24 @@
|
||||||
<th class='border-r'>#
|
<th class='border-r'>#
|
||||||
<th class='border-r'>Jméno
|
<th class='border-r'>Jméno
|
||||||
{% for p in problemy %}
|
{% 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 %}
|
{% endfor %}
|
||||||
{% if ostatni %}<th class='border-r'>Ostatní {% endif %}
|
{% 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 číslo
|
||||||
<th class='border-r'>Za ročník
|
<th class='border-r'>Za ročník
|
||||||
<th class='border-r'>Odjakživa
|
<th class='border-r'>Odjakživa
|
||||||
|
@ -90,6 +115,13 @@
|
||||||
{{ rv.resitel.osoba.plne_jmeno }}
|
{{ rv.resitel.osoba.plne_jmeno }}
|
||||||
{% for b in rv.body_problemy_sezn %}
|
{% for b in rv.body_problemy_sezn %}
|
||||||
<td class='border-r'>{{ b }}
|
<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 %}
|
{% endfor %}
|
||||||
<td class='border-r'>{{ rv.body_cislo }}
|
<td class='border-r'>{{ rv.body_cislo }}
|
||||||
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
|
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
|
||||||
|
@ -97,6 +129,23 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</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 %}
|
{% endif %}
|
||||||
|
|
||||||
{% if not cislo.verejna_vysledkovka and user.je_org %}
|
{% if not cislo.verejna_vysledkovka and user.je_org %}
|
||||||
|
|
|
@ -8,15 +8,17 @@
|
||||||
{% endblock %}{% endblock %}
|
{% endblock %}{% endblock %}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{% if temata_v_rocniku %}
|
{% if rocnik.temata %}
|
||||||
<h2>Témata</h2>
|
<h2>Témata</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for tema in temata_v_rocniku %}
|
{% for tema in rocnik.temata.all %}
|
||||||
<li>{% if tema.text_zadani %}<a href="{{ tema.verejne_url }}">{% endif %}{{ tema.kod_v_rocniku }}: {{ tema.nazev }}{% if tema.text_zadani %}</a>{% endif %}
|
<li>{#<a href="{{ tema.verejne_url }}">#}{{ tema.nazev }}{#</a>#}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h2>Čísla</h2>
|
||||||
|
|
||||||
<div class="cisla-v-rocniku">
|
<div class="cisla-v-rocniku">
|
||||||
{% for c in rocnik.verejna_cisla %}
|
{% for c in rocnik.verejna_cisla %}
|
||||||
<div class="cislo_pole">
|
<div class="cislo_pole">
|
||||||
|
@ -32,7 +34,7 @@
|
||||||
{% if c.titulka_nahled %}
|
{% if c.titulka_nahled %}
|
||||||
<img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px>
|
<img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px>
|
||||||
{% else %}
|
{% else %}
|
||||||
<img src="" alt="no image" height=180px>
|
{% load static %} <img src="{% static 'images/no-picture.png' %}" height=180px alt="no-picture">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% if vysledkovka %}
|
{% if vysledkovka %}
|
||||||
{% if user.je_org %}
|
{% if user.je_org %}
|
||||||
<div class='mam-org-only'>
|
<div class='mam-org-only'>
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{% for rocnik, temata in rocniky.items %}
|
{% 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>
|
<ul>
|
||||||
{% for tema in temata %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -9,15 +9,28 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{% for clanek in object_list %}
|
{% for clanek in object_list %}
|
||||||
{% with clanek.cislo.rocnik.rocnik as rocnik %}
|
{% with clanek.cislo.rocnik.rocnik as rocnik %}
|
||||||
{% ifchanged rocnik %}
|
{% ifchanged rocnik %}
|
||||||
{% if not forloop.first %}</ul>{% endif %}
|
{% if not forloop.first %}</ul>{% endif %}
|
||||||
<h2>{{ rocnik }}. ročník</h2>
|
<h2>{{ rocnik }}. ročník</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% endifchanged %}
|
{% endifchanged %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ clanek.verejne_url }}">{{ clanek.nazev }}</a>
|
{% if clanek.cislo.pdf %}
|
||||||
{% endwith %}
|
<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 %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,59 @@
|
||||||
|
|
||||||
{% block content %}
|
{% 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šené problémy: {{ object.problem.all | join:", " }}</p>
|
||||||
|
|
||||||
<p>Řešitelé: {{ object.resitele.all | join:", " }}</p>
|
<p>Řešitelé: {{ object.resitele.all | join:", " }}</p>
|
||||||
|
@ -27,21 +80,33 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Hodnocení: #}
|
{# Hodnocení: #}
|
||||||
{# FIXME: Udělat jako formulář #}
|
|
||||||
<h3>Hodnocení:</h3>
|
<h3>Hodnocení:</h3>
|
||||||
{% if object.hodnoceni_set.all %}
|
<form method=post><table>
|
||||||
<table>
|
{% csrf_token %}
|
||||||
|
{{ form.management_form }}
|
||||||
|
<table id="form_set">
|
||||||
<tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr>
|
<tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr>
|
||||||
{% for h in object.hodnoceni_set.all %}
|
{% for subform in form %}
|
||||||
<tr>
|
<tr class="hodnoceni">
|
||||||
<td>{{ h.problem }}</a></td>
|
<td>{{ subform.problem }}</td>
|
||||||
<td>{{ h.body }}</td>
|
<td>{{ subform.body }}</td>
|
||||||
<td>{{ h.cislo_body }}</td></tr>
|
<td>{{ subform.cislo_body }}</td>
|
||||||
|
<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{subform.prefix}}-jsremove"></td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</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 %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
<form method=get action=.>
|
||||||
|
{{ filtr.resitele }}
|
||||||
|
{{ filtr.problemy }}
|
||||||
|
Od: {{ filtr.reseni_od }}
|
||||||
|
Do: {{ filtr.reseni_do }}
|
||||||
|
<input type=submit value="→">
|
||||||
|
</form>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td> {# Prázdná buňka v levém horním rohu #}
|
<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 %}
|
{% load staticfiles %}
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||||
|
|
|
@ -20,13 +20,14 @@
|
||||||
<h2><strong>Tvorba čísla</strong></h2>
|
<h2><strong>Tvorba čísla</strong></h2>
|
||||||
|
|
||||||
<ul>
|
<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>
|
<li><strong>korektury</strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/korektury/">korekturování</a></li>
|
<li><a href="/korektury/">korekturování</a></li>
|
||||||
<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li>
|
<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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>
|
<li><a href='{{ posledni_cislo_url }}'><strong>poslední vydané číslo </strong></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
<h2><strong>Soustředění</strong></h2>
|
<h2><strong>Soustředění</strong></h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><a href="/admin/seminar/soustredeni/add/">přidat soustředění</a></li>
|
||||||
<li><strong>přednášky</strong>
|
<li><strong>přednášky</strong>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
|
||||||
{{form.media}}
|
|
||||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
{% block script %}
|
{% 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>
|
<script src="{% static 'seminar/dynamic_formsets.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
|
||||||
{{form.media}}
|
|
||||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="tema_pole">
|
<div class="tema_pole">
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
<a href='{{ rocnik.verejne_url }}'>Téma {{ tematko.nazev }}</a>
|
<a href='{{ tematko.verejne_url }}'>Téma {{ tematko.nazev }}</a>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="flip-card" id="tema-rozcestnik">
|
<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' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
@ -7,10 +16,22 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if nejblizsi_deadline %}
|
{% if nejblizsi_deadline %}
|
||||||
|
<hr>
|
||||||
<div class="odpocet">
|
<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>
|
{{nejblizsi_deadline|timeuntil}}</big></b></p>
|
||||||
</div>
|
</div>
|
||||||
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class=titulnistrana>
|
<div class=titulnistrana>
|
||||||
|
@ -29,7 +50,7 @@
|
||||||
<div>
|
<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,
|
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.
|
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
|
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í.
|
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 #}
|
<a href="cojemam/odmeny"> <div class="button"> Co můžeš vyhrát? </div> </a> {# FIXME odkaz #}
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
{% block nadpis1a %}{% block nadpis1b %}
|
{% 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 %}
|
{% endblock %}{% endblock %}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{% if vysledkovka %}
|
{% if radky_vysledkovky %}
|
||||||
{% include "seminar/vysledkovka_rocnik.html" %}
|
{% include "seminar/vysledkovka_rocnik.html" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>V tomto ročníku zatím žádné výsledky nejsou.</p>
|
<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 %}
|
{% if user.je_org and vysledkovka_s_neverejnymi %}
|
||||||
<div class='mam-org-only'>
|
<div class='mam-org-only'>
|
||||||
<h1>Výsledky včetně neveřejných</h1>
|
<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" %}
|
{% include "seminar/vysledkovka_rocnik.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,79 +5,47 @@
|
||||||
{% endblock %}{% endblock %}
|
{% endblock %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div class="stranka_aktualni_zadani">
|
||||||
|
|
||||||
{% with nastaveni.aktualni_cislo as ac %}
|
{% with nastaveni.aktualni_cislo as ac %}
|
||||||
|
|
||||||
{# Zobrazovani neverejnych zadani jen organizatorum #}
|
{# Zobrazovani neverejnych zadani jen organizatorum #}
|
||||||
{% if user.je_org or verejne %}
|
{% 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 %}
|
||||||
|
|
||||||
{% if ac.zadane_problemy.all %}
|
<hr>
|
||||||
{% if ac.datum_deadline_soustredeni %}
|
<div class="zadani_termin">
|
||||||
<div class="zadani_azad_termin">
|
Termíny pro odeslání řešení {{ac.poradi}}. série:<br>
|
||||||
Termín odeslání {{ac.cislo}}. série pro účast na soustředění:
|
|
||||||
{{ac.datum_deadline_soustredeni}}
|
{% if ac.datum_deadline_soustredeni %}
|
||||||
</div>
|
<span class="datum">{{ac.datum_deadline_soustredeni}}</span> pro účast na soustředění<br>
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% if ac.zadane_problemy.all %}
|
|
||||||
<div class="zadani_azad_termin">
|
|
||||||
Termín odeslání {{ac.cislo}}. série: {{ac.datum_deadline}}
|
|
||||||
</div>
|
|
||||||
{% 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 %}
|
|
||||||
{% 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 %}
|
{% 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 %}
|
||||||
|
|
||||||
|
{% if user.je_org and not verejne%}</div>{% endif %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<h2>Aktuálně nejsou zveřejněny žádné úlohy</h2>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -581,7 +581,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
||||||
rocnik_temata.append(letosni_temata)
|
rocnik_temata.append(letosni_temata)
|
||||||
return rocnik_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. """
|
""" Generování úlohy k danému tématu. """
|
||||||
|
|
||||||
# Proměnné pro náhodné generování názvů a zadání.
|
# 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_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root)
|
||||||
uloha.ulohazadaninode = uloha_zadani
|
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
|
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...')
|
logger.info('Generuji úlohy k tématům...')
|
||||||
|
|
||||||
# Ke každému ročníku si vezmeme příslušná čísla a témata
|
# 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.
|
# Generujeme 1 až 4 úložky k tomuto témátku do tohoto čísla.
|
||||||
for kod in range(1, rnd.randint(1, 4)):
|
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)
|
cislo, cislo_se_vzorakem)
|
||||||
|
|
||||||
insert_last_child(tema_node, uz)
|
insert_last_child(tema_node, uz)
|
||||||
|
@ -860,7 +864,7 @@ def create_test_data(size = 6, rnd = None):
|
||||||
"MFI", 8)
|
"MFI", 8)
|
||||||
|
|
||||||
# generování úloh k tématům ve všech číslech
|
# 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í
|
#generování soustředění
|
||||||
soustredeni = gen_soustredeni(rnd, resitele, organizatori)
|
soustredeni = gen_soustredeni(rnd, resitele, organizatori)
|
||||||
|
|
|
@ -217,6 +217,17 @@ def get_prev_node_of_type(node, type):
|
||||||
return current
|
return current
|
||||||
return None
|
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
|
# 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.urls import path, include, re_path
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from . import views, export
|
from . import views, export
|
||||||
from .utils import org_required, resitel_required
|
from .utils import org_required, resitel_required, viewMethodSwitch
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -9,25 +9,25 @@ urlpatterns = [
|
||||||
# path('<int:rocnik>/t<int:tematko>/', views.TematkoView),
|
# path('<int:rocnik>/t<int:tematko>/', views.TematkoView),
|
||||||
|
|
||||||
# Organizatori
|
# Organizatori
|
||||||
path('co-je-MaM/organizatori/', views.CojemamOrganizatoriView.as_view(), name='organizatori'),
|
path('o-nas/organizatori/', views.CojemamOrganizatoriView.as_view(), name='organizatori'),
|
||||||
path('co-je-MaM/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'),
|
path('o-nas/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'),
|
||||||
|
|
||||||
# Archiv
|
# Archiv
|
||||||
path('archiv/rocniky/', views.ArchivView.as_view(), name="seninar_archiv_rocniky"),
|
path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"),
|
||||||
path('archiv/temata/', views.ArchivTemataView.as_view(), name="seninar_archiv_temata"),
|
path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"),
|
||||||
|
|
||||||
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
|
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
|
||||||
path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'),
|
path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'),
|
||||||
path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'),
|
path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'),
|
||||||
path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'),
|
#path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'),
|
||||||
path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'),
|
#path('treenode/<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/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/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/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/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/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/editor/prohodit/<int:pk>/', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'),
|
||||||
path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'),
|
#path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'),
|
||||||
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'),
|
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'),
|
||||||
|
|
||||||
# Soustredeni
|
# Soustredeni
|
||||||
|
@ -57,13 +57,14 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
|
|
||||||
# Zadani
|
# Zadani
|
||||||
path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'),
|
# path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'),
|
||||||
path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'),
|
path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'),
|
||||||
#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'),
|
#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'),
|
path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'),
|
||||||
|
|
||||||
# Clanky
|
# 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'),
|
#path('clanky/org/', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'),
|
||||||
|
|
||||||
# Aesop
|
# Aesop
|
||||||
|
@ -123,11 +124,6 @@ urlpatterns = [
|
||||||
org_required(views.soustredeniObalkyView),
|
org_required(views.soustredeniObalkyView),
|
||||||
name='seminar_soustredeni_obalky'
|
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
|
# příprava na nestatický orgorozcestník
|
||||||
path(
|
path(
|
||||||
'org/rozcestnik/',
|
'org/rozcestnik/',
|
||||||
|
@ -136,16 +132,16 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
|
|
||||||
path('prihlaska/',views.prihlaskaView, name='seminar_prihlaska'),
|
path('prihlaska/',views.prihlaskaView, name='seminar_prihlaska'),
|
||||||
path('login/', views.LoginView.as_view(), name='login'),
|
path('prihlasit/', views.LoginView.as_view(), name='login'),
|
||||||
path('logout/', views.LogoutView.as_view(), name='logout'),
|
path('odhlasit/', views.LogoutView.as_view(), name='logout'),
|
||||||
path('resitel/', resitel_required(views.ResitelView.as_view()), name='seminar_resitel'),
|
path('resitel/', resitel_required(views.ResitelView.as_view()), name='seminar_resitel'),
|
||||||
path('reset_password/', views.PasswordResetView.as_view(), name='reset_password'),
|
path('reset-hesla/', views.PasswordResetView.as_view(), name='reset_password'),
|
||||||
path('change_password/', views.PasswordChangeView.as_view(), name='change_password'),
|
path('zmena-hesla/', views.PasswordChangeView.as_view(), name='change_password'),
|
||||||
path('reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'),
|
path('reset-hesla/2/', views.PasswordResetDoneView.as_view(), name='reset_password_done'),
|
||||||
path('reset_password_confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
|
path('reset-hesla/potvrzeni/<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/hotovo/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'),
|
||||||
path(
|
path(
|
||||||
'resitel_edit',
|
'resitel/osobni-udaje/',
|
||||||
login_required(views.resitelEditView, login_url='/login/'),
|
login_required(views.resitelEditView, login_url='/login/'),
|
||||||
name='seminar_resitel_edit'
|
name='seminar_resitel_edit'
|
||||||
),
|
),
|
||||||
|
@ -154,9 +150,9 @@ urlpatterns = [
|
||||||
path('profil/', views.profilView, name='profil'),
|
path('profil/', views.profilView, name='profil'),
|
||||||
|
|
||||||
# Autocomplete
|
# Autocomplete
|
||||||
path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'),
|
path('api/autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'),
|
||||||
path('autocomplete/resitel/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'),
|
path('api/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/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/add_solution', org_required(views.AddSolutionView.as_view()), name='seminar_vloz_reseni'),
|
||||||
path('temp/nahraj_reseni', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'),
|
path('temp/nahraj_reseni', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'),
|
||||||
|
@ -174,8 +170,7 @@ urlpatterns = [
|
||||||
|
|
||||||
path('temp/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),
|
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: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/all', org_required(views.SeznamReseniView.as_view())),
|
||||||
path('temp/reseni/akt', org_required(views.SeznamAktualnichReseniView.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 import get_user_model
|
||||||
from django.contrib.auth.decorators import permission_required
|
from django.contrib.auth.decorators import permission_required
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
from django import views as DjangoViews
|
||||||
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.contrib.contenttypes.models import ContentType
|
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)
|
org_required = permission_required('auth.org', raise_exception=True)
|
||||||
resitel_required = permission_required('auth.resitel', raise_exception=True)
|
resitel_required = permission_required('auth.resitel', raise_exception=True)
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
User.je_org = lambda self: self.has_perm('auth.org')
|
# Není to úplně hezké, ale budeme doufat, že to je funkční...
|
||||||
User.je_resitel = lambda self: self.has_perm('auth.resitel')
|
User.je_org = property(lambda self: self.has_perm('auth.org'))
|
||||||
AnonymousUser.je_org = lambda self: False
|
User.je_resitel = property(lambda self: self.has_perm('auth.resitel'))
|
||||||
AnonymousUser.je_resitel = lambda self: False
|
AnonymousUser.je_org = False
|
||||||
|
AnonymousUser.je_resitel = False
|
||||||
|
|
||||||
|
|
||||||
class FirstTagParser(HTMLParser):
|
class FirstTagParser(HTMLParser):
|
||||||
|
@ -191,3 +193,92 @@ def aktivniResitele(cislo, pouze_letosni=False):
|
||||||
else:
|
else:
|
||||||
# spojíme querysety s řešiteli loni a letos do daného čísla
|
# spojíme querysety s řešiteli loni a letos do daného čísla
|
||||||
return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct()
|
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 dal import autocomplete
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
from .helpers import LoginRequiredAjaxMixin
|
from .helpers import LoginRequiredAjaxMixin
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView, FormView
|
||||||
from django.views.generic.base import TemplateView
|
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
|
from dataclasses import dataclass
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
import seminar.models as m
|
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
|
from seminar.utils import aktivniResitele, resi_v_rocniku
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Co chceme?
|
# Co chceme?
|
||||||
# - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení
|
# - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení
|
||||||
# - TabulkaOdevzdanychReseniView
|
# - TabulkaOdevzdanychReseniView
|
||||||
|
@ -30,26 +40,60 @@ class TabulkaOdevzdanychReseniView(ListView):
|
||||||
template_name = 'seminar/odevzdavatko/tabulka.html'
|
template_name = 'seminar/odevzdavatko/tabulka.html'
|
||||||
model = m.Hodnoceni
|
model = m.Hodnoceni
|
||||||
|
|
||||||
def get_queryset(self):
|
def inicializuj_osy_tabulky(self):
|
||||||
# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení.
|
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů"""
|
||||||
self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
|
# FIXME: jméno metody není vypovídající...
|
||||||
self.resitele = resi_v_rocniku(self.akt_rocnik)
|
# 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
|
||||||
# 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.
|
# TODO: Prefetches, Select related, ...
|
||||||
self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic()
|
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 = 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
|
return qs
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
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.resitele, self.reseni a self.problemy jsou již 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()
|
|
||||||
|
|
||||||
ctx = super().get_context_data(*args, **kwargs)
|
ctx = super().get_context_data(*args, **kwargs)
|
||||||
ctx['problemy'] = self.zadane_problemy
|
ctx['problemy'] = self.problemy
|
||||||
ctx['resitele'] = self.resitele
|
ctx['resitele'] = self.resitele
|
||||||
tabulka = dict()
|
tabulka = dict()
|
||||||
|
|
||||||
|
@ -76,7 +120,7 @@ class TabulkaOdevzdanychReseniView(ListView):
|
||||||
hodnoty = []
|
hodnoty = []
|
||||||
for resitel in self.resitele:
|
for resitel in self.resitele:
|
||||||
resiteluv_radek = []
|
resiteluv_radek = []
|
||||||
for problem in self.zadane_problemy:
|
for problem in self.problemy:
|
||||||
if problem in tabulka and resitel in tabulka[problem]:
|
if problem in tabulka and resitel in tabulka[problem]:
|
||||||
resiteluv_radek.append(tabulka[problem][resitel])
|
resiteluv_radek.append(tabulka[problem][resitel])
|
||||||
else:
|
else:
|
||||||
|
@ -84,9 +128,14 @@ class TabulkaOdevzdanychReseniView(ListView):
|
||||||
hodnoty.append(resiteluv_radek)
|
hodnoty.append(resiteluv_radek)
|
||||||
ctx['radky'] = list(zip(self.resitele, hodnoty))
|
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
|
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
|
model = m.Reseni
|
||||||
template_name = 'seminar/odevzdavatko/seznam.html'
|
template_name = 'seminar/odevzdavatko/seznam.html'
|
||||||
|
|
||||||
|
@ -107,12 +156,73 @@ class ReseniProblemuView(ListView):
|
||||||
)
|
)
|
||||||
return qs
|
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?
|
# Kontext automaticky?
|
||||||
|
|
||||||
|
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
|
||||||
class DetailReseniView(DetailView):
|
class DetailReseniView(DetailView):
|
||||||
model = m.Reseni
|
model = m.Reseni
|
||||||
template_name = 'seminar/odevzdavatko/detail.html'
|
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í
|
# 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.forms as f
|
||||||
import seminar.templatetags.treenodes as tnltt
|
import seminar.templatetags.treenodes as tnltt
|
||||||
import seminar.views.views_rest as vr
|
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 datetime import timedelta, date, datetime, MAXYEAR
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -48,7 +49,7 @@ import csv
|
||||||
import logging
|
import logging
|
||||||
import time
|
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
|
# ze starého modelu
|
||||||
#def verejna_temata(rocnik):
|
#def verejna_temata(rocnik):
|
||||||
|
@ -63,20 +64,6 @@ from seminar.utils import aktivniResitele, resi_v_rocniku
|
||||||
def get_problemy_k_tematu(tema):
|
def get_problemy_k_tematu(tema):
|
||||||
return Problem.objects.filter(nadproblem = 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):
|
class ObalkovaniView(generic.ListView):
|
||||||
template_name = 'seminar/org/obalkovani.html'
|
template_name = 'seminar/org/obalkovani.html'
|
||||||
|
|
||||||
|
@ -198,7 +185,7 @@ class TNLData(object):
|
||||||
return [cls.from_treenode(treenode)]
|
return [cls.from_treenode(treenode)]
|
||||||
else:
|
else:
|
||||||
found = []
|
found = []
|
||||||
for tn in all_children(treenode):
|
for tn in treelib.all_children(treenode):
|
||||||
result = cls.filter_treenode(tn, predicate)
|
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.
|
# 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:
|
for tnl in result:
|
||||||
|
@ -397,8 +384,8 @@ class ProblemView(generic.DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class AktualniZadaniView(generic.TemplateView):
|
#class AktualniZadaniView(generic.TemplateView):
|
||||||
template_name = 'seminar/treenode.html'
|
# template_name = 'seminar/treenode.html'
|
||||||
|
|
||||||
# TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného...
|
# TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného...
|
||||||
#class AktualniZadaniView(TreeNodeView):
|
#class AktualniZadaniView(TreeNodeView):
|
||||||
|
@ -413,21 +400,15 @@ class AktualniZadaniView(generic.TemplateView):
|
||||||
# context['verejne'] = verejne
|
# context['verejne'] = verejne
|
||||||
# return context
|
# return context
|
||||||
|
|
||||||
#def AktualniZadaniView(request):
|
def AktualniZadaniView(request):
|
||||||
# nastaveni = get_object_or_404(Nastaveni)
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
# verejne = nastaveni.aktualni_cislo.verejne()
|
verejne = nastaveni.aktualni_cislo.verejne()
|
||||||
# problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany')
|
return render(request, 'seminar/zadani/AktualniZadani.html',
|
||||||
# ulohy = problemy.filter(typ = 'uloha').order_by('kod')
|
{'nastaveni': nastaveni,
|
||||||
# serialy = problemy.filter(typ = 'serial').order_by('kod')
|
'verejne': verejne,
|
||||||
# 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 ZadaniTemataView(request):
|
def ZadaniTemataView(request):
|
||||||
nastaveni = get_object_or_404(Nastaveni)
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
verejne = nastaveni.aktualni_cislo.verejne()
|
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})
|
# return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik})
|
||||||
#
|
#
|
||||||
|
|
||||||
#def ZadaniAktualniVysledkovkaView(request):
|
def ZadaniAktualniVysledkovkaView(request):
|
||||||
# nastaveni = get_object_or_404(Nastaveni)
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
# # Aktualni verejna vysledkovka
|
# Aktualni verejna vysledkovka
|
||||||
# vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik)
|
vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik)
|
||||||
# # kdyz neni verejna vysledkovka, tak zobraz starou
|
cisla = cisla_rocniku(nastaveni.aktualni_rocnik)
|
||||||
# if not vysledkovka:
|
# kdyz neni verejna vysledkovka, tak zobraz starou
|
||||||
# try:
|
if not vysledkovka:
|
||||||
# minuly_rocnik = Rocnik.objects.get(
|
try:
|
||||||
# prvni_rok=(nastaveni.aktualni_rocnik.prvni_rok-1))
|
minuly_rocnik = Rocnik.objects.get(
|
||||||
# vysledkovka = vysledkovka_rocniku(minuly_rocnik)
|
prvni_rok=(nastaveni.aktualni_rocnik.prvni_rok-1))
|
||||||
# except ObjectDoesNotExist:
|
vysledkovka = vysledkovka_rocniku(minuly_rocnik)
|
||||||
# pass
|
cisla = cisla_rocniku(minuly_rocnik)
|
||||||
# # vysledkovka s neverejnyma vysledkama
|
except ObjectDoesNotExist:
|
||||||
# vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False)
|
pass
|
||||||
# return render(
|
# vysledkovka s neverejnyma vysledkama
|
||||||
# request,
|
vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False)
|
||||||
# 'seminar/zadani/AktualniVysledkovka.html',
|
return render(
|
||||||
# {
|
request,
|
||||||
# 'nastaveni': nastaveni,
|
'seminar/zadani/AktualniVysledkovka.html',
|
||||||
# 'vysledkovka': vysledkovka,
|
{
|
||||||
# 'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi,
|
'nastaveni': nastaveni,
|
||||||
# }
|
'radky_vysledkovky': vysledkovka,
|
||||||
# )
|
'cisla': cisla,
|
||||||
|
'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
### Titulni strana
|
### Titulni strana
|
||||||
|
@ -568,6 +552,8 @@ class TitulniStranaView(generic.ListView):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0]
|
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:
|
except IndexError:
|
||||||
nejblizsi_deadline = (None, None) # neni zadna aktualni deadline
|
nejblizsi_deadline = (None, None) # neni zadna aktualni deadline
|
||||||
|
|
||||||
|
@ -649,236 +635,9 @@ class ArchivView(generic.ListView):
|
||||||
|
|
||||||
return context
|
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):
|
class RocnikView(generic.DetailView):
|
||||||
model = Rocnik
|
model = Rocnik
|
||||||
|
@ -888,29 +647,25 @@ class RocnikView(generic.DetailView):
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
rocnik_arg = self.kwargs.get('rocnik')
|
|
||||||
queryset = queryset.filter(rocnik=rocnik_arg)
|
|
||||||
|
|
||||||
try:
|
return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik'))
|
||||||
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
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
start = time.time()
|
||||||
context = super(RocnikView, self).get_context_data(**kwargs)
|
context = super(RocnikView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
# vysledkovka = True zajistí vykreslení,
|
# vysledkovka = True zajistí vykreslení,
|
||||||
# zkontrolovat, kdy se má a nemá vykreslovat
|
# zkontrolovat, kdy se má a nemá vykreslovat
|
||||||
context['vysledkovka'] = True
|
context['vysledkovka'] = True
|
||||||
context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False)
|
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['cisla'] = cisla_rocniku(context["rocnik"])
|
||||||
context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"])
|
context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"])
|
||||||
context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku(
|
context['hlavni_problemy_v_rocniku'] = hlavni_problemy_f(problemy_rocniku(context["rocnik"]))
|
||||||
context["rocnik"], jen_verejne=False)
|
end = time.time()
|
||||||
context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"])
|
print("Kontext:", end-start)
|
||||||
context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False)
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -940,179 +695,6 @@ class ProblemView(generic.DetailView):
|
||||||
return context
|
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):
|
class CisloView(generic.DetailView):
|
||||||
# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf
|
# 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!
|
# FIXME: QuerySet není pole!
|
||||||
def get_queryset(self):
|
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 = []
|
queryset = []
|
||||||
skupiny_clanku = group_by_rocnik(clanky)
|
skupiny_clanku = group_by_rocnik(clanky)
|
||||||
for skupina in skupiny_clanku:
|
for skupina in skupiny_clanku:
|
||||||
|
@ -1476,36 +1058,6 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
||||||
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
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):
|
def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
|
||||||
msg = "{}, form_hash:{}".format(msg,hash(frozenset(form_data.items)))
|
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
|
## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
|
||||||
u = request.user
|
u = request.user
|
||||||
osoba_edit = Osoba.objects.get(user=u)
|
osoba_edit = Osoba.objects.get(user=u)
|
||||||
resitel_edit = osoba_edit.resitel
|
if hasattr(osoba_edit,'resitel'):
|
||||||
|
resitel_edit = osoba_edit.resitel
|
||||||
|
else:
|
||||||
|
resitel_edit = None
|
||||||
user_edit = osoba_edit.user
|
user_edit = osoba_edit.user
|
||||||
## Vytvoření slovníku, kterým předvyplním formulář
|
## Vytvoření slovníku, kterým předvyplním formulář
|
||||||
prefill_1=model_to_dict(user_edit)
|
prefill_1=model_to_dict(user_edit)
|
||||||
prefill_2=model_to_dict(resitel_edit)
|
if resitel_edit:
|
||||||
|
prefill_2=model_to_dict(resitel_edit)
|
||||||
|
prefill_1.update(prefill_2)
|
||||||
prefill_3=model_to_dict(osoba_edit)
|
prefill_3=model_to_dict(osoba_edit)
|
||||||
prefill_1.update(prefill_2)
|
|
||||||
prefill_1.update(prefill_3)
|
prefill_1.update(prefill_3)
|
||||||
form = ProfileEditForm(initial=prefill_1)
|
form = ProfileEditForm(initial=prefill_1)
|
||||||
## Změna údajů a jejich uložení
|
## Změna údajů a jejich uložení
|
||||||
|
@ -1550,17 +1106,18 @@ def resitelEditView(request):
|
||||||
## Neznámá země
|
## Neznámá země
|
||||||
msg = "Unknown country {}".format(fcd['stat_text'])
|
msg = "Unknown country {}".format(fcd['stat_text'])
|
||||||
|
|
||||||
## Změny v řešiteli
|
if resitel_edit:
|
||||||
resitel_edit.skola = fcd['skola']
|
## Změny v řešiteli
|
||||||
resitel_edit.rok_maturity = fcd['rok_maturity']
|
|
||||||
resitel_edit.zasilat = fcd['zasilat']
|
|
||||||
resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
|
|
||||||
if fcd.get('skola'):
|
|
||||||
resitel_edit.skola = fcd['skola']
|
resitel_edit.skola = fcd['skola']
|
||||||
else:
|
resitel_edit.rok_maturity = fcd['rok_maturity']
|
||||||
# Unknown school - log it
|
resitel_edit.zasilat = fcd['zasilat']
|
||||||
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
|
||||||
resitel_edit.save()
|
if fcd.get('skola'):
|
||||||
|
resitel_edit.skola = fcd['skola']
|
||||||
|
else:
|
||||||
|
# Unknown school - log it
|
||||||
|
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
||||||
|
resitel_edit.save()
|
||||||
osoba_edit.save()
|
osoba_edit.save()
|
||||||
return formularOKView(request)
|
return formularOKView(request)
|
||||||
else:
|
else:
|
||||||
|
@ -1649,7 +1206,7 @@ class LoginView(auth_views.LoginView):
|
||||||
# Přesměrovací URL má být v kontextu:
|
# Přesměrovací URL má být v kontextu:
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data(**kwargs)
|
ctx = super().get_context_data(**kwargs)
|
||||||
ctx['next'] = reverse('titulni_strana')
|
ctx['next'] = reverse('profil')
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
class LogoutView(auth_views.LogoutView):
|
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