Browse Source

Merge branch 'develop' into test

export_seznamu_prednasek
Jonas Havelka 3 years ago
parent
commit
74a2fdceec
  1. 8
      api/views/autocomplete.py
  2. 24
      data/sitetree.json
  3. 23
      mamweb/admin.py
  4. 5
      seminar/models.py
  5. 4
      seminar/templates/seminar/archiv/cisla.html
  6. 51
      seminar/templates/seminar/archiv/rocnik.html
  7. 2
      seminar/templates/seminar/archiv/rocnik_vysledkovka.tex
  8. 9
      seminar/templates/seminar/odevzdavatko/detail.html
  9. 50
      seminar/templates/seminar/odevzdavatko/detail_resitele.html
  10. 2
      seminar/templates/seminar/odevzdavatko/resitel_prehled.html
  11. 2
      seminar/urls.py
  12. 39
      seminar/views/odevzdavatko.py
  13. 2
      seminar/views/views_all.py

8
api/views/autocomplete.py

@ -14,6 +14,8 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView):
words = self.q.split(' ') #TODO re split podle bileho znaku words = self.q.split(' ') #TODO re split podle bileho znaku
partq = Q() partq = Q()
for w in words: # Hledej po slovech, zahoď čárky a tečky z konců. for w in words: # Hledej po slovech, zahoď čárky a tečky z konců.
if len(w) == 0:
continue
if w[-1] in (".",","): if w[-1] in (".",","):
w = w[:-1] w = w[:-1]
@ -26,11 +28,15 @@ class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetVie
def get_queryset(self): def get_queryset(self):
qs = m.Resitel.objects.all() qs = m.Resitel.objects.all()
if self.q: if self.q:
qs = qs.filter( parts = self.q.split()
query = Q()
for part in parts:
query &= (
Q(osoba__jmeno__istartswith=self.q)| Q(osoba__jmeno__istartswith=self.q)|
Q(osoba__prijmeni__istartswith=self.q)| Q(osoba__prijmeni__istartswith=self.q)|
Q(osoba__prezdivka__istartswith=self.q) Q(osoba__prezdivka__istartswith=self.q)
) )
qs = qs.filter(query)
return qs return qs
class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView):

24
data/sitetree.json

@ -977,5 +977,29 @@
}, },
"model": "sitetree.treeitem", "model": "sitetree.treeitem",
"pk": 50 "pk": 50
},
{
"fields": {
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [],
"access_restricted": true,
"alias": null,
"description": "",
"hidden": false,
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 42,
"sort_order": 51,
"title": "Detail řešení {{ reseni.id }}",
"tree": 1,
"url": "odevzdavatko_resitel_reseni reseni.id",
"urlaspattern": true
},
"model": "sitetree.treeitem",
"pk": 51
} }
] ]

23
mamweb/admin.py

@ -1,4 +1,6 @@
import locale
from django.contrib import admin from django.contrib import admin
from django.contrib.admin import AdminSite
from django.contrib.flatpages.models import FlatPage from django.contrib.flatpages.models import FlatPage
# Note: we are renaming the original Admin and Form as we import them! # Note: we are renaming the original Admin and Form as we import them!
@ -24,3 +26,24 @@ class FlatPageAdmin(FlatPageAdminOld):
admin.site.unregister(FlatPage) admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdmin) admin.site.register(FlatPage, FlatPageAdmin)
locale.setlocale(locale.LC_COLLATE, 'cs_CZ.UTF-8')
# https://books.agiliq.com/projects/django-admin-cookbook/en/latest/set_ordering.html
# FIXME zpraseno pomocí toho, že Python umí bez problému přepisovat funkce
def get_app_list(self, request):
"""
Return a sorted list of all the installed apps that have been
registered in this site.
"""
app_dict = self._build_app_dict(request)
# Sort the apps alphabetically.
app_list = sorted(app_dict.values(), key=lambda x: locale.strxfrm('!') if (x['name'] == "Seminar") else locale.strxfrm(x['name'].lower()))
# Sort the models alphabetically within each app.
for app in app_list:
app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower()))
return app_list
AdminSite.get_app_list = get_app_list

5
seminar/models.py

