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