diff --git a/Makefile b/Makefile index 9b998ca4..765871b4 100644 --- a/Makefile +++ b/Makefile @@ -153,3 +153,9 @@ sync_local_db: # Sync database and media. See above lines sync_local: sync_local_media sync_local_db + +# Push local compiled Vue to gimli test site +push_compiled_vue_to_test: + scp vue_frontend/webpack-stats.json mam-web@gimli:/akce/mam/www/mamweb-test/vue_frontend/ + rsync -ave ssh seminar/static/seminar/vue mam-web@gimli:/akce/mam/www/mamweb-test/seminar/static/seminar/ + ssh mam-web@gimli.ms.mff.cuni.cz 'cd /akce/mam/www/mamweb-test/ && . env/bin/activate && ./manage.py collectstatic --noinput' diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 0f1d3812..bec16f7f 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', diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 27cc77a6..6cfdf1cf 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -759,17 +759,26 @@ div.odpocet { /*stránka organizátorů*/ -div.seznam_orgu { +div.seznam_orgu, div.rozcestnik_temat { text-align: center; + padding-bottom: 10px; } -div.org_pole, div.rocnik_pole { +div.org_pole, div.rocnik_pole, div.tema_pole { display: inline-block; width: 30%; min-width: 300px; text-align: center; } +div.tema_pole { + display: inline-block; + width: 40%; + min-width: 350px; + padding-bottom: 20px; + text-align: center; +} + div.cislo_pole { display: inline-block; width: 15%; @@ -812,6 +821,11 @@ div.org_email { height: 205px; } +#tema-rozcestnik.flip-card { + width: 300px; + height: 300px; +} + /* This container is needed to position the front and back side */ .flip-card-inner { position: relative; @@ -835,7 +849,8 @@ div.org_email { backface-visibility: hidden; } -div.flip-card-foto img { + +div.flip-card-foto, div.flip-card-foto img { width: 100%; height: 100%; 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..57a44404 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -743,12 +743,17 @@ 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 + stav_verejny = False + if self.stav == 'zadany' or self.stav == 'vyreseny': + stav_verejny = True + + 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): 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/static/images/tema-bez-obrazku.png b/seminar/static/images/tema-bez-obrazku.png new file mode 100644 index 00000000..8f421df2 Binary files /dev/null and b/seminar/static/images/tema-bez-obrazku.png differ 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/tematka/rozcestnik.html b/seminar/templates/seminar/tematka/rozcestnik.html index 8c3b1a38..0218b425 100644 --- a/seminar/templates/seminar/tematka/rozcestnik.html +++ b/seminar/templates/seminar/tematka/rozcestnik.html @@ -2,24 +2,53 @@ {% block content %} -

Témata jsou texty nejen z oblasti matematiky, fyziky a informatiky, které popisují nějaký problém a jsou doprovázeny návodnými úlohami. Vaším úkolem je zamyslet se nad daným problémem a sepsat vaše úvahy ve formě krátkého textu.

-

Jak řešit téma?

+

+ {% block nadpis1a %}{% block nadpis1b %} + Aktuální témata + {% endblock %}{% endblock %} +

-

 

+

Témata jsou texty nejen z oblasti matematiky, fyziky a informatiky, které popisují nějaký + problém a jsou doprovázeny návodnými úlohami. Vaším úkolem je zamyslet se nad daným + problémem a sepsat vaše úvahy ve formě krátkého textu.

-

Aktuální témata

+

Jak řešit téma?

+ + +
{% for tematko in tematka %} -

{{tematko.nazev}}

-
- {% if tematko.obrazek %} - {{tematko.nazev}} - {% else %} {# pokud témátko nemá fotku, zobrazuje se defaultní obrázek #} - {% load static %} {{tematko.nazev}} - {% endif %} -
-

{{tematko.abstrakt}}

+{# karta témátka - zepředu ilustrační, zezadu abstrakt #} +
+ +

+ Téma {{ tematko.nazev }} +

+ +
+ +
+
+
+ {% if tematko.obrazek %} + {{ tematko.nazev }} + {% else %} {# pokud témátko nemá fotku, zobrazuje se defaultní obrázek #} + {% load static %} {{ tematko.nazev }} + {% endif %} +
+
+
+

{{ tematko.abstrakt }}

+
+
+ +
+
+{# konec karty témátka #} {% endfor %} + +
+ {% endblock %} 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/views/views_all.py b/seminar/views/views_all.py index 316211fd..a550c40a 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -127,6 +127,7 @@ class TNLData(object): def from_treenode(cls,anode,parent=None,index=None): out = cls(anode,parent,index) for (idx,ch) in enumerate(treelib.all_children(anode)): + # FIXME přidat filtrování na veřejnost outitem = cls.from_treenode(ch,out,idx) out.children.append(outitem) out.add_edit_options() diff --git a/seminar/viewsets.py b/seminar/viewsets.py index 8c83c067..cc59f7d7 100644 --- a/seminar/viewsets.py +++ b/seminar/viewsets.py @@ -1,7 +1,48 @@ 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] + + def verejne_nad(self, node): + """ Returns output of verejne for closest Rocnik, Cislo or Problem above. + (All of them have method verejne.)""" + parent = get_parent(node) + while True: + rocnik = isinstance(parent, RocnikNode) + cislo = isinstance(parent, CisloNode) + problem = isinstance(parent, ProblemNode) + + if (rocnik or cislo or problem): + break + else: + parent = get_parent(parent) + if rocnik: + return parent.rocnik.verejne() + elif cislo: + return parent.cislo.verejne() + elif problem: + return parent.problem.verjne() + + def has_object_permission(self, request, view, obj): + # test that obj is Node + assert isinstance(obj, Node) + return verejne_nad(node) + class ReadWriteSerializerMixin(object): """ Overrides get_serializer_class to choose the read serializer @@ -46,27 +87,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):