@ -466,6 +466,11 @@ class Rocnik(SeminarModelBase):
vc.sort(key=lambda c: c.poradi) vc.sort(key=lambda c: c.poradi)
return vc return vc
def neverejna_cisla(self):
vc = [c for c in self.cisla.all() if not c.verejne()]
vc.sort(key=lambda c: c.poradi)
return vc
def posledni_verejne_cislo(self): def posledni_verejne_cislo(self):
vc = self.verejna_cisla() vc = self.verejna_cisla()
return vc[-1] if vc else None return vc[-1] if vc else None

4
seminar/templates/seminar/archiv/cisla.html

@ -35,9 +35,9 @@
Jednotlivá čísla: Jednotlivá čísla:
<ul> <ul>
{% for cislo in rocnik.cisla.all reversed %} {% for cislo in rocnik.cisla.all reversed %}
{% if cislo.verejne or user.je_org %}
<li><a href='{{ cislo.verejne_url }}'>{{ cislo.poradi }}. číslo</a> {% if cislo.pdf %}(<a href='{{ cislo.pdf.url }}'>pdf</a>) {% endif %} <li><a href='{{ cislo.verejne_url }}'>{{ cislo.poradi }}. číslo</a> {% if cislo.pdf %}(<a href='{{ cislo.pdf.url }}'>pdf</a>) {% endif %}
{% empty %} {% endif %}
Žádná čísla k zobrazení
{% endfor %} {% endfor %}
</ul> </ul>
<a href='{{ rocnik.verejne_url }}'>Výsledková listina</a> <!-- FIXME: url výsledkovky--> <a href='{{ rocnik.verejne_url }}'>Výsledková listina</a> <!-- FIXME: url výsledkovky-->

51
seminar/templates/seminar/archiv/rocnik.html

@ -63,20 +63,63 @@
{% endfor %} {% endfor %}
</div> </div>
{% if user.je_org and rocnik.neverejna_cisla %}
<div class="mam-org-only">
<div class="cisla-v-rocniku">
{% for c in rocnik.neverejna_cisla %}
<div class="cislo_pole">
{% if vysledkovka %} <h6> Číslo {{ c.kod }}</h6>
{% if user.je_org %}
<div class='mam-org-only'> <div class="flip-card" id="archiv-rocnik">
<a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX)</a>
<div class="flip-card-inner">
<div class="flip-card-front">
<div class="flip-card-foto">
{% if c.titulka_nahled %}
<img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px>
{% else %}
{% load static %} <img src="{% static 'images/no-picture.png' %}" height=180px alt="no-picture">
{% endif %}
</div>
</div>
<div class="flip-card-back">
<div class="cislo_odkazy">
<ul>
<li>
<a href="{{ c.verejne_url }}">archiv čísla</a>
</li>
{% if c.pdf %}
<li>
<a href='{{ c.pdf.url }}'>pdf</a>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div> </div>
{% endif %} {% endif %}
{% if vysledkovka %}
<h2>Výsledková listina</h2> <h2>Výsledková listina</h2>
{% include "seminar/vysledkovka_rocnik.html" %} {% include "seminar/vysledkovka_rocnik.html" %}
{% endif %} {% endif %}
{% if user.je_org %} {% if user.je_org %}
<div class='mam-org-only'> <div class='mam-org-only'>
<a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX, včetně neveřejných)</a>
<h2>Výsledková listina včetně neveřejných bodů</h2> <h2>Výsledková listina včetně neveřejných bodů</h2>
{% with radky_vysledkovky_s_neverejnymi as radky_vysledkovky %} {% with radky_vysledkovky_s_neverejnymi as radky_vysledkovky %}
{% with cisla_s_neverejnymi as cisla %} {% with cisla_s_neverejnymi as cisla %}

2
seminar/templates/seminar/archiv/rocnik_vysledkovka.tex

