Compare commits

...

24 commits

Author SHA1 Message Date
f96c24a474 Merge branch 'dokumentace'
# Conflicts:
#	docs/index.rst
2023-01-02 22:33:33 +01:00
c76fcb363a fix: ….objects.get(…) -> get_or_404 2023-01-02 21:52:19 +01:00
246f63d6e0 add: typová anotace 2023-01-02 21:40:07 +01:00
0e0bd76f28 Merge pull request 'odmeny_podle_petra' (!19) from odmeny_podle_petra into master
Reviewed-on: #19
2023-01-02 21:38:46 +01:00
72b72899fd fix: odměny mají házet alespoň 404 na špatné číslo 2023-01-02 21:20:00 +01:00
MaM Web user
5977f472c7 Fix! 2023-01-02 21:18:03 +01:00
Pavel "LEdoian" Turinsky
3c3047b548 Autodoc: Ignorování modulů 2023-01-02 21:14:00 +01:00
ea7075f707 fix: řazení řešitelů v odměnách podle příjmení 2023-01-02 21:09:56 +01:00
e10a8e0b6d fix: řazení řešitelů v adminu podle příjmení 2023-01-02 20:46:05 +01:00
3110eb92a5 Merge pull request 'Vylepšení odevzdávátka' (!13) from vylepseni_odevzdavatka into master
Reviewed-on: #13
2023-01-02 20:25:00 +01:00
abdd2d65dd http -> https 2022-12-19 23:04:28 +01:00
d14b6bb799 sync_prod_flatpages 2022-12-19 23:00:48 +01:00
bebc120e8f Merge pull request 'Korekturovátko: přejmenovat „irelevantní“' (!18) from neirelevantni into master
Reviewed-on: #18
2022-12-19 22:17:19 +01:00
58f05724e1 hotfix: 485c4180 má špatně proměnnou 2022-12-19 21:25:37 +01:00
5690dc297e hotfix: 1b521049 rozbil tabulku na úzké obrazovce 2022-12-19 21:16:42 +01:00
Pavel "LEdoian" Turinsky
485c4180da make/lib: podpora pro zatím neexistující větve
(pokud jsem to napsal správně.)
2022-12-19 21:09:15 +01:00
Pavel "LEdoian" Turinsky
c2ad4c560d sync_prod_flatpages… 2022-12-17 00:11:00 +01:00
Pavel "LEdoian" Turinsky
4d97e21e96 Merge remote-tracking branch 'gitea/master' into neirelevantni 2022-12-16 23:57:15 +01:00
Pavel "LEdoian" Turinsky
4378c05e3e Korekturovátko: Zrušení irelevance i z nápovědy 2022-12-16 23:51:41 +01:00
Pavel "LEdoian" Turinsky
332e5e88d5 Korekturovátko: ne-irelevantní 2022-12-16 23:44:56 +01:00
9a3f51ca6b hotfix: tabulka došlých řešení 2022-12-14 22:41:00 +01:00
315dc97635 Add: další nápad do aprílu 2022-11-15 13:08:20 +01:00
fd3df6c344 WIP 2022-09-05 20:49:23 +02:00
7ccaf87dfc Pokus o lepší orientaci v dokumentaci 2022-09-04 18:39:00 +02:00
14 changed files with 115 additions and 25 deletions

File diff suppressed because one or more lines are too long

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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
View 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.

View file

@ -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

View file

@ -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 %}

View file

@ -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

View file

@ -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;

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 = 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):

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__jmeno','osoba__prijmeni')
ordering = ('osoba__prijmeni', 'osoba__jmeno')
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(
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)

View file

@ -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()