diff --git a/data/sitetree_new.json b/data/sitetree_new.json index f0eb3a9c..82fc8245 100644 --- a/data/sitetree_new.json +++ b/data/sitetree_new.json @@ -1 +1,816 @@ -[{"model": "sitetree.tree", "pk": 1, "fields": {"title": "Hlavn\u00ed 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 \u0159e\u0161it", "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\u00e1ln\u00ed
ro\u010dn\u00edk", "hint": "", "url": "/zadani/aktualni/", "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": 3, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 4, "fields": {"title": "Soust\u0159ed\u011bn\u00ed", "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": "/archiv/rocniky/", "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": 5, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 6, "fields": {"title": "P\u0159ihl\u00e1sit", "hint": "", "url": "/login/", "urlaspattern": false, "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": "\u00davod", "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\u00e1to\u0159i", "hint": "", "url": "/co-je-MaM/organizatori/", "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": 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\u00e9mata", "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\u00e1t p\u0159\u00edsp\u011bvek", "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\u011bny", "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\u00fdsledkov\u00e1 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": "\u010cl\u00e1nky", "hint": "", "url": "/clanky/resitel/", "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": 34, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 18, "fields": {"title": "\u00davod", "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\u0159ipravujeme", "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\u011bhlo", "hint": "", "url": "/soustredeni/probehlo/", "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": 20, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 21, "fields": {"title": "Profil", "hint": "", "url": "/profil/", "urlaspattern": false, "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\u00ed \u00fadaje", "hint": "", "url": "/profil/osobni-udaje", "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": 21, "sort_order": 23, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 23, "fields": {"title": "Poslat \u0159e\u0161en\u00ed", "hint": "", "url": "/odeslat-reseni/", "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": 21, "sort_order": 36, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 24, "fields": {"title": "T\u00e9mata", "hint": "", "url": "/archiv/temata/", "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": 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\u00e1ln\u00ed", "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": false, "access_perm_type": 1, "parent": 28, "sort_order": 30, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 31, "fields": {"title": "Zastaral\u00e9", "hint": "", "url": "/korektury/zastarale/", "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": 31, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 32, "fields": {"title": "N\u00e1pov\u011bda", "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\u00e1ln\u00ed \u010d\u00edslo", "hint": "", "url": "/zadani/aktualni/", "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": 15, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 34, "fields": {"title": "T\u00e9mata", "hint": "", "url": "/zadani/temata/", "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": 17, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 35, "fields": {"title": "\u010c\u00edsla", "hint": "", "url": "/archiv/rocniky/", "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": 5, "sort_order": 24, "access_permissions": []}}, {"model": "sitetree.treeitem", "pk": 36, "fields": {"title": "\u00davod", "hint": "", "url": "/profil/", "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": 21, "sort_order": 22, "access_permissions": []}}] \ No newline at end of file +[ + { + "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í
ročník", + "hint": "", + "url": "/zadani/aktualni/", + "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": 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": "/archiv/rocniky/", + "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": 5, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 6, + "fields": { + "title": "Přihlásit", + "hint": "", + "url": "/login/", + "urlaspattern": false, + "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": "/co-je-MaM/organizatori/", + "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": 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": 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": 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": "/soustredeni/probehlo/", + "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": 20, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 21, + "fields": { + "title": "Profil", + "hint": "", + "url": "/profil/", + "urlaspattern": false, + "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": "/profil/osobni-udaje", + "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": 21, + "sort_order": 23, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 23, + "fields": { + "title": "Poslat řešení", + "hint": "", + "url": "/odeslat-reseni/", + "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": 21, + "sort_order": 36, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 24, + "fields": { + "title": "Témata", + "hint": "", + "url": "/archiv/temata/", + "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": 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/", + "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": 30, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 31, + "fields": { + "title": "Zastaralé", + "hint": "", + "url": "/korektury/zastarale/", + "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": 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": "/zadani/aktualni/", + "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": 15, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 34, + "fields": { + "title": "Témata", + "hint": "", + "url": "/zadani/temata/", + "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": 17, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 35, + "fields": { + "title": "Čísla", + "hint": "", + "url": "/archiv/rocniky/", + "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": 5, + "sort_order": 24, + "access_permissions": [ + + ] + } + }, + { + "model": "sitetree.treeitem", + "pk": 36, + "fields": { + "title": "Úvod", + "hint": "", + "url": "/profil/", + "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": 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 + ] + } + } +] diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 0f1d3812..7a2bf85b 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -123,6 +123,7 @@ INSTALLED_APPS = ( 'webpack_loader', 'rest_framework', + 'rest_framework.authtoken', # MaMweb 'mamweb', @@ -294,6 +295,9 @@ LOGGING = { }, } +# Permissions for uploads +FILE_UPLOAD_PERMISSIONS = 0o0644 + # MaM specific SEMINAR_RESENI_DIR = os.path.join('reseni') diff --git a/seminar/.~lock.profile_vysledkovka.txt# b/seminar/.~lock.profile_vysledkovka.txt# new file mode 100644 index 00000000..cf1b89b4 --- /dev/null +++ b/seminar/.~lock.profile_vysledkovka.txt# @@ -0,0 +1 @@ +,anet,erebus,25.03.2020 22:21,file:///home/anet/.config/libreoffice/4; \ No newline at end of file diff --git a/seminar/models.py b/seminar/models.py index cf8cd6a9..28e6c42f 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -743,12 +743,20 @@ class Problem(SeminarModelBase,PolymorphicModel): return '' def verejne(self): - # FIXME: Tohle se liší podle typu problému, má se udělat polymorfně. - # Zatím je tu jen dummy fail-safe default: nic není veřejné. - # Doporučené řešení: dělat tohle podle stavu problému a veřejnosti čísla, ve kterém je - return False - # FIXME: Tohle je blbost - return (self.cislo_zadani and self.cislo_zadani.verejne()) + # 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ě? + 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) verejne.boolean = True def verejne_url(self): @@ -986,7 +994,7 @@ def aux_generate_filename(self, filename): unidecode(filename.replace('/', '-').replace('\0', '')) ) datedir = timezone.now().strftime('%Y-%m') - fname = "{}_{}".format( + fname = "{}/{}".format( timezone.now().strftime('%Y-%m-%d-%H:%M'), clean) return os.path.join(datedir, fname) @@ -1039,6 +1047,11 @@ class PrilohaReseni(SeminarModelBase): def __str__(self): return str(self.soubor) + def split(self): + "Vrátí cestu rozsekanou po složkách. To se hodí v templatech" + # Věřím, že tohle funguje, případně použít os.path nebo pathlib. + return self.soubor.url.split('/') + class Pohadka(SeminarModelBase): """Kus pohádky před/za úlohou v čísle""" diff --git a/seminar/permissions.py b/seminar/permissions.py new file mode 100644 index 00000000..5503832f --- /dev/null +++ b/seminar/permissions.py @@ -0,0 +1,7 @@ +from rest_framework.permissions import BasePermission + +class AllowWrite(BasePermission): + + def has_permission(self, request, view): + return request.user.has_perm('auth.org') + diff --git a/seminar/templates/seminar/archiv/cislo-normal.html b/seminar/templates/seminar/archiv/cislo-normal.html new file mode 100644 index 00000000..e23da09c --- /dev/null +++ b/seminar/templates/seminar/archiv/cislo-normal.html @@ -0,0 +1,97 @@ +{% extends "seminar/archiv/base_cisla.html" %} + +{# {% block content %} +
+ +

+ {% block nadpis1a %}{% block nadpis1b %} + Číslo {{ cislo }} + {% endblock %}{% endblock %} +

+ + {% if cislo.pdf %} +

Číslo v pdf + {% endif %} +

Ročník {{ cislo.rocnik }} + + {% if v_cisle_zadane %} +

Zadané problémy

+ + {% endif %} + + {% if resene_problemy %} +

Řešené problémy

+ + {% endif %} + + {% if user.is_staff %} +
+

Orgovské odkazy

+ +
+ {% endif %} + + {% if cislo.verejna_vysledkovka %} +

Výsledkovka

+ {% else %} + {% if user.is_staff %} +
+

Výsledkovka (neveřejná)

+ {% endif %} + {% endif %} + + {% if cislo.verejna_vysledkovka or user.is_staff %} + + + + + {% endfor %} +
# + Jméno #} + {# problémy by měly být veřejné, když je veřejná výsledkovka #} +{# {% for p in problemy %} + {{ p.kod_v_rocniku }} + {% endfor %} + Za číslo + Za ročník + Odjakživa + {% for rv in radky_vysledkovky %} +
{% autoescape off %}{{ rv.poradi }}{% endautoescape %} + + {% if rv.resitel.titul != "" %} + {{ rv.resitel.titul }}MM + {% endif %} + {{ rv.resitel.osoba.plne_jmeno }} + {% for b in rv.hlavni_problemy_body %} + {{ b }} + {% endfor %} + {{ rv.body_cislo }} + {{ rv.body_rocnik }} + {{ rv.body_celkem_odjakziva }} +
+ {% endif %} + + {% if not cislo.verejna_vysledkovka and user.is_staff %} +
+ {% endif %} + +
+{% endblock content %} #} + diff --git a/seminar/templates/seminar/archiv/problem_tema.html b/seminar/templates/seminar/archiv/problem_tema.html new file mode 100644 index 00000000..421d73bd --- /dev/null +++ b/seminar/templates/seminar/archiv/problem_tema.html @@ -0,0 +1,19 @@ +{% extends "seminar/archiv/problem.html" %} + +{% block problem %} +

+ {% block nadpis1a %}{% block nadpis1b %} + {{ problem.nazev_typu }} {{ problem.kod_v_rocniku }}: {{ problem.nazev }} + {% endblock %}{% endblock %} +

+ +

Zadání

+ {{ problem.text_zadani |safe }} + {% if problem.text_reseni %} +

Řešení

+ {{ problem.text_reseni |safe }} + {% endif %} + + {# TODO vysledkovka tematu #} + +{% endblock %} diff --git a/seminar/templates/seminar/archiv/problem_uloha.html b/seminar/templates/seminar/archiv/problem_uloha.html new file mode 100644 index 00000000..df5e97f7 --- /dev/null +++ b/seminar/templates/seminar/archiv/problem_uloha.html @@ -0,0 +1,23 @@ +{% extends "seminar/archiv/problem.html" %} + +{% block problem %} +

+ {% block nadpis1a %}{% block nadpis1b %} + {{ problem.nazev_typu }} {{ problem.kod_v_rocniku }}: {{ problem.nazev }} {{ problem.body_v_zavorce }} + {% endblock %}{% endblock %} +

+ {% if problem.cislo_zadani %} +

Zadáno v čísle {{ problem.cislo_zadani.kod }}. + {% endif %} + {% if problem.cislo_reseni %} +

Řešeno v čísle {{ problem.cislo_reseni.kod }}. + {% endif %} + +

Zadání

+ {{ problem.text_zadani |safe }} + {% if problem.text_reseni %} +

Řešení

+ {{ problem.text_reseni |safe }} + {% endif %} + +{% endblock %} diff --git a/seminar/templates/seminar/odevzdavatko/detail.html b/seminar/templates/seminar/odevzdavatko/detail.html new file mode 100644 index 00000000..6cee990d --- /dev/null +++ b/seminar/templates/seminar/odevzdavatko/detail.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} + +{% block content %} + +

Řešené problémy: {{ object.problem.all | join:", " }}

+ +

Řešitelé: {{ object.resitele.all | join:", " }}

+ +{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} +

Forma: {{ object.get_forma_display }}, doručeno {{ object.cas_doruceni }}

+ +{# Soubory: #} +

Přílohy:

+{% if object.prilohy.all %} + + +{% for priloha in object.prilohy.all %} + + + + + {# TODO: Orgo-poznámka, ideálně jako formulář #} +{% endfor %} +
SouborŘešitelova poznámkaDatum
{{ priloha.split | last }}{{ priloha.res_poznamka }}{{ priloha.vytvoreno }}
+{% else %} +

Žádné přílohy

+{% endif %} + +{# Hodnocení: #} +{# FIXME: Udělat jako formulář #} +

Hodnocení:

+{% if object.hodnoceni_set.all %} + + +{% for h in object.hodnoceni_set.all %} + + + + +{% endfor %} +
ProblémBodyČíslo pro body
{{ h.problem }}{{ h.body }}{{ h.cislo_body }}
+{% else %} +

Ještě nebylo hodnoceno

+{% endif %} + + +{% endblock %} diff --git a/seminar/templates/seminar/odevzdavatko/seznam.html b/seminar/templates/seminar/odevzdavatko/seznam.html new file mode 100644 index 00000000..b58dcb54 --- /dev/null +++ b/seminar/templates/seminar/odevzdavatko/seznam.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block content %} + + + +{% endblock %} diff --git a/seminar/templates/seminar/odevzdavatko/tabulka.html b/seminar/templates/seminar/odevzdavatko/tabulka.html new file mode 100644 index 00000000..ff396ce4 --- /dev/null +++ b/seminar/templates/seminar/odevzdavatko/tabulka.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} + +{% load utils %} {# Možná by mohlo být někde výš v hierarchii templatů... #} + +{% block content %} + + + + {# Prázdná buňka v levém horním rohu #} + {% for p in problemy %} + + {% endfor %} + + {% for resitel,hodnoty in radky%} + + + {% for hodn in hodnoty %} + + {% endfor %} + + {% endfor %} +
+ {# TODO: Přehled řešení k problému, odkázaný odsud? #} + {{ p }} +
+ {# TODO: Chceme mít view i na řešení konkrétního řešitele ke všem problémům? #} + {{ resitel }} + + {% if hodn %} + + {{ hodn.pocet_reseni }} řeš.
{{ hodn.body }} b
{{ hodn.posledni_odevzdani|kratke_datum|default_if_none:"Nikdy"|default:"???"}} +
+ {% endif %} +
+ +{% endblock %} diff --git a/seminar/templatetags/utils.py b/seminar/templatetags/utils.py new file mode 100644 index 00000000..1cb92d6a --- /dev/null +++ b/seminar/templatetags/utils.py @@ -0,0 +1,27 @@ +from django import template +from datetime import datetime, timedelta +from pytz import timezone +from mamweb.settings import TIME_ZONE +import logging +register = template.Library() + +logger = logging.getLogger(__name__) + +@register.filter(name='kratke_datum', expects_localtime=True) +def kratke_datum(dt): + # None dává None, ne-datum dává False, aby se daly použít filtry typu "default". + if dt is None: + return None + if not isinstance(dt, datetime): + logger.warning(f"Špatné volání filtru {__name__}: {dt}") + return False + naive_now = datetime.now() + tz = timezone(TIME_ZONE) + now = tz.localize(naive_now) + delta = now - dt + if delta <= timedelta(days=1): + return dt.strftime("%k:%M") + if delta <= timedelta(days=365): # Timedelta neumí vyjádřit 1 rok + return dt.strftime("%d. %m.") + return dt.strftime("%d. %m. %Y") + diff --git a/seminar/testutils.py b/seminar/testutils.py index 3e81f356..94fa78b6 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -140,7 +140,7 @@ def gen_resitele(rnd, osoby, skoly): x += 1 os.user = user os.save() - os.user.user_permissions.add(resitel_perm) + os.user.user_permissions.add(resitel_perm) resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly), rok_maturity=rnd.randint(2019, 2029), zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0])) @@ -199,7 +199,7 @@ def gen_organizatori(rnd, osoby, last_rocnik): x += 1 os.user = user os.save() - os.user.user_permissions.add(org_perm) + os.user.user_permissions.add(org_perm) organizatori.append(Organizator.objects.create(osoba=os, organizuje_od=od, organizuje_do=do, strucny_popis_organizatora = popis_orga)) return organizatori diff --git a/seminar/urls.py b/seminar/urls.py index 91e0ce15..bf0658d6 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -168,5 +168,10 @@ urlpatterns = [ # org_member_required(views.OrganizatorAutocomplete.as_view()), # name='seminar_autocomplete_organizator') + path('temp/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), + path('temp/reseni///', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'), + path('temp/reseni/', org_required(views.DetailReseniView.as_view()), name='odevzdavatko_detail_reseni'), + path('temp/reseni/all', org_required(views.SeznamReseniView.as_view())), + path('temp/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), ] diff --git a/seminar/utils.py b/seminar/utils.py index ad9be95e..bcc67013 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -148,16 +148,12 @@ def resi_v_rocniku(rocnik, cislo=None): if cislo is None: # filtrujeme pouze podle ročníku - letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik) + return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), + reseni__hodnoceni__cislo_body__rocnik=rocnik).distinct() else: # filtrujeme podle ročníku i čísla - letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik, - hodnoceni__cislo_body__poradi__lte=cislo.poradi) - - # vygenerujeme queryset řešitelů, co letos něco poslali - letosni_resitele = m.Resitel.objects.none() - for reseni in letosni_reseni: - letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok()) - return letosni_resitele.distinct() + return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), + reseni__hodnoceni__cislo_body__rocnik=rocnik, + reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi).distinct() def aktivniResitele(cislo, pouze_letosni=False): diff --git a/seminar/views/__init__.py b/seminar/views/__init__.py index 222d19bb..a9eb3ea9 100644 --- a/seminar/views/__init__.py +++ b/seminar/views/__init__.py @@ -1,3 +1,4 @@ from .views_all import * from .autocomplete import * from .views_rest import * +from .odevzdavatko import * diff --git a/seminar/views/odevzdavatko.py b/seminar/views/odevzdavatko.py new file mode 100644 index 00000000..de8ceec3 --- /dev/null +++ b/seminar/views/odevzdavatko.py @@ -0,0 +1,129 @@ +from django.views.generic import ListView, DetailView +from django.views.generic.base import TemplateView + +from dataclasses import dataclass +import datetime + +import seminar.models as m +from seminar.utils import aktivniResitele, resi_v_rocniku + +# Co chceme? +# - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení +# - TabulkaOdevzdanychReseniView +# - Detail konkrétního problému a řešitele -- přehled všech řešení odevzdaných k tomuto problému +# - ReseniProblemuView +# - Detail konkrétního řešení -- všechny soubory, datum, ... +# - DetailReseniView +# +# Taky se může hodit: +# - Tabulka všech řešitelů x všech problémů? + +@dataclass +class SouhrnReseni: + """Dataclass reprezentující data o odevzdaných řešeních pro zobrazení v tabulce.""" + pocet_reseni : int + posledni_odevzdani : datetime.datetime + body : float + + +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() + + qs = super().get_queryset() + qs = qs.filter(problem__in=self.zadane_problemy).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() + + ctx = super().get_context_data(*args, **kwargs) + ctx['problemy'] = self.zadane_problemy + ctx['resitele'] = self.resitele + tabulka = dict() + + def pridej_reseni(problem, resitel, body, cas): + if problem not in tabulka: + tabulka[problem] = dict() + if resitel not in tabulka[problem]: + tabulka[problem][resitel] = SouhrnReseni(pocet_reseni=1, posledni_odevzdani=cas, body=body) + else: + tabulka[problem][resitel].posledni_odevzdani = max(tabulka[problem][resitel].posledni_odevzdani, cas) + tabulka[problem][resitel].body = max(tabulka[problem][resitel].body, body, + key=lambda x: x if x is not None else -1 # None je malé číslo + # FIXME: Možná dává smysl i mít None jako velké číslo -- jakože "TODO: zadat body" + ) + tabulka[problem][resitel].pocet_reseni += 1 + # Pro jednoduchost template si ještě poznamenáme ID problému a řešitele + tabulka[problem][resitel].problem_id = problem.id + tabulka[problem][resitel].resitel_id = resitel.id + + for hodnoceni in self.get_queryset(): + for resitel in hodnoceni.reseni.resitele.all(): + pridej_reseni(hodnoceni.problem, resitel, hodnoceni.body, hodnoceni.reseni.cas_doruceni) + + hodnoty = [] + for resitel in self.resitele: + resiteluv_radek = [] + for problem in self.zadane_problemy: + if problem in tabulka and resitel in tabulka[problem]: + resiteluv_radek.append(tabulka[problem][resitel]) + else: + resiteluv_radek.append(None) + hodnoty.append(resiteluv_radek) + ctx['radky'] = list(zip(self.resitele, hodnoty)) + + return ctx + +class ReseniProblemuView(ListView): + model = m.Reseni + template_name = 'seminar/odevzdavatko/seznam.html' + + def get_queryset(self): + qs = super().get_queryset() + resitel_id = self.kwargs['resitel'] + if resitel_id is None: + raise ValueError("Nemám řešitele!") + problem_id = self.kwargs['problem'] + if problem_id is None: + raise ValueError("Nemám problém! (To je problém!)") + + resitel = m.Resitel.objects.get(id=resitel_id) + problem = m.Problem.objects.get(id=problem_id) + qs = qs.filter( + problem__in=[problem], + resitele__in=[resitel], + ) + return qs + + # Kontext automaticky? + +class DetailReseniView(DetailView): + model = m.Reseni + template_name = 'seminar/odevzdavatko/detail.html' + # To je všechno? Najde se to podle pk... + +# Přehled všech řešení kvůli debugování + +class SeznamReseniView(ListView): + model = m.Reseni + template_name = 'seminar/odevzdavatko/seznam.html' + +class SeznamAktualnichReseniView(SeznamReseniView): + def get_queryset(self): + qs = super().get_queryset() + akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi... + resitele = resi_v_rocniku(akt_rocnik) + qs = qs.filter(resitele__in=resitele) # FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel + return qs diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 316211fd..79b75b0f 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -1,4 +1,4 @@ -# coding:utf-8 + from django.shortcuts import get_object_or_404, render, redirect from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse @@ -17,6 +17,7 @@ from django.contrib.auth.models import User, Permission from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction from django.core import serializers +from django.core.exceptions import PermissionDenied from django.forms.models import model_to_dict import seminar.models as s @@ -120,14 +121,57 @@ class TNLData(object): self.appendable_siblings = tnltt.appendableChildren(self.parent) else: self.appendable_siblings = [] - - + @classmethod + def public_above(cls, anode): + """ Returns output of verejne for closest Rocnik, Cislo or Problem above. + (All of them have method verejne.)""" + parent = anode # chceme začít už od konkrétního node včetně + while True: + rocnik = isinstance(parent, s.RocnikNode) + cislo = isinstance(parent, s.CisloNode) + uloha = (isinstance(parent, s.UlohaVzorakNode) or + isinstance(parent, s.UlohaZadaniNode)) + tema = isinstance(parent, s.TemaVCisleNode) + + if (rocnik or cislo or uloha or tema) or parent==None: + break + else: + parent = treelib.get_parent(parent) + if rocnik: + return parent.rocnik.verejne() + elif cislo: + return parent.cislo.verejne() + elif uloha: + return parent.uloha.verejne() + elif tema: + return parent.tema.verejne() + elif None: + print("Existuje TreeNode, který není pod číslem, ročníkem, úlohou" + "ani tématem. {}".format(anode)) + return False + + @classmethod + def all_public_children(cls, anode): + for ch in treelib.all_children(anode): + if TNLData.public_above(ch): + yield ch + else: + continue @classmethod - def from_treenode(cls,anode,parent=None,index=None): - out = cls(anode,parent,index) - for (idx,ch) in enumerate(treelib.all_children(anode)): - outitem = cls.from_treenode(ch,out,idx) + def from_treenode(cls, anode, user, parent=None, index=None): + if TNLData.public_above(anode) or user.has_perm('auth.org'): + out = cls(anode,parent,index) + else: + raise PermissionDenied() + + if user.has_perm('auth.org'): + enum_children = enumerate(treelib.all_children(anode)) + else: + enum_children = enumerate(TNLData.all_public_children(anode)) + + for (idx,ch) in enum_children: + outitem = cls.from_treenode(ch, user, out, idx) out.children.append(outitem) out.add_edit_options() return out @@ -194,7 +238,7 @@ class TreeNodeView(generic.DetailView): def get_context_data(self,**kwargs): context = super().get_context_data(**kwargs) - context['tnldata'] = TNLData.from_treenode(self.object) + context['tnldata'] = TNLData.from_treenode(self.object,self.request.user) return context class TreeNodeJSONView(generic.DetailView): @@ -202,7 +246,7 @@ class TreeNodeJSONView(generic.DetailView): def get(self,request,*args, **kwargs): self.object = self.get_object() - data = TNLData.from_treenode(self.object).to_json() + data = TNLData.from_treenode(self.object,self.request.user).to_json() return JsonResponse(data) @@ -331,6 +375,7 @@ class ProblemView(generic.DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + user = self.request.user # Teď potřebujeme doplnit tnldata do kontextu. # Ošklivý type switch, hezčí by bylo udělat to polymorfni. FIXME. if False: @@ -338,11 +383,11 @@ class ProblemView(generic.DetailView): pass elif isinstance(self.object, s.Clanek) or isinstance(self.object, s.Konfera): # Tyhle Problémy mají ŘešeníNode - context['tnldata'] = TNLData.from_treenode(self.object.reseninode) + context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user) elif isinstance(self.object, s.Uloha): # FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever - tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode) - tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode) + tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user) + tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user) context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) elif isinstance(self.object, s.Tema): rocniknode = self.object.rocnik.rocniknode @@ -384,16 +429,16 @@ class AktualniZadaniView(generic.TemplateView): # ) # def ZadaniTemataView(request): - nastaveni = get_object_or_404(Nastaveni) - verejne = nastaveni.aktualni_cislo.verejne() - akt_rocnik = nastaveni.aktualni_cislo.rocnik - temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') - return render(request, 'seminar/tematka/rozcestnik.html', - { - 'tematka': temata, - 'verejne': verejne, - }, - ) + nastaveni = get_object_or_404(Nastaveni) + verejne = nastaveni.aktualni_cislo.verejne() + akt_rocnik = nastaveni.aktualni_cislo.rocnik + temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') + return render(request, 'seminar/tematka/rozcestnik.html', + { + 'tematka': temata, + 'verejne': verejne, + }, + ) # nastaveni = get_object_or_404(Nastaveni) diff --git a/seminar/viewsets.py b/seminar/viewsets.py index 8c83c067..163d2d5a 100644 --- a/seminar/viewsets.py +++ b/seminar/viewsets.py @@ -1,7 +1,23 @@ from rest_framework import viewsets,filters +from rest_framework.permissions import BasePermission, AllowAny from . import models as m from . import views +from seminar.permissions import AllowWrite + +class PermissionMixin(object): + """ Redefines get_permissions so that only organizers can make changes. """ + + def get_permissions(self): + permission_classes = [] + print("get_permissions have been called.") + if self.action in ["create", "update", "partial_update", "destroy"]: + permission_classes = [AllowWrite] # speciální permission na zápis - orgové + else: + permission_classes = [AllowAny] + # návštěvník nemusí být zalogován, aby si prohlížel obsah + return [permission() for permission in permission_classes] + class ReadWriteSerializerMixin(object): """ Overrides get_serializer_class to choose the read serializer @@ -46,27 +62,27 @@ class ReadWriteSerializerMixin(object): ) return self.create_serializer_class -class UlohaVzorakNodeViewSet(viewsets.ModelViewSet): +class UlohaVzorakNodeViewSet(PermissionMixin, viewsets.ModelViewSet): queryset = m.UlohaVzorakNode.objects.all() serializer_class = views.UlohaVzorakNodeSerializer -class TextViewSet(viewsets.ModelViewSet): +class TextViewSet(PermissionMixin, viewsets.ModelViewSet): queryset = m.Text.objects.all() serializer_class = views.TextSerializer -class TextNodeViewSet(ReadWriteSerializerMixin,viewsets.ModelViewSet): +class TextNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet): queryset = m.TextNode.objects.all() read_serializer_class = views.TextNodeSerializer write_serializer_class = views.TextNodeWriteSerializer create_serializer_class = views.TextNodeCreateSerializer -class CastNodeViewSet(ReadWriteSerializerMixin,viewsets.ModelViewSet): +class CastNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet): queryset = m.CastNode.objects.all() read_serializer_class = views.CastNodeSerializer write_serializer_class = views.CastNodeSerializer create_serializer_class = views.CastNodeCreateSerializer -class UlohaVzorakNodeViewSet(viewsets.ModelViewSet): +class UlohaVzorakNodeViewSet(PermissionMixin, viewsets.ModelViewSet): serializer_class = views.UlohaVzorakNodeSerializer def get_queryset(self): @@ -74,4 +90,7 @@ class UlohaVzorakNodeViewSet(viewsets.ModelViewSet): nazev = self.request.query_params.get('nazev',None) if nazev is not None: queryset = queryset.filter(nazev__contains=nazev) - return queryset + if self.request.user.has_perm('auth.org'): + return queryset + else: # pro neorgy jen zveřejněné vzoráky + return queryset.filter(uloha__cislo_reseni__verejne_db=True)