@ -1,5 +1,6 @@
{% with lb="{" %} {% with lb="{" %}
{% with rb="}" %} {% with rb="}" %}
{% with radky_vysledkovky=radky_vysledkovky_s_neverejnymi cisla=cisla_s_neverejnymi %}
\setlength{\tabcolsep}{3pt} \setlength{\tabcolsep}{3pt}
\begin{longtable}{|r|l|c|r|{% for cislo in cisla %}c{% if not forloop.last %}@{\hskip.5em}{% endif %}{% endfor %}|r|}\hline \begin{longtable}{|r|l|c|r|{% for cislo in cisla %}c{% if not forloop.last %}@{\hskip.5em}{% endif %}{% endfor %}|r|}\hline
& & & & \multicolumn{{ lb }}{{ cisla|length }}}{c|}{\textbf{Číslo}} & \\\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} & {% for cislo in cisla %}\textbf{{ lb }}{{ cislo.poradi }}{{ rb }} & {% endfor %}\raisebox{0.7mm}{$\sum_1$} \\\hline & & & & \multicolumn{{ lb }}{{ cisla|length }}}{c|}{\textbf{Číslo}} & \\\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} & {% for cislo in cisla %}\textbf{{ lb }}{{ cislo.poradi }}{{ rb }} & {% endfor %}\raisebox{0.7mm}{$\sum_1$} \\\hline
@ -10,3 +11,4 @@
{% endfor %}\end{longtable} {% endfor %}\end{longtable}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
{% endwith %}

9
seminar/templates/seminar/odevzdavatko/detail.html

@ -134,6 +134,15 @@ $(document).ready(function(){
return false; return false;
} }
} }
function problem_is_empty(elem, index, array) {return elem.firstElementChild.children.length !== 1 && elem.firstElementChild.children[1].textContent === "";}
if ($('.hodnoceni').toArray().some(problem_is_empty)) {
alert("Neuloženo! Nezadal jsi problém, ke kterému posíláš hodnocení. Pokud je toto hodnocení navíc, smaž ho prosím křížkem a znovu odešli.")
event.preventDefault()
return false;
}
return true; return true;
} }
</script> </script>

