Compare commits
24 commits
09e2df75ce
...
f96c24a474
Author | SHA1 | Date | |
---|---|---|---|
f96c24a474 | |||
c76fcb363a | |||
246f63d6e0 | |||
0e0bd76f28 | |||
72b72899fd | |||
![]() |
5977f472c7 | ||
![]() |
3c3047b548 | ||
ea7075f707 | |||
e10a8e0b6d | |||
3110eb92a5 | |||
abdd2d65dd | |||
d14b6bb799 | |||
bebc120e8f | |||
58f05724e1 | |||
5690dc297e | |||
![]() |
485c4180da | ||
![]() |
c2ad4c560d | ||
![]() |
4d97e21e96 | ||
![]() |
4378c05e3e | ||
![]() |
332e5e88d5 | ||
9a3f51ca6b | |||
315dc97635 | |||
fd3df6c344 | |||
7ccaf87dfc |
14 changed files with 115 additions and 25 deletions
File diff suppressed because one or more lines are too long
|
@ -2,3 +2,4 @@ Aprílové nápad
|
|||
==============
|
||||
|
||||
* aprílový easter-egg pro řešitele - vytvořit nějakou vtipnou testovací databázi a nasadit ji místo produkce
|
||||
* změnit veškerý text na oranžovo
|
||||
|
|
14
docs/conf.py
14
docs/conf.py
|
@ -85,3 +85,17 @@ source_suffix = {
|
|||
'.rst': 'restructuredtext',
|
||||
'.md': 'markdown',
|
||||
}
|
||||
|
||||
# Autodoc má ignorovat některé moduly
|
||||
# Ref: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#event-autodoc-skip-member
|
||||
# Kudos: https://stackoverflow.com/a/21449475/
|
||||
def ignorovat(app, what, name, obj, skip, options):
|
||||
blacklist = (
|
||||
# typ (what), name
|
||||
('module', 'settings.mamweb_prod'),
|
||||
)
|
||||
ignore = (what, name) in blacklist
|
||||
return True if ignore else None
|
||||
|
||||
def setup(app):
|
||||
app.connect('autodoc-skip-member', ignorovat)
|
||||
|
|
|
@ -6,15 +6,31 @@
|
|||
Vítejte v dokumentaci M&Mího webu!
|
||||
===================================
|
||||
|
||||
Tzv. produkce (tedy to, co vidí uživatelé) běží na `<mam.mff.cuni.cz>`_ (resp.
|
||||
`<mam.matfyz.cz>`_), menu, obrázky v pozadí menu a spousta stránek (ty pouze se
|
||||
statickým textem/obrázky) se mění přímo na produkci. Testovací verze běží na
|
||||
`<https://mam-test.ks.matfyz.cz/>`_.
|
||||
|
||||
Abychom uměli web vyvíjet, musíme ho většinou nejdřív umět
|
||||
:doc:`naklonovat a spustit lokálně <vyvoj>`.
|
||||
|
||||
:doc:`struktura mamwebu <struktura>` se řídí hlavně djangem, ale snažíme se
|
||||
také o oddělení jednotlivých částí do :doc:`samostatných aplikací
|
||||
<modules/modules>`.
|
||||
|
||||
Dokumentace (jak v ``docs/``, tak přímo v kódu) je psaná ve
|
||||
:doc:`sphinxu <sphinx>`.
|
||||
|
||||
.. toctree::
|
||||
:caption: M&M web
|
||||
:maxdepth: 2
|
||||
:titlesonly:
|
||||
|
||||
vyvoj
|
||||
sphinx
|
||||
dalsi_soubory
|
||||
skripty
|
||||
modules/modules
|
||||
dalsi_soubory
|
||||
zapisy/zapisy
|
||||
|
||||
|
||||
|
|
|
@ -8,12 +8,14 @@ Jinak všechny rst, co jsou ve složce ``doc`` a jejích podsložkách nezačín
|
|||
|
||||
Sphinx se píše v rst: `Návod na syntaxi rst`_ `Cheat sheet`_
|
||||
|
||||
To je snad vše, co je potřeba vědět k dokumentaci mamwebu. Následující sekce jsou o tom, co jsem provedl Sphinxu, aby to fungovalo:
|
||||
|
||||
.. _Návod na syntaxi rst: https://sphinx-tutorial.readthedocs.io/step-1/#sections
|
||||
.. _Cheat sheet: https://sphinx-tutorial.readthedocs.io/cheatsheet/
|
||||
|
||||
make html
|
||||
---------
|
||||
Make html dělá následující: Vygenerují se rst soubory do modules z pythoní dokumentace pomocí::
|
||||
``make html`` dělá následující: Vygenerují se rst soubory do modules z pythoní dokumentace pomocí::
|
||||
|
||||
sphinx-apidoc --module-first -o modules .. ../*/migrations --templatedir _templates -f
|
||||
|
||||
|
|
31
docs/struktura.rst
Normal file
31
docs/struktura.rst
Normal file
|
@ -0,0 +1,31 @@
|
|||
Co kde najít (mamweb + django)
|
||||
==============================
|
||||
|
||||
Nejdůležitější aplikace z pohledu djanga je ``mamweb``. Tu totiž django pouští
|
||||
a obsahuje tedy nastavení (tam se přidávají ostatní aplikace, včetně těch
|
||||
importovaných z djanga, a nastavují se tam různé věci jak v djangu, tak i naše,
|
||||
například složky, kam se budou věci přidané uživateli ukládat). Dále obsahuje
|
||||
základní urls, udávající, „na jaké adrese co je“. A nakonec obsahuje obecné
|
||||
věci jako chybové hlášky a vzhled M&M stránek (menu, patička, atd.). Aktuálně
|
||||
i veškeré csv.
|
||||
|
||||
Další jsou pak jednotlivé aplikace (pokud něco hledáte, tak zřejmě chcete najít
|
||||
tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url), za
|
||||
zmínku stojí seminar, kde jsou takové ty věci, co zbyly. Plus jsou tam aktuálně
|
||||
téměř všechny modely, protože je těžké je přesunout jinam.
|
||||
|
||||
**TLDR: Nevšímejte si složky data/ a souborů přímo v kořenové složce.**
|
||||
Kromě věcí potřebných ke gitu, :doc:`ke spuštění <vyvoj>` a fukci djanga,
|
||||
dalších drobností, lokální databáze a již zmíněných aplikací jsou tu ``data``,
|
||||
kde je takový ten obsah webu, co by se měl dát snadno měnit (tudíž musí být v
|
||||
databázi), tj. statické stránky, menu a obrázky v pozadí menu. Ten je třeba
|
||||
měnit hlavně na produkci a sekundárně tady (může to dělat i newebař a nechcete
|
||||
přepsat jeho práci). Vše, co nejsou aplikace je popsáno :doc:`tady <dalsi_soubory>`.
|
||||
|
||||
Základy djanga
|
||||
--------------
|
||||
|
||||
|
||||
|
||||
mamweb je psaný téměř čistě v djangu. Což znamená, že to „co je vidět na stránkách“
|
||||
jsou views.
|
|
@ -2,4 +2,7 @@ Zápisy
|
|||
======
|
||||
|
||||
.. toctree::
|
||||
2021-12-06-testovani_dokumentace_codereview
|
||||
:caption: Importy zápisů z Markdownu
|
||||
:maxdepth: 1
|
||||
|
||||
2021-12-06-testovani_dokumentace_codereview
|
|
@ -146,7 +146,7 @@
|
|||
</button>
|
||||
{% endif %}
|
||||
{% if o.status != 'neni_chyba' %}
|
||||
<button type='submit' name='action' value='wontfix' title='Označ jako irelevantní '>
|
||||
<button type='submit' name='action' value='wontfix' title='Označ, že se nebude měnit'>
|
||||
<img src="{% static "korektury/imgs/cross.png" %}"/>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
|
15
make/lib.sh
15
make/lib.sh
|
@ -6,6 +6,7 @@ VENV_PATH="${VENV_PATH:-env}"
|
|||
BRANCH="${BRANCH:-master}"
|
||||
|
||||
REPO="${REPO:-git@gitea.ks.matfyz.cz:mam/mamweb.git}"
|
||||
UPSTREAM_REMOTE='origin'
|
||||
GIMLI='gimli.ms.mff.cuni.cz'
|
||||
GIMLI_LOGIN="mam-web@$GIMLI"
|
||||
# Skutečné cesty, jak je vrátí `realpath`
|
||||
|
@ -70,16 +71,26 @@ function safe_checkout_branch {
|
|||
local SCRIPT="$0"
|
||||
|
||||
git fetch --all
|
||||
local UPSTREAM_BRANCH
|
||||
if git rev-parse "$BRANCH@{u}" >/dev/null 2>/dev/null
|
||||
then
|
||||
UPSTREAM_BRANCH="$BRANCH@{u}" # Stačí symbolicky.
|
||||
else
|
||||
# Tohle je jediná možná záchrana.
|
||||
UPSTREAM_BRANCH="$UPSTREAM_REMOTE/$BRANCH"
|
||||
fi
|
||||
git rev-parse "$UPSTREAM_BRANCH" || die "Vzdálená větev $UPSTREAM_BRANCH neexistuje?"
|
||||
|
||||
# Od teď si musíme dát pozor, abychom nezměnili kód, který právě běží.
|
||||
# Zkontrolujeme, že se nemění tahle knihovna a skript, který běží.
|
||||
# `git rev-parse` dává SHA-1 hashe objektů, vizte manuálovou stránku pro pochopení.
|
||||
# Pozor: tohle porovnává jen verze commitnuté do gitu. Lokální změny udělají něco náhodného…
|
||||
if test "$(git rev-parse @:make/lib.sh)" != "$(git rev-parse "$BRANCH@{u}":make/lib.sh)"
|
||||
if test "$(git rev-parse @:make/lib.sh)" != "$(git rev-parse "$UPSTREAM_BRANCH":make/lib.sh)"
|
||||
then
|
||||
echo >&2 "Změna v make/lib.sh, prosím pullni manuálně"
|
||||
exit 1
|
||||
fi
|
||||
if test "$(git rev-parse @:"$SCRIPT")" != "$(git rev-parse "$BRANCH@{u}":"$SCRIPT")"
|
||||
if test "$(git rev-parse @:"$SCRIPT")" != "$(git rev-parse "$UPSTREAM_BRANCH":"$SCRIPT")"
|
||||
then
|
||||
echo >&2 "Změna v $SCRIPT, prosím pullni manuálně"
|
||||
exit 1
|
||||
|
|
|
@ -1222,6 +1222,13 @@ div.gdpr {
|
|||
width: 100vw;
|
||||
margin-left: calc(-50vw + 485px);
|
||||
}
|
||||
/* Na úzkém displeji nechceme nic dělat. */
|
||||
@media(max-width: 860px) {
|
||||
.full_width{
|
||||
margin-left: 0;
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.dosla_reseni tr th, .dosla_reseni tr td {
|
||||
padding: 1px 10px 1px 10px;
|
||||
|
|
|
@ -60,7 +60,7 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
|
||||
self.aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
|
||||
if 'rocnik' in self.kwargs:
|
||||
self.aktualni_rocnik = m.Rocnik.objects.get(rocnik=self.kwargs['rocnik'])
|
||||
self.aktualni_rocnik = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik'])
|
||||
|
||||
form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik)
|
||||
if form.is_valid():
|
||||
|
@ -102,16 +102,21 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
)
|
||||
#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.
|
||||
self.problemy = self.problemy.non_polymorphic()
|
||||
self.problemy = self.problemy.non_polymorphic().distinct()
|
||||
|
||||
self.reseni = self.reseni.filter(cas_doruceni__date__gt=reseni_od, cas_doruceni__date__lte=reseni_do)
|
||||
if jen_neobodovane:
|
||||
self.reseni = self.reseni.filter(hodnoceni__body__isnull=True)
|
||||
self.jen_neobodovane = jen_neobodovane
|
||||
|
||||
def get_queryset(self):
|
||||
self.inicializuj_osy_tabulky()
|
||||
qs = super().get_queryset()
|
||||
if self.jen_neobodovane:
|
||||
qs = qs.filter(body__isnull=True)
|
||||
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')
|
||||
# FIXME tohle je ošklivé, na špatném místě a pomalé. Ale moc mě štvalo, že musím hledat správná místa v tabulce.
|
||||
self.problemy = self.problemy.filter(id__in=qs.values("problem__id"))
|
||||
return qs
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
|
|
|
@ -43,7 +43,7 @@ class OrganizatorAdmin(ReverseModelAdmin):
|
|||
@admin.register(m.Resitel)
|
||||
class ResitelAdmin(ReverseModelAdmin):
|
||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
||||
ordering = ('osoba__jmeno','osoba__prijmeni')
|
||||
ordering = ('osoba__prijmeni', 'osoba__jmeno')
|
||||
inline_type = 'stacked'
|
||||
inline_reverse = ['osoba']
|
||||
|
||||
|
|
|
@ -349,7 +349,7 @@ def resiteleRocnikuCsvExportView(request, rocnik):
|
|||
assert request.method in ('GET', 'HEAD')
|
||||
return dataResiteluCsvResponse(
|
||||
utils.resi_v_rocniku(
|
||||
m.Rocnik.objects.get(rocnik=rocnik)
|
||||
get_object_or_404(m.Rocnik, rocnik=rocnik)
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -442,17 +442,17 @@ class OdmenyView(generic.TemplateView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
fromcislo = Cislo.objects.get(rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo'))
|
||||
tocislo = Cislo.objects.get(rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo'))
|
||||
fromcislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo'))
|
||||
tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo'))
|
||||
resitele = aktivniResitele(tocislo)
|
||||
|
||||
def get_diff(from_deadline: Deadline, to_deadline: Deadline):
|
||||
frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline)
|
||||
tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline)
|
||||
outlist = []
|
||||
for (aid, tbody) in tobody.items():
|
||||
fbody = frombody.get(aid,0)
|
||||
resitel = Resitel.objects.get(pk=aid)
|
||||
for resitel in resitele:
|
||||
fbody = frombody.get(resitel.id, 0)
|
||||
tbody = tobody.get(resitel.id, 0)
|
||||
ftitul = resitel.get_titul(fbody)
|
||||
ttitul = resitel.get_titul(tbody)
|
||||
if ftitul != ttitul:
|
||||
|
@ -554,7 +554,7 @@ class RocnikVysledkovkaView(RocnikView):
|
|||
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
|
||||
|
||||
def cisloObalkyView(request, rocnik, cislo):
|
||||
realne_cislo = Cislo.objects.get(poradi=cislo, rocnik__rocnik=rocnik)
|
||||
realne_cislo = get_object_or_404(Cislo, poradi=cislo, rocnik__rocnik=rocnik)
|
||||
return obalkyView(request, aktivniResitele(realne_cislo))
|
||||
|
||||
|
||||
|
@ -580,14 +580,14 @@ def TitulyViewRocnik(request, rocnik):
|
|||
|
||||
def TitulyView(request, rocnik, cislo):
|
||||
""" View pro stažení makra titulů v TeXu."""
|
||||
rocnik_obj = Rocnik.objects.get(rocnik = rocnik)
|
||||
rocnik_obj = get_object_or_404(Rocnik, rocnik = rocnik)
|
||||
resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok)
|
||||
|
||||
asciijmena = []
|
||||
jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka),
|
||||
# pokud ano, vrátí se jako true
|
||||
if cislo is not None:
|
||||
cislo_obj = Cislo.objects.get(rocnik=rocnik_obj, poradi=cislo)
|
||||
cislo_obj = get_object_or_404(Cislo, rocnik=rocnik_obj, poradi=cislo)
|
||||
slovnik_s_body = body_resitelu(do=cislo_obj.zlomovy_deadline_pro_papirove_cislo(), jen_verejne=False)
|
||||
else:
|
||||
slovnik_s_body = body_resitelu(do=Deadline.objects.filter(cislo__rocnik=rocnik_obj).last(), jen_verejne=False)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import abc
|
||||
from functools import cached_property
|
||||
from typing import Union # TODO: s pythonem 3.10 přepsat na '|'
|
||||
from typing import Union, Iterable # TODO: s pythonem 3.10 přepsat na '|'
|
||||
|
||||
import seminar.models as m
|
||||
from django.db.models import Q, Sum
|
||||
|
@ -22,7 +22,7 @@ def body_resitelu(
|
|||
do: m.Deadline = None,
|
||||
od: m.Deadline = None,
|
||||
jen_verejne: bool = True,
|
||||
resitele=None,
|
||||
resitele: Iterable[m.Resitel] = None,
|
||||
null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body
|
||||
) -> dict[int, int]:
|
||||
filtr = Q()
|
||||
|
|
Loading…
Reference in a new issue