Compare commits

..

No commits in common. "f96c24a4746bc6632af5481992e1a9d4a911d61d" and "09e2df75ce20de22894fd5b3c3dc9af541400b39" have entirely different histories.

14 changed files with 25 additions and 115 deletions

File diff suppressed because one or more lines are too long

View file

@ -2,4 +2,3 @@ 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

View file

@ -85,17 +85,3 @@ 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)

View file

@ -6,31 +6,15 @@
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

View file

@ -8,14 +8,12 @@ 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

View file

@ -1,31 +0,0 @@
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.

View file

@ -2,7 +2,4 @@ Zápisy
======
.. toctree::
:caption: Importy zápisů z Markdownu
:maxdepth: 1
2021-12-06-testovani_dokumentace_codereview
2021-12-06-testovani_dokumentace_codereview

View file

@ -146,7 +146,7 @@
</button>
{% endif %}
{% if o.status != 'neni_chyba' %}
<button type='submit' name='action' value='wontfix' title='Označ, že se nebude měnit'>
<button type='submit' name='action' value='wontfix' title='Označ jako irelevantní '>
<img src="{% static "korektury/imgs/cross.png" %}"/>
</button>
{% endif %}

View file

@ -6,7 +6,6 @@ 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`
@ -71,26 +70,16 @@ 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 "$UPSTREAM_BRANCH":make/lib.sh)"
if test "$(git rev-parse @:make/lib.sh)" != "$(git rev-parse "$BRANCH@{u}":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 "$UPSTREAM_BRANCH":"$SCRIPT")"
if test "$(git rev-parse @:"$SCRIPT")" != "$(git rev-parse "$BRANCH@{u}":"$SCRIPT")"
then
echo >&2 "Změna v $SCRIPT, prosím pullni manuálně"
exit 1

View file

@ -1222,13 +1222,6 @@ 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;

View file

@ -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 = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik'])
self.aktualni_rocnik = m.Rocnik.objects.get(rocnik=self.kwargs['rocnik'])
form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik)
if form.is_valid():
@ -102,21 +102,16 @@ 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().distinct()
self.problemy = self.problemy.non_polymorphic()
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):

View file

@ -43,7 +43,7 @@ class OrganizatorAdmin(ReverseModelAdmin):
@admin.register(m.Resitel)
class ResitelAdmin(ReverseModelAdmin):
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
ordering = ('osoba__prijmeni', 'osoba__jmeno')
ordering = ('osoba__jmeno','osoba__prijmeni')
inline_type = 'stacked'
inline_reverse = ['osoba']

View file

@ -349,7 +349,7 @@ def resiteleRocnikuCsvExportView(request, rocnik):
assert request.method in ('GET', 'HEAD')
return dataResiteluCsvResponse(
utils.resi_v_rocniku(
get_object_or_404(m.Rocnik, rocnik=rocnik)
m.Rocnik.objects.get(rocnik=rocnik)
)
)
@ -442,17 +442,17 @@ class OdmenyView(generic.TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
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'))
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'))
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 resitel in resitele:
fbody = frombody.get(resitel.id, 0)
tbody = tobody.get(resitel.id, 0)
for (aid, tbody) in tobody.items():
fbody = frombody.get(aid,0)
resitel = Resitel.objects.get(pk=aid)
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 = get_object_or_404(Cislo, poradi=cislo, rocnik__rocnik=rocnik)
realne_cislo = Cislo.objects.get(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 = get_object_or_404(Rocnik, rocnik = rocnik)
rocnik_obj = Rocnik.objects.get(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 = get_object_or_404(Cislo, rocnik=rocnik_obj, poradi=cislo)
cislo_obj = Cislo.objects.get(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)

View file

@ -1,6 +1,6 @@
import abc
from functools import cached_property
from typing import Union, Iterable # TODO: s pythonem 3.10 přepsat na '|'
from typing import Union # 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: Iterable[m.Resitel] = None,
resitele=None,
null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body
) -> dict[int, int]:
filtr = Q()