50
seminar/templates/seminar/odevzdavatko/detail_resitele.html

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% load static %}
{% load deadliny %}
{% block content %}
<p>Řešené problémy: {{ object.problem.all | join:", " }}</p>
<p>Řešitelé: {% for r in object.resitele.all %} {{ r }}
{% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}</p>
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
<p>Forma: {{ object.get_forma_display }}</p>
<p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.cas_doruceni | deadline_html }}</p>
{# Soubory: #}
<h3>Přílohy:</h3>
{% if object.prilohy.all %}
<table class="dosla_reseni">
<tr><th>Soubor</th><th>Řešitelova poznámka</th><th>Datum</th></tr>
{% for priloha in object.prilohy.all %}
<tr>
<td><a href="{{ priloha.soubor.url }}" download>{{ priloha.split | last }}</a></td>
<td>{{ priloha.res_poznamka }}</td>
<td>{{ priloha.vytvoreno }}</td></tr>
{# TODO: Orgo-poznámka, ideálně jako formulář #}
{% endfor %}
</table>
{% else %}
<p>Žádné přílohy</p>
{% endif %}
{#<h3>Poznámka:</h3>#}
{#<p>{{ poznamka }}</p>#}
{# Hodnocení: #}
<h3>Hodnocení:</h3>
<table id="form_set" class="dosla_reseni">
<tr><th>Problém</th><th>Body</th>{# <th>Číslo pro body</th> #}</tr>
{% for h in hodnoceni %}
<tr class="hodnoceni">
<td>{{ h.problem }}</td>
<td>{{ h.body }}</td>
{# <td>{{ h.cislo_body }}</td>#}
</tr>
{% endfor %}
</table>
{% endblock %}

2
seminar/templates/seminar/odevzdavatko/resitel_prehled.html

@ -25,7 +25,7 @@
<tr> <tr>
<td>{{ hodn.reseni.cas_doruceni | date:"d.m.Y H:i"}}</td> <td>{{ hodn.reseni.cas_doruceni | date:"d.m.Y H:i"}}</td>
<td id="problem"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu }}</span></td> <td id="problem"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu }}</span></td>
<td>{{ hodn.body|default_if_none:"---" }}</td> <td><a href="{% url 'odevzdavatko_resitel_reseni' hodn.reseni.id %}">{{ hodn.body|default_if_none:"---" }}</a></td>
<td>{{ hodn.reseni.cas_doruceni | deadline_html }}</td> <td>{{ hodn.reseni.cas_doruceni | deadline_html }}</td>
</tr> </tr>
{% endfor %} {% endfor %}

2
seminar/urls.py

@ -142,4 +142,6 @@ urlpatterns = [
path('org/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), path('org/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'),
path('org/reseni/all', org_required(views.SeznamReseniView.as_view())), path('org/reseni/all', org_required(views.SeznamReseniView.as_view())),
path('org/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), path('org/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())),
path('resitel/reseni/<int:pk>', resitel_or_org_required(views.ResitelReseniView.as_view()), name='odevzdavatko_resitel_reseni'),
] ]

39
seminar/views/odevzdavatko.py

@ -1,3 +1,4 @@
from django.core.exceptions import PermissionDenied
from django.views.generic import ListView, DetailView, FormView from django.views.generic import ListView, DetailView, FormView
from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin
from django.views.generic.base import View from django.views.generic.base import View
@ -75,7 +76,6 @@ class TabulkaOdevzdanychReseniView(ListView):
# Chceme jen letošní problémy # Chceme jen letošní problémy
# FIXME: Neexistuje metoda, jak dostat starší problémy…
self.problemy = self.problemy.filter(Q(Tema___rocnik=self.aktualni_rocnik) | Q(Uloha___cislo_zadani__rocnik = self.aktualni_rocnik) | Q(Clanek___cislo__rocnik = self.aktualni_rocnik) | Q(Konfera___soustredeni__rocnik = self.aktualni_rocnik)) self.problemy = self.problemy.filter(Q(Tema___rocnik=self.aktualni_rocnik) | Q(Uloha___cislo_zadani__rocnik = self.aktualni_rocnik) | Q(Clanek___cislo__rocnik = self.aktualni_rocnik) | Q(Konfera___soustredeni__rocnik = self.aktualni_rocnik))
self.chteni_resitele = resitele # Zapamatování pro get_context_data self.chteni_resitele = resitele # Zapamatování pro get_context_data
@ -88,9 +88,14 @@ class TabulkaOdevzdanychReseniView(ListView):
if problemy == FiltrForm.PROBLEMY_MOJE: if problemy == FiltrForm.PROBLEMY_MOJE:
org = m.Organizator.objects.get(osoba__user=self.request.user) org = m.Organizator.objects.get(osoba__user=self.request.user)
self.problemy = self.problemy.filter(Q(autor=org)|Q(garant=org)|Q(opravovatele=org), stav=m.Problem.STAV_ZADANY) self.problemy = self.problemy.filter(
Q(autor=org)|Q(garant=org)|Q(opravovatele=org),
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
)
elif problemy == FiltrForm.PROBLEMY_LETOSNI: elif problemy == FiltrForm.PROBLEMY_LETOSNI:
self.problemy = self.problemy.filter(stav=m.Problem.STAV_ZADANY) self.problemy = self.problemy.filter(
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
)
#self.problemy = list(filter(lambda problem: problem.rocnik() == self.aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník.... #self.problemy = list(filter(lambda problem: problem.rocnik() == self.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. # 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.problemy = self.problemy.non_polymorphic()
@ -264,6 +269,34 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
return redirect(success_url) return redirect(success_url)
class ResitelReseniView(DetailView):
model = m.Reseni
template_name = 'seminar/odevzdavatko/detail_resitele.html'
def aktualni_hodnoceni(self):
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
result = []
for hodn in m.Hodnoceni.objects.filter(reseni=self.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)
hodnoceni = self.aktualni_hodnoceni()
if not self.reseni.resitele.filter(osoba__user=self.request.user).exists():
raise PermissionDenied()
# ctx['poznamka'] = f.PoznamkaReseniForm(instance=self.reseni)
ctx["hodnoceni"] = hodnoceni
return ctx
class PrehledOdevzdanychReseni(ListView): class PrehledOdevzdanychReseni(ListView):
model = m.Hodnoceni model = m.Hodnoceni
template_name = 'seminar/odevzdavatko/resitel_prehled.html' template_name = 'seminar/odevzdavatko/resitel_prehled.html'

2
seminar/views/views_all.py

@ -630,6 +630,8 @@ class ArchivView(generic.ListView):
context = super(ArchivView, self).get_context_data(**kwargs) context = super(ArchivView, self).get_context_data(**kwargs)
cisla = Cislo.objects.filter(poradi=1) cisla = Cislo.objects.filter(poradi=1)
if not self.request.user.je_org:
cisla = cisla.filter(verejne_db=True)
urls ={} urls ={}
for i, c in enumerate(cisla): for i, c in enumerate(cisla):

Loading…
Cancel
Save