From 7ccaf87dfca5688f233612e1945085417decbf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Sun, 10 Jul 2022 23:38:11 +0200 Subject: [PATCH 001/120] =?UTF-8?q?Pokus=20o=20lep=C5=A1=C3=AD=20orientaci?= =?UTF-8?q?=20v=20dokumentaci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/index.rst | 5 ++++- docs/sphinx.rst | 4 +++- docs/zapisy/zapisy.rst | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 10d6016f..5613124b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,14 +6,17 @@ Vítejte v dokumentaci M&Mího webu! =================================== +Abychom uměli web vyvíjet, musíme ho nejdřív umět :doc:`naklonovat a spustit `. + .. toctree:: :caption: M&M web :maxdepth: 2 + :titlesonly: vyvoj sphinx - dalsi_soubory modules/modules + dalsi_soubory zapisy/zapisy diff --git a/docs/sphinx.rst b/docs/sphinx.rst index fea2c1a7..2757ae16 100644 --- a/docs/sphinx.rst +++ b/docs/sphinx.rst @@ -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 diff --git a/docs/zapisy/zapisy.rst b/docs/zapisy/zapisy.rst index 1150a65c..7839449f 100644 --- a/docs/zapisy/zapisy.rst +++ b/docs/zapisy/zapisy.rst @@ -2,4 +2,7 @@ Zápisy ====== .. toctree:: - 2021-12-06-testovani_dokumentace_codereview \ No newline at end of file + :caption: Importy zápisů z Markdownu + :maxdepth: 1 + + 2021-12-06-testovani_dokumentace_codereview \ No newline at end of file From fd3df6c3443ba5c909be667d02b41189b146484d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 5 Sep 2022 20:49:23 +0200 Subject: [PATCH 002/120] WIP --- docs/index.rst | 15 ++++++++++++++- docs/sphinx.rst | 2 +- docs/struktura.rst | 31 +++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 docs/struktura.rst diff --git a/docs/index.rst b/docs/index.rst index 5613124b..1fdc4ce7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,20 @@ Vítejte v dokumentaci M&Mího webu! =================================== -Abychom uměli web vyvíjet, musíme ho nejdřív umět :doc:`naklonovat a spustit `. +Tzv. produkce (tedy to, co vidí uživatelé) běží na ``_ (resp. +``_), 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 +``_. + +Abychom uměli web vyvíjet, musíme ho většinou nejdřív umět +:doc:`naklonovat a spustit lokálně `. + +:doc:`struktura mamwebu ` se řídí hlavně djangem, ale snažíme se +také o oddělení jednotlivých částí do :doc:`samostatných aplikací +`. + +Dokumentace (jak v ``docs/``, tak přímo v kódu) je psaná ve +:doc:`sphinxu `. .. toctree:: :caption: M&M web diff --git a/docs/sphinx.rst b/docs/sphinx.rst index 2757ae16..6a9b7a53 100644 --- a/docs/sphinx.rst +++ b/docs/sphinx.rst @@ -15,7 +15,7 @@ To je snad vše, co je potřeba vědět k dokumentaci mamwebu. Následující se 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 diff --git a/docs/struktura.rst b/docs/struktura.rst new file mode 100644 index 00000000..439e39e9 --- /dev/null +++ b/docs/struktura.rst @@ -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í ` 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 `. + +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. From e3ebb925e1746aaa6953bd714df11d352e18237e Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 7 Nov 2022 22:32:06 +0100 Subject: [PATCH 003/120] =?UTF-8?q?Reforma=20Makefil=C5=AF=20WIP=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 156 ++------------------------------- Makefile_old | 128 +++++++++++++++++++++++++++ make/all | 17 ++++ make/deploy | 3 + make/deploy_prod | 3 + make/install | 1 + make/install_venv | 3 + make/install_web | 3 + make/lib.sh | 26 ++++++ make/push_compiled_vue_to_test | 3 + make/run | 3 + make/schema | 3 + make/schema_all.pdf | 3 + make/schema_seminar.pdf | 3 + make/sync_local | 3 + make/sync_local_db | 3 + make/sync_local_media | 3 + make/sync_prod_flatpages | 3 + make/sync_test | 3 + make/sync_test_db_aggressive | 3 + make/sync_test_media | 3 + make/test | 3 + make/venv_check | 3 + 23 files changed, 231 insertions(+), 151 deletions(-) create mode 100644 Makefile_old create mode 100755 make/all create mode 100755 make/deploy create mode 100755 make/deploy_prod create mode 120000 make/install create mode 100755 make/install_venv create mode 100755 make/install_web create mode 100644 make/lib.sh create mode 100755 make/push_compiled_vue_to_test create mode 100755 make/run create mode 100755 make/schema create mode 100755 make/schema_all.pdf create mode 100755 make/schema_seminar.pdf create mode 100755 make/sync_local create mode 100755 make/sync_local_db create mode 100755 make/sync_local_media create mode 100755 make/sync_prod_flatpages create mode 100755 make/sync_test create mode 100755 make/sync_test_db_aggressive create mode 100755 make/sync_test_media create mode 100755 make/test create mode 100755 make/venv_check diff --git a/Makefile b/Makefile index 94a377c4..5e3acddc 100644 --- a/Makefile +++ b/Makefile @@ -1,154 +1,8 @@ -PYTHON ?= python3 -VENV ?= ${PYTHON} -m venv -# Všechny flagy, které se s venvem/virtualenvem/... mají volat patří sem. Volá se "${VENV} cesta" -VENV_PATH ?= env -# Musí být definovaná, i kdyby to měla být "." +%: + @make/$* -.PHONY: all venv_check clean install install_web install_venv clean_venv clean_schema run test deploy_test deploy_prod sync_test_media sync_test_db sync_test sync_local_media sync_local_db sync_local - -# activate by mělo být předpokladem ke všemu, co volá webový python (i.e. python nasazený do ${VENV}u kvůli webu, např. manage.py) all: - @# Just echo: - # Install je trochu magický: - # Spusť následující posloupnost příkazů: - # make install_venv - # . ${VENV_PATH}/bin/activate - # make install_web - # - # Pokud install_web říká Error: pg_config executable not found. nainstaluj si libpq-dev - # Pokud chybová hláška obsahuje #include , nainstaluj si python3-dev - # - # Až skončíš s vývojem webu, spusť "deactivate". Tím zmizí '(${VENV_PATH})' ze začátku promptu. - -venv_check: - @# Pokud org nemá zapnutý venv, poradíme mu, aby si ho zapnul a spadneme. Jinak nic. - @expr $$PATH : ".*:*$(shell pwd)/${VENV_PATH}/bin" > /dev/null && exit 0 || echo 'Není zapnutý venv, spusť ". ${VENV_PATH}/bin/activate".\nPokud není venv nainstalovaný, spusť "make install_venv"' && false - -clean: clean_venv clean_schema - -install: install_web - -install_web: venv_check - @# venv může být příšerně starý, takže nejdříve upgradujeme venvové věci - pip install --upgrade pip - pip install --upgrade setuptools - # Instalace závislostí webu - pip install -r requirements.txt --upgrade - # Pro vygenerování tesdat spusť ./manage.py testdata - # Po vygenerování testdat spusť ./manage.py loaddata data/*, ať máš menu a další modely - # Pro synchronizaci flatpages spusť make sync_prod_flatpages - -install_venv: - ${VENV} ${VENV_PATH} - -clean_venv: - # Možná není 100% foolproof... - @test ! ${VENV_PATH} = . || ! echo "Smaž si prosím venv sám, nebudu mazat celý web" - rm -rfv ${VENV_PATH} - rm -f pip-selfcheck.json -clean_schema: - rm -f schema_seminar.pdf schema_all.pdf - -run: venv_check - ./manage.py runserver - -test: venv_check - ./manage.py test -v2 seminar mamweb - -# DB schemata - -schema: schema_seminar.pdf schema_all.pdf - -schema_seminar.pdf: venv_check - ./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf - -schema_all.pdf: venv_check - ./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf - -# Deploy to current *mamweb-test* directory -deploy_test: venv_check - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-test" ]; then echo "Only possible in directory mamweb-test"; exit 1; fi - @echo "Installing version from origin/test ..." - git pull origin test - git clean -f - make install - ./manage.py migrate - ./manage.py collectstatic --noinput - (chown -R :mam . || true ) - (chmod -R g+rX,go-w . || true ) - @echo Restarting systemd unit - systemctl --user restart mamweb-test.service - @echo Done. - -# Deploy to current *mamweb-prod* directory -deploy_prod: venv_check - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-prod" ]; then echo "Only possible in directory mamweb-prod"; exit 1; fi - @echo "Backing up production DB ..." - ( cd -P .. && ./backup_prod_db.sh ) - @echo "Installing version from origin/master ..." - git pull origin master - git clean -f - make install - ./manage.py migrate - ./manage.py collectstatic --noinput - (chown -R :mam . || true ) - (chmod -R g+rX,go-w . || true ) - @echo Restarting systemd user unit for MaM web - systemctl --user restart mamweb-prod.service - @echo Done. - - -sync_prod_flatpages: venv_check - @echo Downloading current version of flatpages from mamweb-prod. - ssh mam-web@gimli.ms.mff.cuni.cz \ - "cd /akce/mam/www/mamweb-prod; . env/bin/activate; ./manage.py dumpdata flatpages --indent=2 > flat.json; ./fix_json.py flat.json flat_fixed.json" - rsync -ave ssh mam-web@gimli.ms.mff.cuni.cz:/akce/mam/www/mamweb-prod/flat_fixed.json data/flat.json - @echo "Applying downloaded flatpages." - ./manage.py loaddata data/flat.json - @echo "Done." - -# Sync test media directory with production -sync_test_media: - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-test" ]; then echo "Only possible in /akce/mam/www/mamweb-test"; exit 1; fi - rsync -av --delete /akce/mam/www/mamweb-prod/media/ ./media - -# Sync (with drop) test database with production database -sync_test_db_aggressive: - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - pg_dump mam_test > dump-test-`date +"%Y%m%d_%H%M"`.sql - pg_dump -Fc mam_prod > dump-prod.sql - @# I am not sure which shell is used, so I am calling bash to make sure - psql mam_test -c 'DROP OWNED BY "mam-web";' - pg_restore -c --if-exists -d mam_test dump-prod.sql - rm dump-prod.sql - psql mam_test -c "UPDATE django_site SET name='MaMweb (test)', domain='mam-test.ks.matfyz.cz' WHERE id=1" - @echo Done. - -# Sync test with production -# HACK ALERT: using aggressive variant, due to the schemas being too different. -sync_test: sync_test_media sync_test_db_aggressive - - -# Sync media directory with atrey. Useful for local development with production database -# Does not sync Galerie and CACHE (too huge). -sync_local_media: - rsync -ave ssh --exclude Galerie --exclude CACHE\ - mam-web@gimli.ms.mff.cuni.cz:/akce/mam/www/mamweb-prod/media/ ./media/ -# Downloads and restores production database to local database. PostgreSQL only. -sync_local_db: - scp mam-web@gimli.ms.mff.cuni.cz:`ssh mam-web@gimli.ms.mff.cuni.cz 'ls -v /akce/mam/www/backups/mam_prod-*\.pgdump.xz | tail -n 1'` \ - ./last.pgdump.xz - xz -fd last.pgdump.xz - pg_restore -c -d mam-prod last.pgdump - -# Sync database and media. See above lines -sync_local: sync_local_media sync_local_db + @# Nevím, proč to nefunguje bez těla, ale vlastně je mi to jedno… + @make/all -# 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' +.PHONY: all diff --git a/Makefile_old b/Makefile_old new file mode 100644 index 00000000..ccfdb143 --- /dev/null +++ b/Makefile_old @@ -0,0 +1,128 @@ +.PHONY: all venv_check clean install install_web install_venv clean_venv clean_schema run test deploy_test deploy_prod sync_test_media sync_test_db sync_test sync_local_media sync_local_db sync_local + +install: install_web + +install_web: venv_check + @# venv může být příšerně starý, takže nejdříve upgradujeme venvové věci + pip install --upgrade pip + pip install --upgrade setuptools + # Instalace závislostí webu + pip install -r requirements.txt --upgrade + # Pro vygenerování tesdat spusť ./manage.py testdata + # Po vygenerování testdat spusť ./manage.py loaddata data/*, ať máš menu a další modely + # Pro synchronizaci flatpages spusť make sync_prod_flatpages + +install_venv: + ${VENV} ${VENV_PATH} + +clean_venv: + # Možná není 100% foolproof... + @test ! ${VENV_PATH} = . || ! echo "Smaž si prosím venv sám, nebudu mazat celý web" + rm -rfv ${VENV_PATH} + rm -f pip-selfcheck.json +clean_schema: + rm -f schema_seminar.pdf schema_all.pdf + +run: venv_check + ./manage.py runserver + +test: venv_check + ./manage.py test -v2 seminar mamweb + +# DB schemata + +schema: schema_seminar.pdf schema_all.pdf + +schema_seminar.pdf: venv_check + ./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf + +schema_all.pdf: venv_check + ./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf + +# Deploy to current *mamweb-test* directory +deploy: venv_check + @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi + @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-test" ]; then echo "Only possible in directory mamweb-test"; exit 1; fi + @echo "Installing version from origin/${BRANCH} ..." + git pull origin ${BRANCH} + git clean -f + make install + ./manage.py migrate + ./manage.py collectstatic --noinput + (chown -R :mam . || true ) + (chmod -R g+rX,go-w . || true ) + @echo Restarting systemd unit + systemctl --user restart mamweb-test.service + @echo Done. + +# Deploy to current *mamweb-prod* directory +deploy_prod: venv_check + @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi + @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-prod" ]; then echo "Only possible in directory mamweb-prod"; exit 1; fi + @echo "Backing up production DB ..." + ( cd -P .. && ./backup_prod_db.sh ) + @echo "Installing version from origin/master ..." + git pull origin master + git clean -f + make install + ./manage.py migrate + ./manage.py collectstatic --noinput + (chown -R :mam . || true ) + (chmod -R g+rX,go-w . || true ) + @echo Restarting systemd user unit for MaM web + systemctl --user restart mamweb-prod.service + @echo Done. + + +sync_prod_flatpages: venv_check + @echo Downloading current version of flatpages from mamweb-prod. + ssh mam-web@gimli.ms.mff.cuni.cz \ + "cd /akce/mam/www/mamweb-prod; . env/bin/activate; ./manage.py dumpdata flatpages --indent=2 > flat.json; ./fix_json.py flat.json flat_fixed.json" + rsync -ave ssh mam-web@gimli.ms.mff.cuni.cz:/akce/mam/www/mamweb-prod/flat_fixed.json data/flat.json + @echo "Applying downloaded flatpages." + ./manage.py loaddata data/flat.json + @echo "Done." + +# Sync test media directory with production +sync_test_media: + @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi + @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-test" ]; then echo "Only possible in /akce/mam/www/mamweb-test"; exit 1; fi + rsync -av --delete /akce/mam/www/mamweb-prod/media/ ./media + +# Sync (with drop) test database with production database +sync_test_db_aggressive: + @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi + pg_dump mam_test > dump-test-`date +"%Y%m%d_%H%M"`.sql + pg_dump -Fc mam_prod > dump-prod.sql + @# I am not sure which shell is used, so I am calling bash to make sure + psql mam_test -c 'DROP OWNED BY "mam-web";' + pg_restore -c --if-exists -d mam_test dump-prod.sql + rm dump-prod.sql + psql mam_test -c "UPDATE django_site SET name='MaMweb (test)', domain='mam-test.ks.matfyz.cz' WHERE id=1" + @echo Done. + +# Sync test with production +# HACK ALERT: using aggressive variant, due to the schemas being too different. +sync_test: sync_test_media sync_test_db_aggressive + + +# Sync media directory with atrey. Useful for local development with production database +# Does not sync Galerie and CACHE (too huge). +sync_local_media: + rsync -ave ssh --exclude Galerie --exclude CACHE\ + mam-web@gimli.ms.mff.cuni.cz:/akce/mam/www/mamweb-prod/media/ ./media/ +# Downloads and restores production database to local database. PostgreSQL only. +sync_local_db: + scp mam-web@gimli.ms.mff.cuni.cz:`ssh mam-web@gimli.ms.mff.cuni.cz 'ls -v /akce/mam/www/backups/mam_prod-*\.pgdump.xz | tail -n 1'` \ + ./last.pgdump.xz + xz -fd last.pgdump.xz + pg_restore -c -d mam-prod last.pgdump + +# 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/make/all b/make/all new file mode 100755 index 00000000..de25100a --- /dev/null +++ b/make/all @@ -0,0 +1,17 @@ +#!/bin/bash + +set -euo pipefail +. make/lib.sh + +echo "Install je trochu magický: + Spusť následující posloupnost příkazů: + make install_venv + . ${VENV_PATH}/bin/activate + make install_web + + Pokud install_web říká Error: pg_config executable not found. nainstaluj si libpq-dev + Pokud chybová hláška obsahuje #include , nainstaluj si python3-dev + + Až skončíš s vývojem webu, spusť "deactivate". Tím zmizí '(${VENV_PATH})' ze začátku promptu." + + diff --git a/make/deploy b/make/deploy new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/deploy @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/deploy_prod b/make/deploy_prod new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/deploy_prod @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/install b/make/install new file mode 120000 index 00000000..d6d811b7 --- /dev/null +++ b/make/install @@ -0,0 +1 @@ +make/install_web \ No newline at end of file diff --git a/make/install_venv b/make/install_venv new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/install_venv @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/install_web b/make/install_web new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/install_web @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/lib.sh b/make/lib.sh new file mode 100644 index 00000000..e4aac157 --- /dev/null +++ b/make/lib.sh @@ -0,0 +1,26 @@ +#!/bin/false Tohle je knihovna, nemá se spouštět, ale načítat pomocí source(1) nebo '.' + +PYTHON="${PYTHON:-python3}" +VENV="${VENV:-${PYTHON} -m venv}" +VENV_PATH="${VENV_PATH:-env}" +BRANCH="${BRANCH:-master}" + +REPO="${REPO:-git@gitea.ks.matfyz.cz:mam/mamweb.git}" +GIMLI='gimli.ms.mff.cuni.cz' +GIMLI_LOGIN="mam-web@$GIMLI" +# Skutečné cesty, jak je vrátí `realpath` +PRODWEB="/aux/akce/mam/www/mamweb-prod" +TESTWEB="/aux/akce/mam/www/mamweb-test" + +function die { + echo "$@" >&2 + exit 1 +} + +# Vždycky chceme zajistit, že běžíme z rootu repozitáře +test -d '.git' || die "Make skript spuštěn ve špatné složce, spusť ho z kořenového adresáře repozitáře." + +function ensure_venv { + test -f "$VENV_PATH/bin/activate" || $VENV "$VENV_PATH" + . "$VENV_PATH/bin/activate" +} diff --git a/make/push_compiled_vue_to_test b/make/push_compiled_vue_to_test new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/push_compiled_vue_to_test @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/run b/make/run new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/run @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/schema b/make/schema new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/schema @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/schema_all.pdf b/make/schema_all.pdf new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/schema_all.pdf @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/schema_seminar.pdf b/make/schema_seminar.pdf new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/schema_seminar.pdf @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_local b/make/sync_local new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_local @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_local_db b/make/sync_local_db new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_local_db @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_local_media b/make/sync_local_media new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_local_media @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_prod_flatpages b/make/sync_prod_flatpages new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_prod_flatpages @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_test b/make/sync_test new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_test @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_test_db_aggressive b/make/sync_test_db_aggressive new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_test_db_aggressive @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/sync_test_media b/make/sync_test_media new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/sync_test_media @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/test b/make/test new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/test @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail diff --git a/make/venv_check b/make/venv_check new file mode 100755 index 00000000..60347f1e --- /dev/null +++ b/make/venv_check @@ -0,0 +1,3 @@ +#!/bin/bash + +set -exuo pipefail From 75f388ddfb0fd8780c79ceaecb8b4685ffe9d676 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 7 Nov 2022 22:34:54 +0100 Subject: [PATCH 004/120] Fix symlink --- make/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/install b/make/install index d6d811b7..53e51b50 120000 --- a/make/install +++ b/make/install @@ -1 +1 @@ -make/install_web \ No newline at end of file +install_web \ No newline at end of file From 9f78963e7f26963cca1d8442d9508f5f179aa7d2 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 7 Nov 2022 23:12:57 +0100 Subject: [PATCH 005/120] =?UTF-8?q?Pozn=C3=A1mka=20o=20neplatnosti=20READM?= =?UTF-8?q?E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 369dd30e..5d2f9c30 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**TODO: přepsat, tohle dávno neplatí!** + Basic commands for web development ================================== From 8b36e79f045c58d45f2432b9393f8c1babac0ff1 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 7 Nov 2022 23:14:06 +0100 Subject: [PATCH 006/120] Makefile reforma vol.2 [WIP] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Už máme ty jednoduché targety (nebo byly smazány), zbývá dořešit několik složitých. --- Makefile_old | 63 ---------------------------------- make/install_venv | 3 ++ make/install_web | 13 +++++++ make/lib.sh | 1 + make/push_compiled_vue_to_test | 6 ++++ make/run | 5 +++ make/schema | 6 ++++ make/schema_all.pdf | 3 -- make/schema_seminar.pdf | 3 -- make/sync_local | 3 -- make/sync_local_db | 3 -- make/sync_local_media | 3 -- make/test | 5 +++ 13 files changed, 39 insertions(+), 78 deletions(-) delete mode 100755 make/schema_all.pdf delete mode 100755 make/schema_seminar.pdf delete mode 100755 make/sync_local delete mode 100755 make/sync_local_db delete mode 100755 make/sync_local_media diff --git a/Makefile_old b/Makefile_old index ccfdb143..d9f1f8a2 100644 --- a/Makefile_old +++ b/Makefile_old @@ -1,44 +1,3 @@ -.PHONY: all venv_check clean install install_web install_venv clean_venv clean_schema run test deploy_test deploy_prod sync_test_media sync_test_db sync_test sync_local_media sync_local_db sync_local - -install: install_web - -install_web: venv_check - @# venv může být příšerně starý, takže nejdříve upgradujeme venvové věci - pip install --upgrade pip - pip install --upgrade setuptools - # Instalace závislostí webu - pip install -r requirements.txt --upgrade - # Pro vygenerování tesdat spusť ./manage.py testdata - # Po vygenerování testdat spusť ./manage.py loaddata data/*, ať máš menu a další modely - # Pro synchronizaci flatpages spusť make sync_prod_flatpages - -install_venv: - ${VENV} ${VENV_PATH} - -clean_venv: - # Možná není 100% foolproof... - @test ! ${VENV_PATH} = . || ! echo "Smaž si prosím venv sám, nebudu mazat celý web" - rm -rfv ${VENV_PATH} - rm -f pip-selfcheck.json -clean_schema: - rm -f schema_seminar.pdf schema_all.pdf - -run: venv_check - ./manage.py runserver - -test: venv_check - ./manage.py test -v2 seminar mamweb - -# DB schemata - -schema: schema_seminar.pdf schema_all.pdf - -schema_seminar.pdf: venv_check - ./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf - -schema_all.pdf: venv_check - ./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf - # Deploy to current *mamweb-test* directory deploy: venv_check @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi @@ -104,25 +63,3 @@ sync_test_db_aggressive: # Sync test with production # HACK ALERT: using aggressive variant, due to the schemas being too different. sync_test: sync_test_media sync_test_db_aggressive - - -# Sync media directory with atrey. Useful for local development with production database -# Does not sync Galerie and CACHE (too huge). -sync_local_media: - rsync -ave ssh --exclude Galerie --exclude CACHE\ - mam-web@gimli.ms.mff.cuni.cz:/akce/mam/www/mamweb-prod/media/ ./media/ -# Downloads and restores production database to local database. PostgreSQL only. -sync_local_db: - scp mam-web@gimli.ms.mff.cuni.cz:`ssh mam-web@gimli.ms.mff.cuni.cz 'ls -v /akce/mam/www/backups/mam_prod-*\.pgdump.xz | tail -n 1'` \ - ./last.pgdump.xz - xz -fd last.pgdump.xz - pg_restore -c -d mam-prod last.pgdump - -# 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/make/install_venv b/make/install_venv index 60347f1e..f5f3664f 100755 --- a/make/install_venv +++ b/make/install_venv @@ -1,3 +1,6 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +${VENV} ${VENV_PATH} diff --git a/make/install_web b/make/install_web index 60347f1e..5ebf963d 100755 --- a/make/install_web +++ b/make/install_web @@ -1,3 +1,16 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +ensure_venv + +# Aktualizace toolchainu +pip install --upgrade pip setuptools +# Instalace závislostí webu +pip install -r requirements.txt --upgrade + +# XXX: Připomínka z původního Makefile: +echo 'Pro vygenerování tesdat spusť ./manage.py testdata +Po vygenerování testdat spusť ./manage.py loaddata data/*, ať máš menu a další modely +Pro synchronizaci flatpages spusť make/sync_prod_flatpages' diff --git a/make/lib.sh b/make/lib.sh index e4aac157..3e4e99c9 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -18,6 +18,7 @@ function die { } # Vždycky chceme zajistit, že běžíme z rootu repozitáře +# TODO: chceme? Nechceme naopak umět to spouštět odkudkoliv, aspoň u většiny targetů? test -d '.git' || die "Make skript spuštěn ve špatné složce, spusť ho z kořenového adresáře repozitáře." function ensure_venv { diff --git a/make/push_compiled_vue_to_test b/make/push_compiled_vue_to_test index 60347f1e..8ed57cd4 100755 --- a/make/push_compiled_vue_to_test +++ b/make/push_compiled_vue_to_test @@ -1,3 +1,9 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +scp vue_frontend/webpack-stats.json "$GIMLI_LOGIN:$TESTWEB/vue_frontend/" +rsync -ave ssh seminar/static/seminar/vue "$GIMLI_LOGIN:$TESTWEB/seminar/static/seminar/" +# TODO: Je OK tady zapínat venv přímo takhle, nebo by to taky mělo být enkapsulováno do nějakého skriptu (e.g. make/deploy/collect_static)? +ssh "$GIMLI_LOGIN" "cd $TESTWEB && . env/bin/activate && ./manage.py collectstatic --noinput' diff --git a/make/run b/make/run index 60347f1e..44852432 100755 --- a/make/run +++ b/make/run @@ -1,3 +1,8 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +ensure_venv + +./manage.py runserver diff --git a/make/schema b/make/schema index 60347f1e..9688e49a 100755 --- a/make/schema +++ b/make/schema @@ -1,3 +1,9 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +ensure_venv + +./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf +./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf diff --git a/make/schema_all.pdf b/make/schema_all.pdf deleted file mode 100755 index 60347f1e..00000000 --- a/make/schema_all.pdf +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/schema_seminar.pdf b/make/schema_seminar.pdf deleted file mode 100755 index 60347f1e..00000000 --- a/make/schema_seminar.pdf +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/sync_local b/make/sync_local deleted file mode 100755 index 60347f1e..00000000 --- a/make/sync_local +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/sync_local_db b/make/sync_local_db deleted file mode 100755 index 60347f1e..00000000 --- a/make/sync_local_db +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/sync_local_media b/make/sync_local_media deleted file mode 100755 index 60347f1e..00000000 --- a/make/sync_local_media +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/test b/make/test index 60347f1e..bdf9f12b 100755 --- a/make/test +++ b/make/test @@ -1,3 +1,8 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +ensure_venv + +./manage.py test -v2 From 657e73f2f0ac199dc9bf82bae029b3e634a1af45 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 7 Nov 2022 23:15:39 +0100 Subject: [PATCH 007/120] =?UTF-8?q?Pozn=C3=A1mka=20o=20d=C5=AFvodu=20exist?= =?UTF-8?q?ence=20Makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 5e3acddc..5f902dda 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +# Existence tohohle Makefile je tu jen proto, aby fungovala svalová paměť. Pokud můžete, použijte rovnou `make/…` %: @make/$* From 4f7c4057eafd8e49700650a57e4ead3f732addd5 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 14 Nov 2022 21:37:01 +0100 Subject: [PATCH 008/120] fix uvozovek Thanks shellcheck --- make/all | 2 +- make/push_compiled_vue_to_test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/make/all b/make/all index de25100a..c6a92157 100755 --- a/make/all +++ b/make/all @@ -12,6 +12,6 @@ echo "Install je trochu magický: Pokud install_web říká Error: pg_config executable not found. nainstaluj si libpq-dev Pokud chybová hláška obsahuje #include , nainstaluj si python3-dev - Až skončíš s vývojem webu, spusť "deactivate". Tím zmizí '(${VENV_PATH})' ze začátku promptu." + Až skončíš s vývojem webu, spusť 'deactivate'. Tím zmizí '(${VENV_PATH})' ze začátku promptu." diff --git a/make/push_compiled_vue_to_test b/make/push_compiled_vue_to_test index 8ed57cd4..f386ee86 100755 --- a/make/push_compiled_vue_to_test +++ b/make/push_compiled_vue_to_test @@ -6,4 +6,4 @@ set -exuo pipefail scp vue_frontend/webpack-stats.json "$GIMLI_LOGIN:$TESTWEB/vue_frontend/" rsync -ave ssh seminar/static/seminar/vue "$GIMLI_LOGIN:$TESTWEB/seminar/static/seminar/" # TODO: Je OK tady zapínat venv přímo takhle, nebo by to taky mělo být enkapsulováno do nějakého skriptu (e.g. make/deploy/collect_static)? -ssh "$GIMLI_LOGIN" "cd $TESTWEB && . env/bin/activate && ./manage.py collectstatic --noinput' +ssh "$GIMLI_LOGIN" "cd $TESTWEB && . env/bin/activate && ./manage.py collectstatic --noinput" From a9e7cd7af27014dc3904ae9039fb9feb68b09daa Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 14 Nov 2022 23:16:19 +0100 Subject: [PATCH 009/120] =?UTF-8?q?Deploy=20v=20nov=C3=A9m=20kab=C3=A1tu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit prosím čtěte… --- Makefile_old | 35 ----------------------------------- make/deploy | 22 ++++++++++++++++++++++ make/deploy_prod | 31 +++++++++++++++++++++++++++++++ make/lib.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 35 deletions(-) diff --git a/Makefile_old b/Makefile_old index d9f1f8a2..20a19bdf 100644 --- a/Makefile_old +++ b/Makefile_old @@ -1,38 +1,3 @@ -# Deploy to current *mamweb-test* directory -deploy: venv_check - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-test" ]; then echo "Only possible in directory mamweb-test"; exit 1; fi - @echo "Installing version from origin/${BRANCH} ..." - git pull origin ${BRANCH} - git clean -f - make install - ./manage.py migrate - ./manage.py collectstatic --noinput - (chown -R :mam . || true ) - (chmod -R g+rX,go-w . || true ) - @echo Restarting systemd unit - systemctl --user restart mamweb-test.service - @echo Done. - -# Deploy to current *mamweb-prod* directory -deploy_prod: venv_check - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-prod" ]; then echo "Only possible in directory mamweb-prod"; exit 1; fi - @echo "Backing up production DB ..." - ( cd -P .. && ./backup_prod_db.sh ) - @echo "Installing version from origin/master ..." - git pull origin master - git clean -f - make install - ./manage.py migrate - ./manage.py collectstatic --noinput - (chown -R :mam . || true ) - (chmod -R g+rX,go-w . || true ) - @echo Restarting systemd user unit for MaM web - systemctl --user restart mamweb-prod.service - @echo Done. - - sync_prod_flatpages: venv_check @echo Downloading current version of flatpages from mamweb-prod. ssh mam-web@gimli.ms.mff.cuni.cz \ diff --git a/make/deploy b/make/deploy index 60347f1e..d25df6ce 100755 --- a/make/deploy +++ b/make/deploy @@ -1,3 +1,25 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +if test "$USER" != "mam-web"; +then + echo >&2 "Nasadit web smí jen uživatel mam-web" + exit 1 +fi + +if test "$(readlink -f .)" != "$TESTWEB"; +then + echo >&2 "Nasadit testweb lze jen v jeho složce" + exit 1 +fi + +CURRENT_BRANCH="$(git branch --show-current)" +BRANCH="${1:-$CURRENT_BRANCH}" + +safe_checkout_branch "$BRANCH" "$0" + +# Teď máme správnou větev, jdeme vše nainstalovat +install_everything +systemctl --user restart mamweb-test.service diff --git a/make/deploy_prod b/make/deploy_prod index 60347f1e..8e006938 100755 --- a/make/deploy_prod +++ b/make/deploy_prod @@ -1,3 +1,34 @@ #!/bin/bash set -exuo pipefail +. make/lib.sh + +if test "$USER" != "mam-web" +then + echo >&2 "Nasadit web smí jen uživatel mam-web" + exit 1 +fi + +if test "$(readlink -f .)" != "$PRODWEB" +then + echo >&2 "Nasadit testweb lze jen v jeho složce" + exit 1 +fi + +CURRENT_BRANCH="$(git branch --show-current)" +BRANCH="${1:-$CURRENT_BRANCH}" + +if test "$BRANCH" != master +then + echo "Pozor, nasazuješ na produkci větev, která není master ($BRANCH), chceš pokračovat? Pokud ne, sestřel tento skript." + read +fi + +# Záloha DB +( cd -P .. && ./backup_prod_db.sh ) + +safe_checkout_branch "$BRANCH" "$0" + +# Teď máme správnou větev, jdeme vše nainstalovat +install_everything +systemctl --user restart mamweb-prod.service diff --git a/make/lib.sh b/make/lib.sh index 3e4e99c9..fb56e991 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -25,3 +25,45 @@ function ensure_venv { test -f "$VENV_PATH/bin/activate" || $VENV "$VENV_PATH" . "$VENV_PATH/bin/activate" } + + +function safe_checkout_branch { + if "$#" -ne 2; + then + echo >&2 "Použití: $0 " + return 1 + fi + + BRANCH="$1" + SCRIPT="$2" + + echo "Debug: I am $0, $SCRIPT as param" + + git fetch --all + # 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ěží. + if test $(git rev-parse @:make/lib.sh) != $(git rev-parse "$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":"$SCRIPT") + then + echo >&2 "Změna v $SCRIPT, prosím pullni manuálně" + exit 1 + fi + git checkout "$BRANCH" + git pull + git clean -f +} + +function install_everything { + # Skoro celé nasazení webu je stejné pro testweb i pro produkci, tak je to tady dohromady + ensure_venv + make/install + ./manage.py migrate + ./manage.py collectstatic --noinput + chown -R :mam . || true + chmod -R g+rX,go-w . || true +} + From d80babfd73cab28df5ba6effe77edf680e51d2d6 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 14 Nov 2022 23:20:55 +0100 Subject: [PATCH 010/120] =?UTF-8?q?Smaz=C3=A1n=20obsah=20make/sync*?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nic tam nebylo a teď je vidět ve `file *`, že tam nic není. --- make/sync_prod_flatpages | 3 --- make/sync_test | 3 --- make/sync_test_db_aggressive | 3 --- make/sync_test_media | 3 --- 4 files changed, 12 deletions(-) diff --git a/make/sync_prod_flatpages b/make/sync_prod_flatpages index 60347f1e..e69de29b 100755 --- a/make/sync_prod_flatpages +++ b/make/sync_prod_flatpages @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/sync_test b/make/sync_test index 60347f1e..e69de29b 100755 --- a/make/sync_test +++ b/make/sync_test @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/sync_test_db_aggressive b/make/sync_test_db_aggressive index 60347f1e..e69de29b 100755 --- a/make/sync_test_db_aggressive +++ b/make/sync_test_db_aggressive @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail diff --git a/make/sync_test_media b/make/sync_test_media index 60347f1e..e69de29b 100755 --- a/make/sync_test_media +++ b/make/sync_test_media @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail From 323477dbf23a46fa20e59ca47e5968a1c18e600c Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 14 Nov 2022 23:21:20 +0100 Subject: [PATCH 011/120] =?UTF-8?q?Smaz=C3=A1n=20venv=5Fcheck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Je úplně k ničemu, když si nové skripty zapínají venv samy --- make/venv_check | 3 --- 1 file changed, 3 deletions(-) delete mode 100755 make/venv_check diff --git a/make/venv_check b/make/venv_check deleted file mode 100755 index 60347f1e..00000000 --- a/make/venv_check +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -set -exuo pipefail From f6d939fc31251ddfe451930e57275f842f287356 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 15 Nov 2022 11:48:48 +0100 Subject: [PATCH 012/120] =?UTF-8?q?P=C5=99ebyte=C4=8Dn=C3=BD=20st=C5=99edn?= =?UTF-8?q?=C3=ADk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/lib.sh b/make/lib.sh index fb56e991..8ae08e1f 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -28,7 +28,7 @@ function ensure_venv { function safe_checkout_branch { - if "$#" -ne 2; + if "$#" -ne 2 then echo >&2 "Použití: $0 " return 1 From 0458abb41900904d96d3a2d82f516296aa8509ae Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 15 Nov 2022 11:58:22 +0100 Subject: [PATCH 013/120] Kontroly prerekvizit do knihovny --- make/deploy | 13 ++----------- make/deploy_prod | 13 ++----------- make/lib.sh | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/make/deploy b/make/deploy index d25df6ce..4e93d5e1 100755 --- a/make/deploy +++ b/make/deploy @@ -3,17 +3,8 @@ set -exuo pipefail . make/lib.sh -if test "$USER" != "mam-web"; -then - echo >&2 "Nasadit web smí jen uživatel mam-web" - exit 1 -fi - -if test "$(readlink -f .)" != "$TESTWEB"; -then - echo >&2 "Nasadit testweb lze jen v jeho složce" - exit 1 -fi +gimli_only +only_in_directory "$TESTWEB" CURRENT_BRANCH="$(git branch --show-current)" BRANCH="${1:-$CURRENT_BRANCH}" diff --git a/make/deploy_prod b/make/deploy_prod index 8e006938..0c10219d 100755 --- a/make/deploy_prod +++ b/make/deploy_prod @@ -3,17 +3,8 @@ set -exuo pipefail . make/lib.sh -if test "$USER" != "mam-web" -then - echo >&2 "Nasadit web smí jen uživatel mam-web" - exit 1 -fi - -if test "$(readlink -f .)" != "$PRODWEB" -then - echo >&2 "Nasadit testweb lze jen v jeho složce" - exit 1 -fi +gimli_only +only_in_directory "$PRODWEB" CURRENT_BRANCH="$(git branch --show-current)" BRANCH="${1:-$CURRENT_BRANCH}" diff --git a/make/lib.sh b/make/lib.sh index 8ae08e1f..20329d3f 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -26,6 +26,24 @@ function ensure_venv { . "$VENV_PATH/bin/activate" } +function gimli_only { + # Rovnou zkontrolujeme i uživatele + if test "$HOSTNAME" != gimli -o "$USER" != mam-web + then + echo "Tento příkaz se má spouštět na gimlim, chceš pokračovat? Pokud ne, sestřel tento skript." + read + fi +} + +function only_in_directory { + DIR="$1" + CURRENT="$(readlink -f .)" + if test "$CURRENT" != "$DIR" + then + echo "Tento příkaz se má spouštět ve složce $DIR, chceš pokračovat? Pokud ne, sestřel tento skript." + read + fi +} function safe_checkout_branch { if "$#" -ne 2 From 5cfadf9236890bcfc418f1b9af6aa223d7e1d1d1 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 15 Nov 2022 12:05:12 +0100 Subject: [PATCH 014/120] =?UTF-8?q?Make:=20p=C5=99esunuty=20sync=5Ftest=5F?= =?UTF-8?q?media?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Taky jsem smazal nějaké komentáře v Makefile_old, aby nepřekážely. --- Makefile_old | 8 -------- make/sync_test_media | 9 +++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile_old b/Makefile_old index 20a19bdf..7f3799cc 100644 --- a/Makefile_old +++ b/Makefile_old @@ -7,12 +7,6 @@ sync_prod_flatpages: venv_check ./manage.py loaddata data/flat.json @echo "Done." -# Sync test media directory with production -sync_test_media: - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - @if [ `readlink -f .` != "/aux/akce/mam/www/mamweb-test" ]; then echo "Only possible in /akce/mam/www/mamweb-test"; exit 1; fi - rsync -av --delete /akce/mam/www/mamweb-prod/media/ ./media - # Sync (with drop) test database with production database sync_test_db_aggressive: @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi @@ -25,6 +19,4 @@ sync_test_db_aggressive: psql mam_test -c "UPDATE django_site SET name='MaMweb (test)', domain='mam-test.ks.matfyz.cz' WHERE id=1" @echo Done. -# Sync test with production -# HACK ALERT: using aggressive variant, due to the schemas being too different. sync_test: sync_test_media sync_test_db_aggressive diff --git a/make/sync_test_media b/make/sync_test_media index e69de29b..d7991b65 100755 --- a/make/sync_test_media +++ b/make/sync_test_media @@ -0,0 +1,9 @@ +#!/bin/bash + +set -exuo pipefail +. make/lib.sh + +gimli_only +only_in_dir "$TESTWEB" + +rsync -av --delete "$PRODWEB/media/" ./media From 5abd93d27f82df656f95d66c823216ab6fc54ece Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 15 Nov 2022 12:16:50 +0100 Subject: [PATCH 015/120] Zbytek sync_test --- Makefile_old | 14 -------------- make/sync_test | 11 +++++++++++ make/sync_test_db_aggressive | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Makefile_old b/Makefile_old index 7f3799cc..3b32e7e7 100644 --- a/Makefile_old +++ b/Makefile_old @@ -6,17 +6,3 @@ sync_prod_flatpages: venv_check @echo "Applying downloaded flatpages." ./manage.py loaddata data/flat.json @echo "Done." - -# Sync (with drop) test database with production database -sync_test_db_aggressive: - @if [ ${USER} != "mam-web" ]; then echo "Only possible by user mam-web"; exit 1; fi - pg_dump mam_test > dump-test-`date +"%Y%m%d_%H%M"`.sql - pg_dump -Fc mam_prod > dump-prod.sql - @# I am not sure which shell is used, so I am calling bash to make sure - psql mam_test -c 'DROP OWNED BY "mam-web";' - pg_restore -c --if-exists -d mam_test dump-prod.sql - rm dump-prod.sql - psql mam_test -c "UPDATE django_site SET name='MaMweb (test)', domain='mam-test.ks.matfyz.cz' WHERE id=1" - @echo Done. - -sync_test: sync_test_media sync_test_db_aggressive diff --git a/make/sync_test b/make/sync_test index e69de29b..68c6020d 100755 --- a/make/sync_test +++ b/make/sync_test @@ -0,0 +1,11 @@ +#!/bin/bash + +set -exuo pipefail +. make/lib.sh + +# Prerekvizity nekontrolujeme, dokud voláme další make skripty, tak by akorát +# vedly k víc dotazům na stejnou věc a bylo by to otravné. Pokud tu někdy bude +# něco jiného, tak pak ať tu prerekvizity zmíněné jsou. + +make/sync_test_db_aggressive +make/sync_test_media diff --git a/make/sync_test_db_aggressive b/make/sync_test_db_aggressive index e69de29b..11faba7b 100755 --- a/make/sync_test_db_aggressive +++ b/make/sync_test_db_aggressive @@ -0,0 +1,18 @@ +#!/bin/bash + +set -exuo pipefail +. make/lib.sh + +gimli_only +# Teoreticky není potřeba, ale stejně jinde make skripty nejsou a pouštět to z +# produkce nezní jako běžný stav, kromě toho to aktuálně vyrábí pomocné soubory +# v aktuální složce (FIXME do budoucna) a to na produkci nechceme +only_in_directory "$TESTWEB" + +pg_dump mam_test > dump-test-`date +"%Y%m%d_%H%M"`.sql +pg_dump -Fc mam_prod > dump-prod.sql +psql mam_test -c 'DROP OWNED BY "mam-web";' +pg_restore -c --if-exists -d mam_test dump-prod.sql +rm dump-prod.sql +psql mam_test -c "UPDATE django_site SET name='MaMweb (test)', domain='mam-test.ks.matfyz.cz' WHERE id=1" + From 7c90c1ef5e4b7343ca6bc4a7d7a7c78c0c7b3af3 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 15 Nov 2022 12:43:31 +0100 Subject: [PATCH 016/120] sync_prod_flatpages --- Makefile_old | 8 -------- make/sync_prod_flatpages | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 Makefile_old diff --git a/Makefile_old b/Makefile_old deleted file mode 100644 index 3b32e7e7..00000000 --- a/Makefile_old +++ /dev/null @@ -1,8 +0,0 @@ -sync_prod_flatpages: venv_check - @echo Downloading current version of flatpages from mamweb-prod. - ssh mam-web@gimli.ms.mff.cuni.cz \ - "cd /akce/mam/www/mamweb-prod; . env/bin/activate; ./manage.py dumpdata flatpages --indent=2 > flat.json; ./fix_json.py flat.json flat_fixed.json" - rsync -ave ssh mam-web@gimli.ms.mff.cuni.cz:/akce/mam/www/mamweb-prod/flat_fixed.json data/flat.json - @echo "Applying downloaded flatpages." - ./manage.py loaddata data/flat.json - @echo "Done." diff --git a/make/sync_prod_flatpages b/make/sync_prod_flatpages index e69de29b..76c31f1b 100755 --- a/make/sync_prod_flatpages +++ b/make/sync_prod_flatpages @@ -0,0 +1,18 @@ +#!/bin/bash + +set -exuo pipefail +. make/lib.sh + +ensure_venv + +# TODO: This is very ugly, will fix in a future PR (hopefully) +ssh "$GIMLI_LOGIN" " + cd $PRODWEB + . env/bin/activate + ./manage.py dumpdata flatpages --indent=2 > flat.json + ./fix_json.py flat.json flat_fixed.json + " +rsync -ave ssh "$GIMLI_LOGIN:$PRODWEB/flat_fixed.json" data/flat.json + +./manage.py loaddata data/flat.json + From 315dc97635ba155a59910969cc0de2ddaca65b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 15 Nov 2022 13:08:20 +0100 Subject: [PATCH 017/120] =?UTF-8?q?Add:=20dal=C5=A1=C3=AD=20n=C3=A1pad=20d?= =?UTF-8?q?o=20apr=C3=ADlu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/april.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/april.rst b/docs/april.rst index 5f103357..8a479b20 100644 --- a/docs/april.rst +++ b/docs/april.rst @@ -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 From 4fd3a0c4f51f57a45a5bdf14fd73dabc1135aea4 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 17 Nov 2022 01:57:37 +0100 Subject: [PATCH 018/120] =?UTF-8?q?Hlasit=C4=9Bj=C5=A1=C3=AD=20varov=C3=A1?= =?UTF-8?q?n=C3=AD=20p=C5=99i=20pou=C5=BEit=C3=AD=20make?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5f902dda..ff0af09e 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ # Existence tohohle Makefile je tu jen proto, aby fungovala svalová paměť. Pokud můžete, použijte rovnou `make/…` %: - @make/$* + # Používání make jako příkazu je zastaralé, prosím používej radši skripty ze složky make. Spouštím následující příkaz: + make/$* all: @# Nevím, proč to nefunguje bez těla, ale vlastně je mi to jedno… - @make/all + make/all .PHONY: all From 31834b7f8a26df3ab146152988bb416d2533f938 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:12:35 +0100 Subject: [PATCH 019/120] =?UTF-8?q?Zru=C5=A1en=20`make/all`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nic moc nedělal, tak jsem ho nahradil (IMHO lepší) dokumentací. --- Makefile | 3 +-- make/README.md | 34 ++++++++++++++++++++++++++++++++++ make/all | 17 ----------------- 3 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 make/README.md delete mode 100755 make/all diff --git a/Makefile b/Makefile index ff0af09e..09d9acc8 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,6 @@ make/$* all: - @# Nevím, proč to nefunguje bez těla, ale vlastně je mi to jedno… - make/all + @cat make/README.md .PHONY: all diff --git a/make/README.md b/make/README.md new file mode 100644 index 00000000..1d1baa47 --- /dev/null +++ b/make/README.md @@ -0,0 +1,34 @@ +Milý člověče, M&Mí web tě vítá. Prosím, neděs se, zkusím tě provést lokálním zprovozněním webu. + +Předně: většina příkazů bude asi vypisovat spoustu detailů, takže ti doporučuji +si tento text otevřít někde separátně. Nachází se v repozitáři v +`make/README.md`, případně si jej můžeš zobrazit hezčeji vykreslený na +[Gitee](https://gitea.ks.matfyz.cz/mam/mamweb/src/branch/master/make/README.md). + +O zprovoznění webu se stará hlavně skript `make/install_web`. Ten vytvoří +virtualenv (neexistuje-li) a nainstaluje do něj závislosti webu. Pak ovšem +budeš potřebovat nahrát další data do databáze, což uděláš pomocí příkazů +`./manage.py testdata` a `./manage.py loaddata data/*`. Skript +`make/install_web` ti to kdyžtak připomene. + +Samotný web spustíš třeba pomocí `make/run`, nebo ekvivalentně `./manage.py runserver`. + +Pozor: zatímco skripty v `make/` to nepotřebují, pro použití skriptu +`./manage.py` se potřebuješ přepnout do virtuálního prostředí. To uděláš velmi +pravděpodobně spuštěním `source env/bin/activate`, před začátkem _promptu_ by +se mělo objevit `(env)`. Pro opuštění spusť `deactivate`. + +Časté problémy +----- +Je možné, že nemáš všechny potřebné závislosti v systému. Proto je možné, že +`make/install_web` vyhodí nějakou chybovou hlášku: + +- `Error: pg_config executable not found.`: nainstaluj si `libpq-dev` (na Ubuntu/Debianu, jinde se příslušný balíček může jmenovat jinak) +- Chybová hláška obsahuje `#include `: nainstaluj si `python3-dev` + + + diff --git a/make/all b/make/all deleted file mode 100755 index c6a92157..00000000 --- a/make/all +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -euo pipefail -. make/lib.sh - -echo "Install je trochu magický: - Spusť následující posloupnost příkazů: - make install_venv - . ${VENV_PATH}/bin/activate - make install_web - - Pokud install_web říká Error: pg_config executable not found. nainstaluj si libpq-dev - Pokud chybová hláška obsahuje #include , nainstaluj si python3-dev - - Až skončíš s vývojem webu, spusť 'deactivate'. Tím zmizí '(${VENV_PATH})' ze začátku promptu." - - From 1a56ba02f35d2540ca176276be64dfbe35c2277d Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:13:15 +0100 Subject: [PATCH 020/120] =?UTF-8?q?Make:=20v=C3=BDchoz=C3=AD=20target=20se?= =?UTF-8?q?=20jmenuje=20`default`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `all` tam nemá žádnou sémantiku… --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 09d9acc8..fb967112 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # Používání make jako příkazu je zastaralé, prosím používej radši skripty ze složky make. Spouštím následující příkaz: make/$* -all: +default: @cat make/README.md -.PHONY: all +.PHONY: default From d0865010956dbf66f885e87c88050237b03248e6 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:15:00 +0100 Subject: [PATCH 021/120] =?UTF-8?q?make/lib:=20lok=C3=A1ln=C3=AD=20prom?= =?UTF-8?q?=C4=9Bnn=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index 20329d3f..727df395 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -36,8 +36,8 @@ function gimli_only { } function only_in_directory { - DIR="$1" - CURRENT="$(readlink -f .)" + local DIR="$1" + local CURRENT="$(readlink -f .)" if test "$CURRENT" != "$DIR" then echo "Tento příkaz se má spouštět ve složce $DIR, chceš pokračovat? Pokud ne, sestřel tento skript." @@ -52,8 +52,8 @@ function safe_checkout_branch { return 1 fi - BRANCH="$1" - SCRIPT="$2" + local BRANCH="$1" + local SCRIPT="$2" echo "Debug: I am $0, $SCRIPT as param" From 346079cee3e0eb20c924bf09af61e59eac8106a4 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:17:57 +0100 Subject: [PATCH 022/120] make/lib: Funkce install_everything je jen pro Gimliho --- make/lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/make/lib.sh b/make/lib.sh index 727df395..bc919886 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -77,6 +77,7 @@ function safe_checkout_branch { function install_everything { # Skoro celé nasazení webu je stejné pro testweb i pro produkci, tak je to tady dohromady + gimli_only ensure_venv make/install ./manage.py migrate From ac9cb53185edf2c2ee29bea8bffece0d21015bc2 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:23:41 +0100 Subject: [PATCH 023/120] =?UTF-8?q?Hez=C4=8D=C3=AD=20SSH=20spojen=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Přidal jsem `set -euxo pipefail`, takže nejsou potřeba `&&` a obecně se to chová víc jako ostatní make skripty - Venv se zapíná stejně jako v lokálních skriptech, takže se dá jednoduše změnit jeho cesta --- make/push_compiled_vue_to_test | 9 +++++++-- make/sync_prod_flatpages | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/make/push_compiled_vue_to_test b/make/push_compiled_vue_to_test index f386ee86..99495a7b 100755 --- a/make/push_compiled_vue_to_test +++ b/make/push_compiled_vue_to_test @@ -5,5 +5,10 @@ set -exuo pipefail scp vue_frontend/webpack-stats.json "$GIMLI_LOGIN:$TESTWEB/vue_frontend/" rsync -ave ssh seminar/static/seminar/vue "$GIMLI_LOGIN:$TESTWEB/seminar/static/seminar/" -# TODO: Je OK tady zapínat venv přímo takhle, nebo by to taky mělo být enkapsulováno do nějakého skriptu (e.g. make/deploy/collect_static)? -ssh "$GIMLI_LOGIN" "cd $TESTWEB && . env/bin/activate && ./manage.py collectstatic --noinput" +ssh "$GIMLI_LOGIN" " + set -euxo pipefail + cd $TESTWEB + . make/lib.sh + ensure_venv + ./manage.py collectstatic --noinput + " diff --git a/make/sync_prod_flatpages b/make/sync_prod_flatpages index 76c31f1b..4d9d8e1f 100755 --- a/make/sync_prod_flatpages +++ b/make/sync_prod_flatpages @@ -7,8 +7,10 @@ ensure_venv # TODO: This is very ugly, will fix in a future PR (hopefully) ssh "$GIMLI_LOGIN" " + set -euxo pipefail cd $PRODWEB - . env/bin/activate + . make/lib.sh + ensure_venv ./manage.py dumpdata flatpages --indent=2 > flat.json ./fix_json.py flat.json flat_fixed.json " From e36410ee823bffb7e8dbe0be819aa78fc391cbea Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:27:16 +0100 Subject: [PATCH 024/120] =?UTF-8?q?make/lib:=20koment=C3=A1=C5=99e=20kolem?= =?UTF-8?q?=20git-rev-parse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index bc919886..062dc617 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -60,12 +60,13 @@ function safe_checkout_branch { git fetch --all # 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ěží. - if test $(git rev-parse @:make/lib.sh) != $(git rev-parse "$BRANCH":make/lib.sh) + # `git rev-parse` dává SHA-1 hashe objektů, vizte manuálovou stránku pro pochopení. + if test "$(git rev-parse @:make/lib.sh)" != "$(git rev-parse "$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":"$SCRIPT") + if test "$(git rev-parse @:"$SCRIPT")" != "$(git rev-parse "$BRANCH":"$SCRIPT")" then echo >&2 "Změna v $SCRIPT, prosím pullni manuálně" exit 1 From 28760e2c4d0e8adb43c90b3eeed7f9c764996686 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:33:19 +0100 Subject: [PATCH 025/120] =?UTF-8?q?Smaz=C3=A1n=20install=5Fvenv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Všechny make skripty stejně vyrábí venv přes `ensure_venv` a protože web nikdo nikdy nebude instalovat jinak (nebo když bude, tak asi ví, co dělá), tak tohle nedává smysl spouštět. --- make/install_venv | 6 ------ 1 file changed, 6 deletions(-) delete mode 100755 make/install_venv diff --git a/make/install_venv b/make/install_venv deleted file mode 100755 index f5f3664f..00000000 --- a/make/install_venv +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -set -exuo pipefail -. make/lib.sh - -${VENV} ${VENV_PATH} From 5963089539c955363f18a2558f5175ce5ae4fdae Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:37:27 +0100 Subject: [PATCH 026/120] =?UTF-8?q?make/lib:=20.git=20m=C5=AF=C5=BEe=20b?= =?UTF-8?q?=C3=BDt=20i=20norm=C3=A1ln=C3=AD=20soubor=20(worktree)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/lib.sh b/make/lib.sh index 062dc617..7df79d82 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -19,7 +19,7 @@ function die { # Vždycky chceme zajistit, že běžíme z rootu repozitáře # TODO: chceme? Nechceme naopak umět to spouštět odkudkoliv, aspoň u většiny targetů? -test -d '.git' || die "Make skript spuštěn ve špatné složce, spusť ho z kořenového adresáře repozitáře." +test -e '.git' || die "Make skript spuštěn ve špatné složce, spusť ho z kořenového adresáře repozitáře." function ensure_venv { test -f "$VENV_PATH/bin/activate" || $VENV "$VENV_PATH" From e4c2eda955aef9c1442189273f552f20401158c9 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:38:03 +0100 Subject: [PATCH 027/120] make/run bere parametry --- make/run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/run b/make/run index 44852432..c601593a 100755 --- a/make/run +++ b/make/run @@ -5,4 +5,4 @@ set -exuo pipefail ensure_venv -./manage.py runserver +./manage.py runserver "$@" From a390ef03293d70945800a3fecbe5041ebfed03d9 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:40:03 +0100 Subject: [PATCH 028/120] =?UTF-8?q?make/lib:=20safe=5Fcheckout=5Fbranch=20?= =?UTF-8?q?sta=C4=8D=C3=AD=201=20parametr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/deploy | 2 +- make/deploy_prod | 2 +- make/lib.sh | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/make/deploy b/make/deploy index 4e93d5e1..24ade165 100755 --- a/make/deploy +++ b/make/deploy @@ -9,7 +9,7 @@ only_in_directory "$TESTWEB" CURRENT_BRANCH="$(git branch --show-current)" BRANCH="${1:-$CURRENT_BRANCH}" -safe_checkout_branch "$BRANCH" "$0" +safe_checkout_branch "$BRANCH" # Teď máme správnou větev, jdeme vše nainstalovat install_everything diff --git a/make/deploy_prod b/make/deploy_prod index 0c10219d..ec3110c6 100755 --- a/make/deploy_prod +++ b/make/deploy_prod @@ -18,7 +18,7 @@ fi # Záloha DB ( cd -P .. && ./backup_prod_db.sh ) -safe_checkout_branch "$BRANCH" "$0" +safe_checkout_branch "$BRANCH" # Teď máme správnou větev, jdeme vše nainstalovat install_everything diff --git a/make/lib.sh b/make/lib.sh index 7df79d82..581252cf 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -48,14 +48,12 @@ function only_in_directory { function safe_checkout_branch { if "$#" -ne 2 then - echo >&2 "Použití: $0 " + echo >&2 "Použití: $0 " return 1 fi local BRANCH="$1" - local SCRIPT="$2" - - echo "Debug: I am $0, $SCRIPT as param" + local SCRIPT="$0" git fetch --all # Od teď si musíme dát pozor, abychom nezměnili kód, který právě běží. From 1615e921590d06e151d80b3bf8e56e615bc0ee2d Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:45:48 +0100 Subject: [PATCH 029/120] =?UTF-8?q?safe=5Fcheckout=5Fbranch:=20porovn?= =?UTF-8?q?=C3=A1v=C3=A1me=20proti=20upstreamu,=20ne=20aktu=C3=A1ln=C3=AD?= =?UTF-8?q?=20verzi.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index 581252cf..e45df48f 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -59,12 +59,12 @@ function safe_checkout_branch { # 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í. - if test "$(git rev-parse @:make/lib.sh)" != "$(git rev-parse "$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 "$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 From 34e751fd699b4b0e637935848b00145925925a4c Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:52:51 +0100 Subject: [PATCH 030/120] =?UTF-8?q?fixup!=20make/lib:=20safe=5Fcheckout=5F?= =?UTF-8?q?branch=20sta=C4=8D=C3=AD=201=20parametr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/lib.sh b/make/lib.sh index e45df48f..2085ad8f 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -46,7 +46,7 @@ function only_in_directory { } function safe_checkout_branch { - if "$#" -ne 2 + if test "$#" -ne 1 then echo >&2 "Použití: $0 " return 1 From b1e6448c3c47a0b2bc936c9bb4da18cdda3fb348 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 04:57:48 +0100 Subject: [PATCH 031/120] =?UTF-8?q?safe=5Fcheckout=5Fbranch:=20varov=C3=A1?= =?UTF-8?q?n=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/make/lib.sh b/make/lib.sh index 2085ad8f..ce4c9a4d 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -59,6 +59,7 @@ function safe_checkout_branch { # 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)" then echo >&2 "Změna v make/lib.sh, prosím pullni manuálně" From 3fcc03b769014fad6be594c18b3d714eae1901a4 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 05:08:24 +0100 Subject: [PATCH 032/120] Shellcheck :-) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ne že by mi na tom záviselo, ale kód to nezhoršuje a pokud to aspoň o trochu zmenší šanci na nějaké přehlédnutí, tak je to OK. A spoustu varování shellchecku jsem vyignoroval a nemíním plevelit kód komentáři, o čem všem vím a on ne :-) --- make/lib.sh | 4 ++-- make/sync_test_db_aggressive | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index ce4c9a4d..f04b9941 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -60,12 +60,12 @@ function safe_checkout_branch { # 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 "$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 "$BRANCH"@{u}:"$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 diff --git a/make/sync_test_db_aggressive b/make/sync_test_db_aggressive index 11faba7b..9acce93b 100755 --- a/make/sync_test_db_aggressive +++ b/make/sync_test_db_aggressive @@ -9,7 +9,7 @@ gimli_only # v aktuální složce (FIXME do budoucna) a to na produkci nechceme only_in_directory "$TESTWEB" -pg_dump mam_test > dump-test-`date +"%Y%m%d_%H%M"`.sql +pg_dump mam_test > "dump-test-$(date +"%Y%m%d_%H%M").sql" pg_dump -Fc mam_prod > dump-prod.sql psql mam_test -c 'DROP OWNED BY "mam-web";' pg_restore -c --if-exists -d mam_test dump-prod.sql From a184871bf9b26aa4977e5227a809a7a4811b24bd Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 05:16:02 +0100 Subject: [PATCH 033/120] =?UTF-8?q?make/lib:=20Kdy=C5=BE=20se=20to=20nepov?= =?UTF-8?q?ede,=20tak=20budeme=20k=C5=99i=C4=8Det?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uvědomil jsem si, že aktuálně nejde skoro vůbec poznat, jestli make skript uspěl nebo ne. Tohle by se mělo vypsat, když se nepovede. Víc by se mi líbilo, kdyby úspěšné doběhnutí ohlásilo "OK", ale to neumím udělat bez nějakého zápatí skriptů. (Resp. uměl bych: make/lib může být interpretr, který na začátku zinicalizuje proměnné, pak natáhne příslušný skript a na konci ohlásí OK. Ale přijde mi to trochu moc magické, takže pokud někdo nebude nějak extra pro, tak to tak neudělám :-)) --- make/lib.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/make/lib.sh b/make/lib.sh index f04b9941..135f859e 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -17,6 +17,8 @@ function die { exit 1 } +trap 'echo Něco se nepovedlo :-/' ERR + # Vždycky chceme zajistit, že běžíme z rootu repozitáře # TODO: chceme? Nechceme naopak umět to spouštět odkudkoliv, aspoň u většiny targetů? test -e '.git' || die "Make skript spuštěn ve špatné složce, spusť ho z kořenového adresáře repozitáře." From 50eaaf9eb7838b8560e2823b26bda124c8e8f054 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 15:38:41 +0100 Subject: [PATCH 034/120] =?UTF-8?q?P=C5=99ed=C4=9Bl=C3=A1no=20Readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Teď vypadá víc jako readme, na které chceš narazit v gitee, než jako pár random tipů. Random tipy jsem přesunul do dokumentace --- README.md | 137 +++++++++++++++++++++++------------------------------- 1 file changed, 58 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 5d2f9c30..94b8cf2d 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,58 @@ -**TODO: přepsat, tohle dávno neplatí!** - -Basic commands for web development -================================== - -After you clone this repository, run `make`. It will download, locally install -and setup virtualenv and pip, and then locally install all required packages -from `requirements.txt`. - -When working with the code, always use the binaries in `./bin/`, such as -`bin/pip`, `bin/python`, ... never the global python, pip, ... -Use `make` and `./manage.py` for most things. - -Use git :-) - -Quickstart ----------- -Run the following commands: - make install_venv - . env/bin/activate - make install_web - -Pokud install_web říká Error: pg_config executable not found. nainstaluj si libpq-dev -Pokud chybová hláška obsahuje #include , nainstaluj si python3-dev - -After finishing development, run "deactivate". - -Make commands -------------- - -* `make install` (or `make`) - locally install and setup virtualpy, install - required packages. Ran again installs missing packages. Run after changing - `requirements.txt`. - -* `make clean` - remove local python packages. - -* `make veryclean` - remove local packages and virtualpy enviroment and - binaries. - -* `make run` - runs "./manage.py runserver_plus" - -* `make push_test` - pushes the last commited version to test location. - Only git-commited changes are pushed! Rest is re-generated from scratch. - At test server, the media data and database are kept the same. - Everything else not in .gitignore is deleted/overwritten on the test server. - -* `make schema` - generates graph of seminar and all schemas as PDF. Supercool! - -* `make sync_prod_flatpages` - downloads and applies static/flat pages from mamweb-prod - -./manage.py commands --------------------- - -* `./manage.py migrate` - update the database schema, initialise the database. - You need to run this in the beginning. - -* `./manage.py runserver_plus` - run a debugging server for the web. Slightly - enhanced compared to `./manage.py runserver`. - Open [127.0.0.1:8000](127.0.0.1:8000). - -* `./manage.py testdata` - create pseudo-random seminar data and admin/admin - user. - -* `./manage.py test` - run the tests. - -* `./manage.py shell` - run commands, list elemements of database, check syntax - by importing files, etc. - -Configurations --------------- - -* `mamweb/settings_common.py` contains most configuration options. -* `mamweb/settings.py` is used only for local development. -* `mamweb/settings_test.py` is used for testing on atrey. -* `mamweb/settings_prod.py` is used in production deployment. - -These are automatically switched by `make`. - - +Web M&M +====== + +Tohle je repozitář s kódem M&Mího webu. Pokud zde hledáte web samotný nebo +informace o semináři, najdete je na (a upřímně nechápu, +jak jste se dostali k tomuhle textu :-D) + +Pokud jste tu zůstali, tak vás beztak zajímá vývoj webu (a jestli ne, tak +budeme rádi, když začne :-)). + +Co je M&Mweb uvnitř +------ +Celý náš web je napsaný v [Pythonu](https://www.python.org) ve frameworku +[Django](https://www.djangoproject.com/). Web běží na serveru zvaném Gimli, +jako databázi používá PostgreSQL (pro lokální vývoj naopak SQLite) a všechen +náš kód je uložený v [Gitu](https://git-scm.com/) na [téhle +gitee](https://gitea.ks.matfyz.cz/). Pro dokumentaci používáme +[Sphinx](https://www.sphinx-doc.org). + + + +Jak si web pořídit +------ +Prosím přečti si podrobnější návod v (tady by bylo zbytečné +ho duplikovat). + +Jak web vyvíjet +---- + + +Na webu je mnoho věcí k dělání, některé ani nevyžadují kódění (třeba uhánění +orgů, aby si psali medailonky, aktualizace fotek, …), některé se naopak týkají +infrastruktury pod kódem (Gitea, Gimli, …). Je proto těžké mít na to úplně +obecný návod, tak tady je zhruba návod na úpravy kódu a pokud se něco z toho +nedá aplikovat, tak to prostě zkus nějak udělat jinak, po svém. (Omlouvám se +neinformatikům, ale líp to teď nesepíšu :-)) + +1. Nejprve si stáhni repozitář a rozběhni si lokální web u sebe (viz ). +1. Najdi si problém v Kanboardu (klikni na „Issues“ na Gitee) a/nebo se domluv + s webaři, na čem bys tak mohl/a pracovat. +1. Najdi místo, kde se to dá opravit a zkus to tam opravit. Uznávám, že tenhle + bod je otravně obecný, pokud tápeš, zkus se zeptat zkušenějších webařů nebo + podívat do dokumentace. +1. Vyzkoušej, že ti to lokálně funguje tak, jak má. +1. Zvládneš-li a máš-li čas, zkus to i zdokumentovat a/nebo napsat testy (TODO: chybí návod) +1. Po dohodě s webaři to vyzkoušej na testwebu +1. Pošli pull-request a případně zkus reagovat na komentáře +1. Až se změna začlení do hlavní větve (`master`) a nasadí se web na produkci, + můžeš mít radost, že se web bude používat lépe Tobě i ostatním orgům :-) + +### Proč pull-requesty? + + +Účelů pull-requestů je několik. Jednak doufáme, že pomůže webařům se orientovat +v kódu, jednak tím umožňujeme dělat experimenty a dávat si zpětnou vazbu. V +neposlední řadě pomáhají držet aspoň trochu konzistentní kód, což má pomoci +pohodě při programování… (A asi jsem na něco zapomněl :-)) From bac44b41f8a489b0ac2a3d6af449632c9b264539 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 15:39:20 +0100 Subject: [PATCH 035/120] =?UTF-8?q?Dokumentace=20lok=C3=A1ln=C3=ADho=20v?= =?UTF-8?q?=C3=BDvoje?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Obsahuje tipy z Readme, obsahuje velkou část make/README.md. Zatím nevím, co provedu s make/README.md, ale na něco třeba přijdu :-) --- docs/vyvoj.rst | 197 ++++++++++++++++++++++++++++++++++++++++++++----- make/README.md | 32 -------- 2 files changed, 179 insertions(+), 50 deletions(-) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index 438e8199..3bfdace8 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -1,20 +1,181 @@ Lokální vývoj mamwebu ===================== -Stačí spustit:: - - ## Nahradte svym gimli username - git clone USER@gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb.git mamweb - cd mamweb - ## Instalace je trochu magická, spusť následující posloupnost příkazů: - make install_venv - . env/bin/activate - make install_web - - ## Vygeneruje nejaka testovaci data (spis chuda) - ./manage.py testdata - ## Nahraje statické stránky, menu a obrázky v pozadí menu - ./manage.py loaddata data/* - ## Spusti testovaci server na http://127.0.0.1:8000/ - ./manage.py runserver - -Když si lokálně spustíte web, běží na http://127.0.0.1:8000/, admin najdete na http://127.0.0.1:8000/admin/ (admin/admin) Až skončíš s vývojem webu, spusť “deactivate”. Tím zmizí ‘(env)’ ze začátku promptu. \ No newline at end of file + +Asi hlavní část vývoje většiny webu probíhá lokálně. Každý tak může pracovat na +vlastních úpravách nezávisle na ostatních. + +Potřebné vybavení +------- + +Tento soupis cílí na nějakého typického vývojáře webu – Linuxáka. Jistě je +spousta dalších možností, které zde nejsou postihnuty – poraď se s webaři, +pokud si nejsi jistý. (Speciálně lze nějak vyvíjet na Windows, leč lze často +narazit na odlišné chování od Linuxu.) + +Motivace cílení na Linux je to, že Gimli je Linuxový stroj, takže je vývojové +prostředí pak podobné produkci a zmenšuje to množství odlišného chování. + +.. TODO: Na dokumentaci odlišného chování (Postgres vs. SQLite, Win vs. Linux, …) + by to asi chtělo výhledově separátní stránku, ale teď píšu tuhle :-) + +Nutné +^^^^ + +- `Git `_ +- `Python `_ + + - Ideálně ve verzi 3.9 (to je to, co je aktuálně (2022) na Gimlim) + - Včetně pip-u (na Ubuntu balíček ``python3-pip``) a knihoven pro kompilaci + Cčkových rozšíření (``python3-dev``) +- Knihovna pro práci s PostgreSQL (``libpq-dev``) +- Webový prohlížeč +- \*NIXový shell (typicky ``bash``) + +.. TODO: Pokud tu něco chybí, tak to dopiš :-) + +Kromě toho je potřeba mít účet na `Gitee `_, kde +bydlí gitový repozitář s kódem. + +Doporučené +^^^^^^^^^^ + +- Python wheel (možná řeší problémy s potřebou ``-dev`` balíčků…) +- Editor / IDE podporující `Editorconfig `_ +- Uživatelská zkušenost s `produkční verzí webu `_ +- Účet v `Kanci `_ + +.. TODO: A nejspíš další věci, na které jsem si teď nevzpomněl. + + +Zprovoznění +------- + +Nejprve je potřeba stáhnout si repozitář. To se provede příkazem ``git clone +https://gitea.ks.matfyz.cz/mam/mamweb.git``, případně ``git clone +git@gitea.ks.matfyz.cz:mam/mamweb.git``, pokud už máš nahraný SSH klíč na +Giteu. (Obě adresy se dají zkopírovat ze `stránky repozitáře +`_.) To vyrobí složku ``mamweb``, přepni +se do ní (``cd mamweb``) + +O zprovoznění webu se stará skript ``make/install_web``, stačí ho spustit. Ten +vytvoří virtualenv (neexistuje-li) a nainstaluje do něj závislosti webu (podle +souboru ``requirements.txt``). + +.. FIXME: Novowebaři zmínka o requirements.txt tady moc nepomůže, to má být na + stránce o celkové stavbě repozitáře a stacku… + +Následně je potřeba nahrát další data do databáze, což uděláš pomocí příkazů +``./manage.py testdata`` a ``./manage.py loaddata data/*``. Skript +``make/install_web`` to kdyžtak na konci připomene. + +.. caution:: Zatímco skripty v ``make/`` to nepotřebují, pro použití skriptu + ``./manage.py`` (a dalších) se potřebuješ přepnout do virtuálního prostředí. + To uděláš velmi pravděpodobně spuštěním ``source env/bin/activate``, před + začátkem *promptu* by se mělo objevit ``(env)``. Pro opuštění spusť + ``deactivate``. + +Samotný web se spustí třeba pomocí ``make/run``, nebo ekvivalentně +``./manage.py runserver`` a pak je vidět na ``_. + +Časté problémy +^^^^^^ + +- ``make/install_web`` vypíše ``Error: pg_config executable not found.``: + Chybí ``libpq-dev`` +- Chybová hláška obsahuje ``#include ``: chybí ``python3-dev`` +- Na webu není vidět meníčko: spusť ``./manage.py loaddata data/*`` + +S dalšími problémy se zkus obrátit na další webaře, třeba někdo bude vědět :-) + +Příkazy pro ovládání webu +------- + +Příkazy se dělí do několika skupin. Některé souvisí přímo s webem, Djangem, +databází a podobně, ty typicky používají ``./manage.py``. Skripty pro +obhospodařování repozitáře a webu „zvenku“ typicky bydlí ve složce ``make/``. +Ostatní skripty jsou na náhodných místech :-) + +Tady jsou rozebrány jen příkazy relevantní pro lokální web a univerzálně +užitečné, všechny by chtěly být sepsané jinde (ale zatím nejsou :-/) + +Make skripty +^^^^^^^ + +- ``make/install_web`` nainstaluje závislosti webu +- ``make/run`` spustí web (ekvivalentní s ``./manage.py runserver``) +- ``make/schema`` nakreslí schéma vazeb modelů (může se hodit pro referenci a představu) +- ``make/test`` spustí testy (ale moc jich zatím není; ekvivalentní s ``./manage.py test``) +- ``make/sync_prod_flatpages`` stáhne statické stránky z produkčního webu a + uloží je do souboru v gitu, což umožňuje jejich verzování +- ``make/push_compiled_vue_to_test`` nahraje lokálně zkompilované části + frontendu ve Vue na testweb (Gimli má typicky moc starou verzi nodejs, takže + nejde kompilovat tam.) + +Manage.py skripty +^^^^^ + +.. note:: Je potřeba je spouštět ve virtuálním prostředí, viz výše. + +Všechny skripty kdyžtak mají ``--help``, dá se tak zjistit, co všechno umějí. + +- ``./manage.py testdata`` vygeneruje spíše chudá testovací data, aby bylo na + čem testovat web. +- ``./manage.py loaddata `` nahraje data ze souborů do databáze +- ``./manage.py dumpdata `` naopak z databáze vyrobí textovou reprezentaci +- ``./manage.py shell`` spustí interaktivní pythoní shell, ze kterého lze + interagovat s webem / Djangem. +- ``./manage.py dbshell`` spustí databázový shell (typicky používá SQL) + +- ``./manage.py makemigrations`` vyrobí popis migrací, ``./manage.py migrate`` + je spustí, ``showmigrations`` ukáže, které migrace jsou aplikované a které + ne. +- ``./manage.py runserver_plus`` spouští o něco lepší vývojový server (ale + nikdy jsem asi ty lepší featury nepoužil) + +Může se hodit vědět, že spuštění ``./manage.py`` bez parametrů vypíše seznam +všech příkazů, které lze spustit. + +Dokumentace djangových příkazů je v `dokumentaci Djanga +`_ + +Ostatní užitečné příkazy +^^^^^ + +- ``git status`` je univerzální nápověda na aktuální stav repozitáře a co s tím + lze dělat. +- ``git clean -fxd`` smaže všechny soubory, které nejsou uložené v gitu (včetně + ignorovaných). **Nebezpečný příkaz**, zamysli se, než ho spustíš + +Specifika lokálního webu +------- + +Lokální uživatelé +^^^^^^^ + +Přihlašovací údaje jsou psány jako ``login:heslo`` + +- Superuživatel: ``admin:admin`` +- Orgovské účty: ``o:o``, ``o1:o1`` až ``o3:o3`` +- Řešitelské účty: ``r:r``, ``r1:r1`` až ``r3:r3`` + +Všechny tyto účty jsou (?) svázané s nějakými fiktivními osobami, není ale zřejmé se +kterými, budeš to muset vyzkoušet a pak tady zdokumentovat :-) + +E-maily +^^^^^ + +Posílání e-mailů se lokálně dá zkoušet, e-mail se vypíše do terminálu, kde je +web spuštěn (e.g. pomocí ``make/run``). + +Pruhy +^^^^ + +To, že má lokální web po stranách zelené pruhy je normální a správně, slouží to +k vizuálnímu odlišení lokálního webu. + +.. TODO: Mít někde popis všech tří instancí a tady na něj pak odkázat. +.. - Tahák k používání gitových větví: do workflow, ne sem… +.. - Užitečné odkazy – kam se kouknout + (dohledávání views podle URL, settings_*, ) +.. - Zpříjemnění práce (ssh-klíče, tea, --help, …) + diff --git a/make/README.md b/make/README.md index 1d1baa47..f7e873f1 100644 --- a/make/README.md +++ b/make/README.md @@ -1,34 +1,2 @@ Milý člověče, M&Mí web tě vítá. Prosím, neděs se, zkusím tě provést lokálním zprovozněním webu. -Předně: většina příkazů bude asi vypisovat spoustu detailů, takže ti doporučuji -si tento text otevřít někde separátně. Nachází se v repozitáři v -`make/README.md`, případně si jej můžeš zobrazit hezčeji vykreslený na -[Gitee](https://gitea.ks.matfyz.cz/mam/mamweb/src/branch/master/make/README.md). - -O zprovoznění webu se stará hlavně skript `make/install_web`. Ten vytvoří -virtualenv (neexistuje-li) a nainstaluje do něj závislosti webu. Pak ovšem -budeš potřebovat nahrát další data do databáze, což uděláš pomocí příkazů -`./manage.py testdata` a `./manage.py loaddata data/*`. Skript -`make/install_web` ti to kdyžtak připomene. - -Samotný web spustíš třeba pomocí `make/run`, nebo ekvivalentně `./manage.py runserver`. - -Pozor: zatímco skripty v `make/` to nepotřebují, pro použití skriptu -`./manage.py` se potřebuješ přepnout do virtuálního prostředí. To uděláš velmi -pravděpodobně spuštěním `source env/bin/activate`, před začátkem _promptu_ by -se mělo objevit `(env)`. Pro opuštění spusť `deactivate`. - -Časté problémy ------ -Je možné, že nemáš všechny potřebné závislosti v systému. Proto je možné, že -`make/install_web` vyhodí nějakou chybovou hlášku: - -- `Error: pg_config executable not found.`: nainstaluj si `libpq-dev` (na Ubuntu/Debianu, jinde se příslušný balíček může jmenovat jinak) -- Chybová hláška obsahuje `#include `: nainstaluj si `python3-dev` - - - From 2ffd3bf445eec5b86e300b4c1329bdae1106955e Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 20 Nov 2022 15:52:23 +0100 Subject: [PATCH 036/120] =?UTF-8?q?Aspo=C5=88=20trochu=20relevantn=C3=AD?= =?UTF-8?q?=20make/README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A není to zas tak moc README, spíš intro :-/ --- make/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/make/README.md b/make/README.md index f7e873f1..0597a96b 100644 --- a/make/README.md +++ b/make/README.md @@ -1,2 +1,9 @@ -Milý člověče, M&Mí web tě vítá. Prosím, neděs se, zkusím tě provést lokálním zprovozněním webu. +TL;DR: Web vyrobíš pomocí následující posloupnosti příkazů: + make/install_web + . env/bin/activate + ./manage.py testdata + ./manage.py loaddata data/* + make/run +a web potom najdeš na +Pro detaily a nápovědu si prosím přečti dokumentaci v docs/vyvoj.rst. From 25fcddfba8e0296d7eb43cf7d8b722841c0d0709 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 20:18:22 +0100 Subject: [PATCH 037/120] =?UTF-8?q?P=C5=99i=20deployi=20vyrob=20dokumentac?= =?UTF-8?q?i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/deploy | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/make/deploy b/make/deploy index 24ade165..635364b6 100755 --- a/make/deploy +++ b/make/deploy @@ -14,3 +14,10 @@ safe_checkout_branch "$BRANCH" # Teď máme správnou větev, jdeme vše nainstalovat install_everything systemctl --user restart mamweb-test.service + +# Build dokumentace +ensure_venv +( + cd docs + make html +) From e612899dc2bfa9f7484a69b63797c55f4cb46740 Mon Sep 17 00:00:00 2001 From: MaM Web user Date: Mon, 21 Nov 2022 20:05:06 +0100 Subject: [PATCH 038/120] Requirements: myst_parser pro dokumentaci --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 9ad24a7f..d4713d76 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,3 +62,4 @@ lorem sphinx sphinx_rtd_theme +myst_parser From dfc12671197dcb85a8df83db3ce6df26963fc751 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 20:40:43 +0100 Subject: [PATCH 039/120] =?UTF-8?q?deploy:=20pr=C3=A1va=20k=20dokumentaci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/deploy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/make/deploy b/make/deploy index 635364b6..779bbd5c 100755 --- a/make/deploy +++ b/make/deploy @@ -21,3 +21,7 @@ ensure_venv cd docs make html ) +# Oprava práv k dokumentaci +setfacl -m u:www-data:x . docs docs/_build +setfacl -R -m u:www-data:rX docs/_build/html +setfacl -R -m default:u:www-data:rX docs/_build/html From 5c97735948bd727bae692415b7fef8ad53238be7 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 20:54:36 +0100 Subject: [PATCH 040/120] testweb heslo --- make/deploy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/make/deploy b/make/deploy index 779bbd5c..eaccc25b 100755 --- a/make/deploy +++ b/make/deploy @@ -15,6 +15,9 @@ safe_checkout_branch "$BRANCH" install_everything systemctl --user restart mamweb-test.service +# Přihlášení +htpasswd -bc .htpasswd test lisakjelisak + # Build dokumentace ensure_venv ( From df9e473686300f0feee5ffa26cb5fda3005651ae Mon Sep 17 00:00:00 2001 From: MaM Web user Date: Mon, 21 Nov 2022 20:59:14 +0100 Subject: [PATCH 041/120] fixup! testweb heslo --- make/deploy | 1 + 1 file changed, 1 insertion(+) diff --git a/make/deploy b/make/deploy index eaccc25b..b615f73f 100755 --- a/make/deploy +++ b/make/deploy @@ -17,6 +17,7 @@ systemctl --user restart mamweb-test.service # Přihlášení htpasswd -bc .htpasswd test lisakjelisak +setfacl -m u:www-data:r .htpasswd # Build dokumentace ensure_venv From 5985c4940f23f0000c33e17b232ba8978342c07e Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 21:03:42 +0100 Subject: [PATCH 042/120] fix --- make/sync_test_media | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/sync_test_media b/make/sync_test_media index d7991b65..44b0e830 100755 --- a/make/sync_test_media +++ b/make/sync_test_media @@ -4,6 +4,6 @@ set -exuo pipefail . make/lib.sh gimli_only -only_in_dir "$TESTWEB" +only_in_directory "$TESTWEB" rsync -av --delete "$PRODWEB/media/" ./media From 66ce7d209da592422ec9f65decfaec2e59311e79 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 21:11:50 +0100 Subject: [PATCH 043/120] =?UTF-8?q?M=C3=A9n=C4=9B=20hlu=C4=8Dn=C3=A9=20tes?= =?UTF-8?q?ty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/test | 1 + 1 file changed, 1 insertion(+) diff --git a/make/test b/make/test index bdf9f12b..ff5175c9 100755 --- a/make/test +++ b/make/test @@ -5,4 +5,5 @@ set -exuo pipefail ensure_venv +trap - ERR # Testy nejspíš selžou, ale nechceme kolem toho dělat další chybovou hlášku. ./manage.py test -v2 From 6b306f0849bc628bb2ec39f16c31eabdd0b15e12 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 21:57:41 +0100 Subject: [PATCH 044/120] ensure_venv v2 --- make/lib.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/make/lib.sh b/make/lib.sh index 135f859e..195b6647 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -26,6 +26,11 @@ test -e '.git' || die "Make skript spuštěn ve špatné složce, spusť ho z ko function ensure_venv { test -f "$VENV_PATH/bin/activate" || $VENV "$VENV_PATH" . "$VENV_PATH/bin/activate" + # To ale není všechno Horste – ten venv nemusí fungovat, chceme to ověřit a případně spadnout. + local SPRAVNA_CESTA="$(readlink -f "$env/bin/python")" + local SKUTECNA_CESTA="$(readlink -f "$(which python)")" + test "$SPRAVNA_CESTA" != "$SKUTECNA_CESTA" || die "Venv asi nefunguje. Prosím smaž si ho a zkus to znovu." + python -c 'print()' > /dev/null || die "Python ve venvu je rozbitý. Prosím smaž venv a zkus to znovu." } function gimli_only { From d468108188c40be69e71e84d0eeeabf894ce5517 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 21:59:38 +0100 Subject: [PATCH 045/120] ensure_web_installed --- make/lib.sh | 7 +++++++ make/run | 2 +- make/schema | 2 +- make/sync_prod_flatpages | 2 +- make/test | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index 195b6647..a02b94e2 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -33,6 +33,13 @@ function ensure_venv { python -c 'print()' > /dev/null || die "Python ve venvu je rozbitý. Prosím smaž venv a zkus to znovu." } +function ensure_web_installed { + ensure_venv + python -c 'import django; print(django.__version__)' > /dev/null && return + echo >&2 "POZOR: Web nevypadá nainstalovaně, instaluji." + make/install_web +} + function gimli_only { # Rovnou zkontrolujeme i uživatele if test "$HOSTNAME" != gimli -o "$USER" != mam-web diff --git a/make/run b/make/run index c601593a..c3caf58d 100755 --- a/make/run +++ b/make/run @@ -3,6 +3,6 @@ set -exuo pipefail . make/lib.sh -ensure_venv +ensure_web_installed ./manage.py runserver "$@" diff --git a/make/schema b/make/schema index 9688e49a..05e84b61 100755 --- a/make/schema +++ b/make/schema @@ -3,7 +3,7 @@ set -exuo pipefail . make/lib.sh -ensure_venv +ensure_web_installed ./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf ./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf diff --git a/make/sync_prod_flatpages b/make/sync_prod_flatpages index 4d9d8e1f..ca32c95b 100755 --- a/make/sync_prod_flatpages +++ b/make/sync_prod_flatpages @@ -3,7 +3,7 @@ set -exuo pipefail . make/lib.sh -ensure_venv +ensure_web_installed # TODO: This is very ugly, will fix in a future PR (hopefully) ssh "$GIMLI_LOGIN" " diff --git a/make/test b/make/test index ff5175c9..155d03fa 100755 --- a/make/test +++ b/make/test @@ -3,7 +3,7 @@ set -exuo pipefail . make/lib.sh -ensure_venv +ensure_web_installed trap - ERR # Testy nejspíš selžou, ale nechceme kolem toho dělat další chybovou hlášku. ./manage.py test -v2 From c17afece9deea9c21eada821f91f5d299d15f290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 21 Nov 2022 22:09:24 +0100 Subject: [PATCH 046/120] Fix: #1427 --- .../static/odevzdavatko/check_for_detail.js | 22 ++++ .../dynamic_formsets_for_detail.js | 56 ++++++++++ .../templates/odevzdavatko/detail.html | 101 ++++-------------- odevzdavatko/urls.py | 4 +- odevzdavatko/views.py | 63 ++++++----- 5 files changed, 131 insertions(+), 115 deletions(-) create mode 100644 odevzdavatko/static/odevzdavatko/check_for_detail.js create mode 100644 odevzdavatko/static/odevzdavatko/dynamic_formsets_for_detail.js diff --git a/odevzdavatko/static/odevzdavatko/check_for_detail.js b/odevzdavatko/static/odevzdavatko/check_for_detail.js new file mode 100644 index 00000000..3be7e1e9 --- /dev/null +++ b/odevzdavatko/static/odevzdavatko/check_for_detail.js @@ -0,0 +1,22 @@ +// Kontrola, že org neposílá nějakou blbost v detail.html + +function zkontroluj_hodnoceni() { + const pocet = $('.hodnoceni').length; + if (pocet === 1) { // vydím pouze plusko + const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?"); + if (!vysledek) { + event.preventDefault(); + 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; +} diff --git a/odevzdavatko/static/odevzdavatko/dynamic_formsets_for_detail.js b/odevzdavatko/static/odevzdavatko/dynamic_formsets_for_detail.js new file mode 100644 index 00000000..a14c9f8f --- /dev/null +++ b/odevzdavatko/static/odevzdavatko/dynamic_formsets_for_detail.js @@ -0,0 +1,56 @@ +// FIXME: Necopypastovat! Tohle je zkopírované ze static/odevzdavatko/dynamic_formsets.js + + +// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 +function updateElementIndex(el, prefix, ndx) { + var id_regex = new RegExp('(' + prefix + '-\\d+)'); + var replacement = prefix + '-' + ndx; + if ($(el).attr("for")) { + $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); + } + if (el.id) { + el.id = el.id.replace(id_regex, replacement); + } + if (el.name) { + el.name = el.name.replace(id_regex, replacement); + } +} + +// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 +function deleteForm(prefix, btn) { + var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); + if (total >= 1){ + btn.closest('tr').remove(); + var forms = $('.hodnoceni'); + var formCount = forms.length - 1; // There is one extra such form hidden as template! + $('#id_' + prefix + '-TOTAL_FORMS').val(formCount); + for (var i=0; i -// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 -function updateElementIndex(el, prefix, ndx) { - var id_regex = new RegExp('(' + prefix + '-\\d+)'); - var replacement = prefix + '-' + ndx; - if ($(el).attr("for")) { - $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); - } - if (el.id) { - el.id = el.id.replace(id_regex, replacement); - } - if (el.name) { - el.name = el.name.replace(id_regex, replacement); - } -} - -// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 -function deleteForm(prefix, btn) { - var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); - if (total >= 1){ - btn.closest('tr').remove(); - var forms = $('.hodnoceni'); - var formCount = forms.length - 1; // There is one extra such form hidden as template! - $('#id_' + prefix + '-TOTAL_FORMS').val(formCount); - for (var i=0; i + {% if edit %} + + + {% endif %}

Řešené problémy: {{ object.problem.all | join:", " }}

-

Řešitelé: {% for r in object.resitele.all %} {{ r }} ({{ r.osoba.email }}) +

Řešitelé: {% for r in object.resitele.all %} {{ r }} {% if edit %}({{ r.osoba.email }}){% endif %} {% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} @@ -82,13 +30,13 @@ $(document).ready(function(){ {{ priloha.split | last }} {{ priloha.res_poznamka }} {{ priloha.vytvoreno }} - {# TODO: Orgo-poznámka, ideálně jako formulář #} {% endfor %} {% else %}

Žádné přílohy

{% endif %} + {% if edit %}
{# Poznámka #}

Neveřejná poznámka:

@@ -116,7 +64,7 @@ $(document).ready(function(){ - Přidat hodnocení
+ Přidat hodnocení
@@ -129,28 +77,19 @@ $(document).ready(function(){ + {% else %} +

Hodnocení:

+ + +{% for h in hodnoceni %} + + + + + +{% endfor %} +
ProblémBodyZpětná vazba od opravovatele
{{ h.problem }}{{ h.body }}{{ h.feedback }}
+ {% endif %} - {% endblock %} diff --git a/odevzdavatko/urls.py b/odevzdavatko/urls.py index e15b3807..8c53de6b 100644 --- a/odevzdavatko/urls.py +++ b/odevzdavatko/urls.py @@ -26,9 +26,9 @@ urlpatterns = [ path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), path('org/reseni/rocnik//', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), path('org/reseni///', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'), - path('org/reseni/', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), + path('org/reseni/', org_required(viewMethodSwitch(get=views.EditReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), path('org/reseni/all', org_required(views.SeznamReseniView.as_view())), path('org/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), - path('resitel/reseni/', resitel_or_org_required(views.ResitelReseniView.as_view()), name='odevzdavatko_resitel_reseni'), + path('resitel/reseni/', resitel_or_org_required(views.DetailReseniView.as_view()), name='odevzdavatko_resitel_reseni'), ] diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 6c232172..2927838b 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -211,6 +211,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex class DetailReseniView(DetailView): + """ Náhled na řešení. Editace je v :py:class:`EditReseniView`. """ model = m.Reseni template_name = 'odevzdavatko/detail.html' @@ -227,18 +228,43 @@ class DetailReseniView(DetailView): return result def get_context_data(self, **kw): + self.check_access() ctx = super().get_context_data(**kw) - ctx['form'] = f.OhodnoceniReseniFormSet( - initial = self.aktualni_hodnoceni() - ) + hodnoceni = self.aktualni_hodnoceni() + ctx["hodnoceni"] = hodnoceni + return ctx + + def get(self, request, *args, **kwargs): + """ + Oproti :py:class:`django.views.generic.detail.BaseDetailView` + kontroluje přístup pomocí :py:meth:`check_access` + """ + response = super().get(self, request, *args, **kwargs) + self.check_access() + return response + + def check_access(self): + """ Řešitel musí být součástí řešení, jinak se na něj nemá co dívat. """ + if not self.object.resitele.filter(osoba__user=self.request.user).exists(): + raise PermissionDenied() + + +class EditReseniView(DetailReseniView): + """ Editace (hlavně hodnocení) řešení. """ + def get_context_data(self, **kw): + ctx = super().get_context_data(**kw) + ctx['form'] = f.OhodnoceniReseniFormSet(initial=ctx["hodnoceni"]) ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni) + ctx['edit'] = True return ctx + def check_access(self): + """ Na orga máme nároky už v urls.py """ + pass + def hodnoceniReseniView(request, pk, *args, **kwargs): reseni = get_object_or_404(m.Reseni, pk=pk) - template_name = 'odevzdavatko/detail.html' - form_class = f.OhodnoceniReseniFormSet success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) # FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově @@ -270,33 +296,6 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): return redirect(success_url) -class ResitelReseniView(DetailView): - model = m.Reseni - template_name = '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, - "feedback": hodn.feedback, - # "deadline_body": hodn.deadline_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): From dfede45535ce07ff36f99945a18abc57762315a1 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 22:17:52 +0100 Subject: [PATCH 047/120] init_local --- make/init_local | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 make/init_local diff --git a/make/init_local b/make/init_local new file mode 100755 index 00000000..75ee1ccf --- /dev/null +++ b/make/init_local @@ -0,0 +1,10 @@ +#!/bin/bash + +set -exuo pipefail +. make/lib.sh + +make/install_web +ensure_venv +./manage.py testdata +./manage.py loaddata data/* +make/sync_prod_flatpages From 5e26ac5e692047b17effb549b2cf4a585262cbce Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 22:21:34 +0100 Subject: [PATCH 048/120] =?UTF-8?q?fix=20pr=C3=A1v=20v=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/make/lib.sh b/make/lib.sh index a02b94e2..65f19e47 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -96,6 +96,8 @@ function install_everything { make/install ./manage.py migrate ./manage.py collectstatic --noinput + setfacl -R -m default:u:www-data:rX media static + setfacl -R -m u:www-data:rX media static chown -R :mam . || true chmod -R g+rX,go-w . || true } From 9c92ca6575f6afeacc7138f257f24c30dc5d9b9b Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 22:51:02 +0100 Subject: [PATCH 049/120] make/README.md z make_docs --- make/README.md | 43 +++++++++---------------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/make/README.md b/make/README.md index 1d1baa47..0597a96b 100644 --- a/make/README.md +++ b/make/README.md @@ -1,34 +1,9 @@ -Milý člověče, M&Mí web tě vítá. Prosím, neděs se, zkusím tě provést lokálním zprovozněním webu. - -Předně: většina příkazů bude asi vypisovat spoustu detailů, takže ti doporučuji -si tento text otevřít někde separátně. Nachází se v repozitáři v -`make/README.md`, případně si jej můžeš zobrazit hezčeji vykreslený na -[Gitee](https://gitea.ks.matfyz.cz/mam/mamweb/src/branch/master/make/README.md). - -O zprovoznění webu se stará hlavně skript `make/install_web`. Ten vytvoří -virtualenv (neexistuje-li) a nainstaluje do něj závislosti webu. Pak ovšem -budeš potřebovat nahrát další data do databáze, což uděláš pomocí příkazů -`./manage.py testdata` a `./manage.py loaddata data/*`. Skript -`make/install_web` ti to kdyžtak připomene. - -Samotný web spustíš třeba pomocí `make/run`, nebo ekvivalentně `./manage.py runserver`. - -Pozor: zatímco skripty v `make/` to nepotřebují, pro použití skriptu -`./manage.py` se potřebuješ přepnout do virtuálního prostředí. To uděláš velmi -pravděpodobně spuštěním `source env/bin/activate`, před začátkem _promptu_ by -se mělo objevit `(env)`. Pro opuštění spusť `deactivate`. - -Časté problémy ------ -Je možné, že nemáš všechny potřebné závislosti v systému. Proto je možné, že -`make/install_web` vyhodí nějakou chybovou hlášku: - -- `Error: pg_config executable not found.`: nainstaluj si `libpq-dev` (na Ubuntu/Debianu, jinde se příslušný balíček může jmenovat jinak) -- Chybová hláška obsahuje `#include `: nainstaluj si `python3-dev` - - - +TL;DR: Web vyrobíš pomocí následující posloupnosti příkazů: + make/install_web + . env/bin/activate + ./manage.py testdata + ./manage.py loaddata data/* + make/run +a web potom najdeš na + +Pro detaily a nápovědu si prosím přečti dokumentaci v docs/vyvoj.rst. From d186575712d0519c7f399df6ddb642a954591e32 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 22:51:39 +0100 Subject: [PATCH 050/120] =?UTF-8?q?make/README.md:=20V=C3=ADc=20fancy,=20p?= =?UTF-8?q?ou=C5=BE=C3=ADt=20make/init=5Flocal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/make/README.md b/make/README.md index 0597a96b..16b77901 100644 --- a/make/README.md +++ b/make/README.md @@ -1,9 +1,8 @@ +Milý člověče, M&Mí web tě vítá. Prosím, neděs se, zkusím tě provést lokálním zprovozněním webu. +Pro detaily a nápovědu si prosím přečti dokumentaci v docs/vyvoj.rst. + TL;DR: Web vyrobíš pomocí následující posloupnosti příkazů: - make/install_web - . env/bin/activate - ./manage.py testdata - ./manage.py loaddata data/* + make/init_local make/run a web potom najdeš na -Pro detaily a nápovědu si prosím přečti dokumentaci v docs/vyvoj.rst. From 0d17b450112fcababb8cb944dcae2cdbf504d55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 21 Nov 2022 23:27:44 +0100 Subject: [PATCH 051/120] =?UTF-8?q?Fix:=200.5=20=C2=B7=20#1237?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/forms.py | 25 +++++++++++++++++++ .../templates/personalni/udaje/edit.html | 1 + .../templates/personalni/udaje/gdpr.html | 2 +- .../templates/personalni/udaje/prihlaska.html | 1 + personalni/views.py | 2 ++ seminar/migrations/0110_resitel_prezdivka.py | 18 +++++++++++++ seminar/models/personalni.py | 2 ++ 7 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 seminar/migrations/0110_resitel_prezdivka.py diff --git a/personalni/forms.py b/personalni/forms.py index ea200267..5f0d27f6 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -32,6 +32,7 @@ class PrihlaskaForm(PasswordResetForm): help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři') jmeno = forms.CharField(label='Jméno', max_length=256, required=True) + prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) pohlavi_muz = forms.ChoiceField(label='Pohlaví', choices = ((True,'muž'),(False,'žena')), required=True) @@ -105,6 +106,17 @@ class PrihlaskaForm(PasswordResetForm): pass return email + def clean_prezdivka_resitele(self): + prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') + if prezdivka_resitele == '': + return prezdivka_resitele + try: + Resitel.objects.get(prezdivka_resitele=prezdivka_resitele) + raise forms.ValidationError('Přezdívka je již použita') + except ObjectDoesNotExist: + pass + return prezdivka_resitele + def clean_zasilat(self): zasilat = self.cleaned_data.get('zasilat') ulice = self.cleaned_data.get('ulice') @@ -138,6 +150,7 @@ class ProfileEditForm(forms.Form): disabled=True) jmeno = forms.CharField(label='Jméno', max_length=256, required=True) + prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) pohlavi_muz = forms.ChoiceField(label='Pohlaví', choices = ((True,'muž'),(False,'žena')), required=True) @@ -190,6 +203,18 @@ class ProfileEditForm(forms.Form): # pass # return username # + + def clean_prezdivka_resitele(self): + prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') + if prezdivka_resitele == '': + return prezdivka_resitele + try: + Resitel.objects.get(prezdivka_resitele=prezdivka_resitele) + raise forms.ValidationError('Přezdívka je již použita') + except ObjectDoesNotExist: + pass + return prezdivka_resitele + def clean_email(self): err_logger = logging.getLogger('seminar.prihlaska.problem') email = self.cleaned_data.get('email') diff --git a/personalni/templates/personalni/udaje/edit.html b/personalni/templates/personalni/udaje/edit.html index 5ec690d9..9091925d 100644 --- a/personalni/templates/personalni/udaje/edit.html +++ b/personalni/templates/personalni/udaje/edit.html @@ -44,6 +44,7 @@ {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} diff --git a/personalni/templates/personalni/udaje/gdpr.html b/personalni/templates/personalni/udaje/gdpr.html index 3e85de78..5d9af535 100644 --- a/personalni/templates/personalni/udaje/gdpr.html +++ b/personalni/templates/personalni/udaje/gdpr.html @@ -11,7 +11,7 @@ Získáváme od Tebe údaje vyplněné v přihlášce do semináře (jméno, př Slibujeme Ti, že Tvá osobní data nezneužijeme k ničemu, co by nesouviselo s M&M nebo s dalšími aktivitami Matfyzu, a nikdy je nepředáme nikomu cizímu. Údaje využíváme k zajištění chodu semináře a také je sdílíme s ostatními propagačními akcemi Matfyzu, abychom mohli vyhodnocovat úspěšnost akcí. Pokud budeš mít zájem, budeme Ti také posílat zajímavé zprávy a novinky týkajíci se Matfyzu.

-Veřejně vystavujeme pouze výsledkové listiny, které také uchováváme pro archivní účely. Pokud ale z nějakého důvodu nebudeš chtít mít své jméno či školu uvedené ve výsledkové listině, není problém to zařídit, napiš nám. Z tištěných materiálů samozřejmě údaje už odstranit nemůžeme. +Veřejně vystavujeme pouze seznam přezdívek (pro výběr spoluřešitelů k řešení) a výsledkové listiny, které také uchováváme pro archivní účely. Pokud ale z nějakého důvodu nebudeš chtít mít své jméno či školu uvedené ve výsledkové listině, není problém to zařídit, napiš nám. Z tištěných materiálů samozřejmě údaje už odstranit nemůžeme.

Na soustředěních a dalších akcích semináře navíc pořizujeme fotografie a videozáznamy a používáme je ke zpravodajským a propagačním účelům. Pro propagační účely si od Tebe vyžádáme samostatný souhlas na začátku akce. diff --git a/personalni/templates/personalni/udaje/prihlaska.html b/personalni/templates/personalni/udaje/prihlaska.html index 5e6434bf..33adba03 100644 --- a/personalni/templates/personalni/udaje/prihlaska.html +++ b/personalni/templates/personalni/udaje/prihlaska.html @@ -46,6 +46,7 @@

{% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} diff --git a/personalni/views.py b/personalni/views.py index 94b90dea..d7d52540 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -160,6 +160,7 @@ def resitelEditView(request): if resitel_edit: ## Změny v řešiteli + resitel_edit.prezdivka_resitele = fcd['prezdivka_resitele'] resitel_edit.skola = fcd['skola'] resitel_edit.rok_maturity = fcd['rok_maturity'] resitel_edit.zasilat = fcd['zasilat'] @@ -263,6 +264,7 @@ def prihlaskaView(request): err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}') r = s.Resitel( + prezdivka_resitele=fcd['prezdivka_resitele'], rok_maturity = fcd['rok_maturity'], zasilat = fcd['zasilat'], zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] diff --git a/seminar/migrations/0110_resitel_prezdivka.py b/seminar/migrations/0110_resitel_prezdivka.py new file mode 100644 index 00000000..51d25822 --- /dev/null +++ b/seminar/migrations/0110_resitel_prezdivka.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-11-21 22:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0109_hodnoceni_feedback'), + ] + + operations = [ + migrations.AddField( + model_name='resitel', + name='prezdivka_resitele', + field=models.CharField(blank=True, max_length=256, null=True, unique=True, verbose_name='přezdívka řešitele'), + ), + ] diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py index 28deec4d..3e64af45 100644 --- a/seminar/models/personalni.py +++ b/seminar/models/personalni.py @@ -211,6 +211,8 @@ class Resitel(SeminarModelBase): # Interní ID id = models.AutoField(primary_key = True) + prezdivka_resitele = models.CharField('přezdívka řešitele', blank=True, null=True, max_length=256, unique=True) + osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba', on_delete=models.PROTECT) From 41c2ed0dee42df8ceab7b34fe09ddef1d6064b5c Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 23:48:12 +0100 Subject: [PATCH 052/120] =?UTF-8?q?Dokumentace=20make=20skript=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/skripty.rst | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/skripty.rst diff --git a/docs/skripty.rst b/docs/skripty.rst new file mode 100644 index 00000000..ca45394b --- /dev/null +++ b/docs/skripty.rst @@ -0,0 +1,108 @@ +Skripty pro práci s repozitářem +=================== + +Máme dvě hlavní sady skriptů/příkazů na ovládání webu a repozitáře. Skripty pro +práci s webem psané v Pythonu jsou uložené ve složkách +``/management/commands/``, případně vestavěné, a volají se pomocí +``./manage.py ``. Oproti tomu skripty pro práci s repozitářem a pro +úpravy databáze a souborů „zvenčí“ se nejčastěji nacházejí ve složce ``make/`` +a volají se pomocí cesty: ``make/``. + +Občas existují i nějaké další skripty na různých jiných místech. Všechny by +měly být ideálně popsány asi tady. + +Make skripty +---- + +Skripty v ``make/`` se označují jako „Make skripty“. Slouží často k velkým +úkonům s repozitářem, jako je nasazení celého webu, zprovoznění lokálního webu +a podobně. + +.. hint:: Označení pro tyto skripty je dáno tím, že byly původně volány pomocí + make (tj. z Makefile). Ve skutečnosti je lze stále volat i jako ``make + ``, ale pak není možné předávat parametry a obecně je tato cesta + zastaralá a existuje jen pro zpětnou kompatibilitu se svalovou pamětí. + +Tyto skripty jsou samonosné, dají se spustit rovnou a v případě problémů si +budou hlasitě stěžovat. Pro účely debugování různých věcí jsou ale (bohužel?) +hlasité i při normálním spuštění, konkrétně vypisují všechny příkazy, které se +spouštějí (\ ``set -x``). Tyto příkazy jsou vidět za jedním či více plusky (\ ``+``). + +.. tip:: Pokud některý make skript selže, tak by na konci měl vypsat, že se něco nepovedlo. + + +Knihovna ``make/lib.sh`` +^^^^^^ + +Pro pohodlí při psaní velká část z nich využívá knihovnu uloženou +v ``make/lib.sh``. Jsou zde definované užitečné proměnné, kontroly a společný +kód. Kromě toho při inicializaci otestuje, že je skript spuštěn z kořene +repozitáře (takže to pak není potřeba zkoumat v ostatních skriptech). + +Proměnné +""""" + +Popsány jsou jen užitečné proměnné, ve skutečnosti jich je definovaných víc, +ale jsou triviální a samopopisné. + +``VENV_PATH`` : + Cesta virtuálního prostředí. Též lze přepsat. +``REPO`` : + Cesta ke gitovému repozitáři na serveru, rovnou použitelná v ``git clone`` +``GIMLI_LOGIN`` : + Přihlašovací údaje ke Gimlimu +``PRODWEB`` a ``TESTWEB`` : + Cesty ke složkám s produkčním a testovacím webem + +Funkce a další zkratky +"""""" + +``ensure_venv`` : + Zajistí, že se zbytek skriptu spustí ve virtuálním prostředí, a pokud neexistuje, tak jej založí. +``ensure_web_installed`` : + Vyzkouší, že je web (django) aspoň elementárně zprovozněno a pokud ne, tak vyzve uživatele, aby to spravil. +``gimli_only`` : + Otestuje, že je příkaz spuštěn na Gimlim, pokud tomu tak není, zeptá se, jestli si uživatel skutečně přeje zbytek skriptu vykonat +``only_in_directory `` : + Otestuje, že skript běží z konkrétní složky. Zejména použitelné s ``gimli_only`` a ``$TESTWEB`` +``safe_checkout_branch `` : + Bezpečně přepne repozitář na jinou větev. Pokud by mělo dojít k přepsání + knihovny nebo volajícího make skriptu, vyzve uživatele, aby přepnul ručně. +``install_everything`` : + Společná část kódu pro nasazování produkce a testwebu. + +Skripty pro lokální vývoj +^^^^^^^ + +``make/install_web`` (nebo ekvivalentně ``make/install``) : + Vytvoří virtualenv a nainstaluje do něj závislosti webu podle ``requirements.txt``. Následně popíše, jak vyrobit zbytek lokálního webu. +``make/run`` : + Spustí lokální web (ekvivalentní s ``./manage.py runserver``) +``make/schema`` : + Vykreslí závislosti a atributy modelů +``make/sync_prod_flatpages`` : + Stáhne z produkce aktuální statické stránky a uloží je do složky ``data/`` +``make/test`` : + Spustí testy (ekvivalentní s ``./manage.py test -v2``) +``make/init_local`` : + Zkratka za posloupnost ``make/install_web``, ``./manage.py testdata``, ``./manage.py loaddata data/*``, ``make/sync_prod_flatpages`` + +Práce s testwebem +^^^^^^^ + +``make/deploy`` : + Nasadí testweb. Volitelně bere jako parametr jméno větve, kterou má nasadit. + Rovnou nastaví přihlašování a vygeneruje příslušnou verzi dokumentace `sem `_. +``make/push_compiled_vue_to_test`` : + **Neotestováno** Nahraje Vue z lokálního počítače na testweb. (Gimli často má moc starou verzi Node.js, takže nejde zkompilovat tam) +``make/sync_test_db_aggressive`` : + Zkopíruje databázi z produkčního webu. +``make/sync_test_media`` : + Zkopíruje média (obrázky, nahrané soubory) z produkčního webu. +``make/sync_test`` : + Zkratka za ``make/sync_test_db_aggressive`` + ``make/sync_test_media``. + +Nasazení produkce +^^^^ + +``make/deploy_prod``. Před samotným nasazením zálohuje databázi a zkontroluje, že se nasazuje větev ``master``. From fef51e2a755dd3fa4c9f013841c2382e4d0856ee Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 23:52:58 +0100 Subject: [PATCH 053/120] =?UTF-8?q?fixup!=20Dokumentace=20make=20skript?= =?UTF-8?q?=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/skripty.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/skripty.rst b/docs/skripty.rst index ca45394b..aa2d8016 100644 --- a/docs/skripty.rst +++ b/docs/skripty.rst @@ -18,7 +18,7 @@ Skripty v ``make/`` se označují jako „Make skripty“. Slouží často k vel úkonům s repozitářem, jako je nasazení celého webu, zprovoznění lokálního webu a podobně. -.. hint:: Označení pro tyto skripty je dáno tím, že byly původně volány pomocí +.. note:: Označení pro tyto skripty je dáno tím, že byly původně volány pomocí make (tj. z Makefile). Ve skutečnosti je lze stále volat i jako ``make ``, ale pak není možné předávat parametry a obecně je tato cesta zastaralá a existuje jen pro zpětnou kompatibilitu se svalovou pamětí. From 1b34c2dbc443cc9202cbd583a5bb7a09e7dff4b2 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 21 Nov 2022 23:59:19 +0100 Subject: [PATCH 054/120] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20do=20sidebaru?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 10d6016f..92d27c50 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ Vítejte v dokumentaci M&Mího webu! vyvoj sphinx dalsi_soubory + skripty modules/modules zapisy/zapisy From 61bb9786b30d1e26e3558efbd584e00c3bdc3ad4 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 22 Nov 2022 00:00:22 +0100 Subject: [PATCH 055/120] =?UTF-8?q?Zru=C5=A1en=C3=AD=20dvojte=C4=8Dek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/skripty.rst | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/skripty.rst b/docs/skripty.rst index aa2d8016..8f9e1064 100644 --- a/docs/skripty.rst +++ b/docs/skripty.rst @@ -45,61 +45,61 @@ Proměnné Popsány jsou jen užitečné proměnné, ve skutečnosti jich je definovaných víc, ale jsou triviální a samopopisné. -``VENV_PATH`` : +``VENV_PATH`` Cesta virtuálního prostředí. Též lze přepsat. -``REPO`` : +``REPO`` Cesta ke gitovému repozitáři na serveru, rovnou použitelná v ``git clone`` -``GIMLI_LOGIN`` : +``GIMLI_LOGIN`` Přihlašovací údaje ke Gimlimu -``PRODWEB`` a ``TESTWEB`` : +``PRODWEB`` a ``TESTWEB`` Cesty ke složkám s produkčním a testovacím webem Funkce a další zkratky """""" -``ensure_venv`` : +``ensure_venv`` Zajistí, že se zbytek skriptu spustí ve virtuálním prostředí, a pokud neexistuje, tak jej založí. -``ensure_web_installed`` : +``ensure_web_installed`` Vyzkouší, že je web (django) aspoň elementárně zprovozněno a pokud ne, tak vyzve uživatele, aby to spravil. -``gimli_only`` : +``gimli_only`` Otestuje, že je příkaz spuštěn na Gimlim, pokud tomu tak není, zeptá se, jestli si uživatel skutečně přeje zbytek skriptu vykonat -``only_in_directory `` : +``only_in_directory `` Otestuje, že skript běží z konkrétní složky. Zejména použitelné s ``gimli_only`` a ``$TESTWEB`` -``safe_checkout_branch `` : +``safe_checkout_branch `` Bezpečně přepne repozitář na jinou větev. Pokud by mělo dojít k přepsání knihovny nebo volajícího make skriptu, vyzve uživatele, aby přepnul ručně. -``install_everything`` : +``install_everything`` Společná část kódu pro nasazování produkce a testwebu. Skripty pro lokální vývoj ^^^^^^^ -``make/install_web`` (nebo ekvivalentně ``make/install``) : +``make/install_web`` (nebo ekvivalentně ``make/install``) Vytvoří virtualenv a nainstaluje do něj závislosti webu podle ``requirements.txt``. Následně popíše, jak vyrobit zbytek lokálního webu. -``make/run`` : +``make/run`` Spustí lokální web (ekvivalentní s ``./manage.py runserver``) -``make/schema`` : +``make/schema`` Vykreslí závislosti a atributy modelů -``make/sync_prod_flatpages`` : +``make/sync_prod_flatpages`` Stáhne z produkce aktuální statické stránky a uloží je do složky ``data/`` -``make/test`` : +``make/test`` Spustí testy (ekvivalentní s ``./manage.py test -v2``) -``make/init_local`` : +``make/init_local`` Zkratka za posloupnost ``make/install_web``, ``./manage.py testdata``, ``./manage.py loaddata data/*``, ``make/sync_prod_flatpages`` Práce s testwebem ^^^^^^^ -``make/deploy`` : +``make/deploy`` Nasadí testweb. Volitelně bere jako parametr jméno větve, kterou má nasadit. Rovnou nastaví přihlašování a vygeneruje příslušnou verzi dokumentace `sem `_. -``make/push_compiled_vue_to_test`` : +``make/push_compiled_vue_to_test`` **Neotestováno** Nahraje Vue z lokálního počítače na testweb. (Gimli často má moc starou verzi Node.js, takže nejde zkompilovat tam) -``make/sync_test_db_aggressive`` : +``make/sync_test_db_aggressive`` Zkopíruje databázi z produkčního webu. -``make/sync_test_media`` : +``make/sync_test_media`` Zkopíruje média (obrázky, nahrané soubory) z produkčního webu. -``make/sync_test`` : +``make/sync_test`` Zkratka za ``make/sync_test_db_aggressive`` + ``make/sync_test_media``. Nasazení produkce From fc6f29bbf15abd4a4da7037d39e7d889dcbc26f8 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 22 Nov 2022 00:05:56 +0100 Subject: [PATCH 056/120] Fix hesel --- docs/vyvoj.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index 3bfdace8..c2eb3d1f 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -155,8 +155,8 @@ Lokální uživatelé Přihlašovací údaje jsou psány jako ``login:heslo`` - Superuživatel: ``admin:admin`` -- Orgovské účty: ``o:o``, ``o1:o1`` až ``o3:o3`` -- Řešitelské účty: ``r:r``, ``r1:r1`` až ``r3:r3`` +- Orgovské účty: ``o:o``, ``o1:o`` až ``o3:o`` +- Řešitelské účty: ``r:r``, ``r1:r`` až ``r3:r`` Všechny tyto účty jsou (?) svázané s nějakými fiktivními osobami, není ale zřejmé se kterými, budeš to muset vyzkoušet a pak tady zdokumentovat :-) From 4549a91e062a9886ed6d952ee1d31a33384d46ed Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 22 Nov 2022 00:11:16 +0100 Subject: [PATCH 057/120] =?UTF-8?q?Dokumentace=20Jid=C3=A1=C5=A1ova=20bugu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Není to častý bug, ale nějaký obsah přece potřebujeme :-) --- docs/vyvoj.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index c2eb3d1f..08d44e38 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -84,6 +84,9 @@ Samotný web se spustí třeba pomocí ``make/run``, nebo ekvivalentně Chybí ``libpq-dev`` - Chybová hláška obsahuje ``#include ``: chybí ``python3-dev`` - Na webu není vidět meníčko: spusť ``./manage.py loaddata data/*`` +- ``locale.Error: unsupported locale setting``: Chybí podpora pro příslušný + jazyk ve tvém systému. Odkomentuj příslušnou lokalizaci v ``/etc/locale.gen`` + a spusť ``locale-gen`` jako root, tím se to spraví. S dalšími problémy se zkus obrátit na další webaře, třeba někdo bude vědět :-) From 4853a47899944039bfe28a9eae2ec97fdf838c2c Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 22 Nov 2022 00:11:41 +0100 Subject: [PATCH 058/120] =?UTF-8?q?vyvoj.rst:=20Odkaz=20na=20dokumentaci?= =?UTF-8?q?=20make=20skript=C5=AF=20+=20smaz=C3=A1n=C3=AD=20irelevantn?= =?UTF-8?q?=C3=ADho=20p=C5=99=C3=ADkazu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vyvoj.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index 08d44e38..625245a1 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -99,7 +99,7 @@ obhospodařování repozitáře a webu „zvenku“ typicky bydlí ve složce `` Ostatní skripty jsou na náhodných místech :-) Tady jsou rozebrány jen příkazy relevantní pro lokální web a univerzálně -užitečné, všechny by chtěly být sepsané jinde (ale zatím nejsou :-/) +užitečné, ostatní najdeš v :ref:`Skripty pro práci s repozitářem` Make skripty ^^^^^^^ @@ -110,9 +110,6 @@ Make skripty - ``make/test`` spustí testy (ale moc jich zatím není; ekvivalentní s ``./manage.py test``) - ``make/sync_prod_flatpages`` stáhne statické stránky z produkčního webu a uloží je do souboru v gitu, což umožňuje jejich verzování -- ``make/push_compiled_vue_to_test`` nahraje lokálně zkompilované části - frontendu ve Vue na testweb (Gimli má typicky moc starou verzi nodejs, takže - nejde kompilovat tam.) Manage.py skripty ^^^^^ From 062653e70856998af7231a3eff17f35d99b685d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 22 Nov 2022 00:15:20 +0100 Subject: [PATCH 059/120] =?UTF-8?q?Fix:=200.5=20=C2=B7=20#1237?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/urls.py | 1 + api/views/autocomplete.py | 20 +++++++++++++++++++ odevzdavatko/forms.py | 9 ++++++++- .../templates/odevzdavatko/nahraj_reseni.html | 2 ++ odevzdavatko/views.py | 1 + 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/api/urls.py b/api/urls.py index 23aafe36..76d82b25 100644 --- a/api/urls.py +++ b/api/urls.py @@ -22,6 +22,7 @@ urlpatterns = [ # Autocomplete path('api/autocomplete/skola/', views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), path('api/autocomplete/resitel/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'), + path('api/autocomplete/resitel_public/', views.PublicResitelAutocomplete.as_view(), name='autocomplete_resitel_public'), path('api/autocomplete/problem/odevzdatelny', views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), # Ceka na autocomplete v3 diff --git a/api/views/autocomplete.py b/api/views/autocomplete.py index 217df008..c706b126 100644 --- a/api/views/autocomplete.py +++ b/api/views/autocomplete.py @@ -44,6 +44,26 @@ class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetVie qs = qs.filter(query) return qs + +class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2QuerySetView): + """ + View k :mod:`dal.autocomplete` pro vyhledávání řešitelů podle přezdívky + především v odevzdávátku. + """ + def get_queryset(self): + qs = m.Resitel.objects.filter( + prezdivka_resitele__isnull=False + ).exclude( + prezdivka_resitele="" + ).filter( + prezdivka_resitele__startswith=self.q + ).all() + return qs + + def get_result_label(self, result): + return result.prezdivka_resitele + + class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ def get_queryset(self): diff --git a/odevzdavatko/forms.py b/odevzdavatko/forms.py index a8538668..b52c30f4 100644 --- a/odevzdavatko/forms.py +++ b/odevzdavatko/forms.py @@ -63,7 +63,7 @@ class PosliReseniForm(forms.Form): class NahrajReseniForm(forms.ModelForm): class Meta: model = m.Reseni - fields = ('problem',) + fields = ('problem', 'resitele') help_texts = {'problem':''} # Nezobrazovat help text ve formuláři widgets = {'problem': @@ -72,6 +72,13 @@ class NahrajReseniForm(forms.ModelForm): attrs = {'data-placeholder--id': '-1', 'data-placeholder--text' : '---', 'data-allow-clear': 'true'}, + ), + 'resitele': + autocomplete.ModelSelect2Multiple( + url='autocomplete_resitel_public', + attrs = {'data-placeholder--id': '-1', + 'data-placeholder--text' : '---', + 'data-allow-clear': 'true'}, ) } diff --git a/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html b/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html index 64ef92c1..07529720 100644 --- a/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html +++ b/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html @@ -13,6 +13,8 @@

Když řešení různých témátek vložíš každé zvlášť, lépe se v nich vyznáme a třeba ti je i rychleji opravíme.

+

Pokud řešíte ve více lidech, je nutné přidat tyto lidi jako „Autory řešení“! V tomto poli se vyhledává podle přezdívek, které si lze nastavit v „Osobní údaje“. Sebe vyplňovat nemusíte a za skupinu odevzdávejte pouze jednou (ne každý sám)!

+
{% csrf_token %}
diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 2927838b..3b29d397 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -407,6 +407,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): with transaction.atomic(): self.object = form.save() self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user)) + self.object.resitele.add(*form.cleaned_data["resitele"]) self.object.cas_doruceni = timezone.now() self.object.forma = m.Reseni.FORMA_UPLOAD self.object.save() From 1bfe8cee5ee88e190719416f080d22fa0b067f08 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 22 Nov 2022 00:22:01 +0100 Subject: [PATCH 060/120] =?UTF-8?q?te=C4=8Dka?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vyvoj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index 625245a1..cedd7a67 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -99,7 +99,7 @@ obhospodařování repozitáře a webu „zvenku“ typicky bydlí ve složce `` Ostatní skripty jsou na náhodných místech :-) Tady jsou rozebrány jen příkazy relevantní pro lokální web a univerzálně -užitečné, ostatní najdeš v :ref:`Skripty pro práci s repozitářem` +užitečné, ostatní najdeš v :ref:`Skripty pro práci s repozitářem`. Make skripty ^^^^^^^ From ad45b0a23b5fca93794d024cc3bc9cfb7d6184ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 22 Nov 2022 00:24:57 +0100 Subject: [PATCH 061/120] =?UTF-8?q?Fix:=20=C4=8D=C3=A1rky=20v=20detailu=20?= =?UTF-8?q?=C5=99e=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 7448f71e..2e4ad53a 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -12,8 +12,7 @@

Řešené problémy: {{ object.problem.all | join:", " }}

-

Řešitelé: {% for r in object.resitele.all %} {{ r }} {% if edit %}({{ r.osoba.email }}){% endif %} -{% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

+

Řešitelé: {% for r in object.resitele.all %}{{ r }}{% if edit %}({{ r.osoba.email }}){% endif %}{% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}

Forma: {{ object.get_forma_display }}

From e325d41f76e07f8bb1024a0b9074007e22dd9c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 22 Nov 2022 00:26:35 +0100 Subject: [PATCH 062/120] =?UTF-8?q?Fix:=20zapomn=C4=9Bl=20jsem=20je=C5=A1t?= =?UTF-8?q?=C4=9B=20po=20sjednocen=C3=AD=20odstranit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../odevzdavatko/detail_resitele.html | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 odevzdavatko/templates/odevzdavatko/detail_resitele.html diff --git a/odevzdavatko/templates/odevzdavatko/detail_resitele.html b/odevzdavatko/templates/odevzdavatko/detail_resitele.html deleted file mode 100644 index fb0cb5fb..00000000 --- a/odevzdavatko/templates/odevzdavatko/detail_resitele.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} -{% load static %} -{% load deadliny %} - -{% block content %} - -

Řešené problémy: {{ object.problem.all | join:", " }}

- -

Řešitelé: {% for r in object.resitele.all %} {{ r }} - {% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

- -{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} -

Forma: {{ object.get_forma_display }}

- -

Doručeno {{ object.cas_doruceni }}, deadline: {{object.deadline_reseni | deadline_html }}

- -{# Soubory: #} -

Přílohy:

-{% if object.prilohy.all %} -
- -{% for priloha in object.prilohy.all %} - - - - - {# TODO: Orgo-poznámka, ideálně jako formulář #} -{% endfor %} -
SouborŘešitelova poznámkaDatum
{{ priloha.split | last }}{{ priloha.res_poznamka }}{{ priloha.vytvoreno }}
-{% else %} -

Žádné přílohy

-{% endif %} - -{#

Poznámka:

#} -{#

{{ poznamka }}

#} - -{# Hodnocení: #} -

Hodnocení:

- -{# #} -{% for h in hodnoceni %} - - - - -{# #} - -{% endfor %} -
ProblémBodyZpětná vazba od opravovateleDeadline pro body
{{ h.problem }}{{ h.body }}{{ h.feedback }}{{ h.deadline_body }}
- -{% endblock %} From d8554ad70dad95da64f1df984afa800e011397b9 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 24 Nov 2022 02:05:09 +0100 Subject: [PATCH 063/120] =?UTF-8?q?Ubuntu=20m=C3=A1=20i=20venv=20v=20separ?= =?UTF-8?q?=C3=A1tn=C3=ADm=20bal=C3=AD=C4=8Dku?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vyvoj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index cedd7a67..583521b1 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -25,7 +25,7 @@ Nutné - `Python `_ - Ideálně ve verzi 3.9 (to je to, co je aktuálně (2022) na Gimlim) - - Včetně pip-u (na Ubuntu balíček ``python3-pip``) a knihoven pro kompilaci + - Včetně pip-u (na Ubuntu balíček ``python3-pip``), venvu (``python3-venv``) a knihoven pro kompilaci Cčkových rozšíření (``python3-dev``) - Knihovna pro práci s PostgreSQL (``libpq-dev``) - Webový prohlížeč From fcd1320b45781e488f54d23a4f26f718d2e276a5 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 24 Nov 2022 02:23:03 +0100 Subject: [PATCH 064/120] Fix cesty --- make/lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/lib.sh b/make/lib.sh index 65f19e47..fad464a5 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -27,7 +27,7 @@ function ensure_venv { test -f "$VENV_PATH/bin/activate" || $VENV "$VENV_PATH" . "$VENV_PATH/bin/activate" # To ale není všechno Horste – ten venv nemusí fungovat, chceme to ověřit a případně spadnout. - local SPRAVNA_CESTA="$(readlink -f "$env/bin/python")" + local SPRAVNA_CESTA="$(readlink -f "$VENV_PATH/bin/python")" local SKUTECNA_CESTA="$(readlink -f "$(which python)")" test "$SPRAVNA_CESTA" != "$SKUTECNA_CESTA" || die "Venv asi nefunguje. Prosím smaž si ho a zkus to znovu." python -c 'print()' > /dev/null || die "Python ve venvu je rozbitý. Prosím smaž venv a zkus to znovu." From 9fefbd4bbac1d680231ad2fa87279168fa2f469e Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 24 Nov 2022 02:50:46 +0100 Subject: [PATCH 065/120] Fix logiky --- make/lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/lib.sh b/make/lib.sh index fad464a5..9dc3d6c5 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -29,7 +29,7 @@ function ensure_venv { # To ale není všechno Horste – ten venv nemusí fungovat, chceme to ověřit a případně spadnout. local SPRAVNA_CESTA="$(readlink -f "$VENV_PATH/bin/python")" local SKUTECNA_CESTA="$(readlink -f "$(which python)")" - test "$SPRAVNA_CESTA" != "$SKUTECNA_CESTA" || die "Venv asi nefunguje. Prosím smaž si ho a zkus to znovu." + test "$SPRAVNA_CESTA" == "$SKUTECNA_CESTA" || die "Venv asi nefunguje. Prosím smaž si ho a zkus to znovu." python -c 'print()' > /dev/null || die "Python ve venvu je rozbitý. Prosím smaž venv a zkus to znovu." } From 430430b4606b42fef9b695af4af092e09b2d9cd7 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 24 Nov 2022 04:56:52 +0100 Subject: [PATCH 066/120] =?UTF-8?q?Tabulka=20prerekvizit=20v=20r=C5=AFzn?= =?UTF-8?q?=C3=BDch=20distribuc=C3=ADch=20/=20syst=C3=A9mech?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tabulka_prerekvizit.rst | 25 +++++++++++++++++++++++++ docs/vyvoj.rst | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 docs/tabulka_prerekvizit.rst diff --git a/docs/tabulka_prerekvizit.rst b/docs/tabulka_prerekvizit.rst new file mode 100644 index 00000000..9dcce4c5 --- /dev/null +++ b/docs/tabulka_prerekvizit.rst @@ -0,0 +1,25 @@ +.. Není odkázaná z menu, je to záměr + +Tabulka prerekvizit v různých distribucích +========= + +.. admonition:: Metodika + + Na čistém repozitáři (``git clean -fxd``) a čistém systému spouštíme + ``make/init_local``. Když to spadne, tak do tabulky zapíšeme, co jsme + přiinstalovali. Protože větev ``makefiles`` aktuálně není mergenutá do + masteru, nefunguje synchronizace flatpages (a stejně nemáme SSH klíč), takže + tam ``make/init_local`` sestřelíme a vyzkoušíme, že ``make/test`` spustí + testy. + +.. Grafické tabulky (grid-tables, simple-tables) jsou strašný porod vyrábět, dlabu na to a cpu to do CSV… + +.. csv-table:: Prerekvizity v jednotlivých distribucích + :header: Distribuce / OS, Repozitář s Py3.9, venv, py knihovny, PostgreSQL knihovna, poznámky + + Ubuntu 22.10, ??, ``python3-venv``, ``python3-dev``, ``libpq-dev``, "Je potřeba zapnout zdroj ``universe`` a nainstalovat kompilátor C (``gcc``)?" + Linux Mint 21, ??, ``python3-venv``, ``python3-dev``, ``libpq-dev``, "" + Archlinux 2022.11.01, AUR, vestavěný, vestavěné, ``postgresql-libs``, "Je potřeba céčkový kompilátor (``gcc``)" + openSUSE Leap 15.4, oficiální (``python39``), předinstalovaný?, ``python39-devel``, ??FIXME!!, "Výchozí verze pythonu je 3.6 a ta je moc stará, potřeba instalovat ``gcc``. Nevím jak sehnat pg_config." + Debian 11, "oficiální, výchozí", ??, ??, ??, "Určitě to tam rozběhat jde, protože Gimli. Nejspíš bude relativně podobné Ubuntu." + diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index 583521b1..256f5eec 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -36,6 +36,9 @@ Nutné Kromě toho je potřeba mít účet na `Gitee `_, kde bydlí gitový repozitář s kódem. +.. tip:: Potřebné balíčky v různých distribucích jsou sepsané v :ref:`tabulce + prerekvizit `. + Doporučené ^^^^^^^^^^ From f86e6b68383697ba8d535691daf5fb47807283a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 28 Nov 2022 23:38:13 +0100 Subject: [PATCH 067/120] hotfix: #1430 --- mamweb/static/css/mamweb.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 53e06967..236072f5 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -1152,8 +1152,7 @@ div.zadani_termin .datum { /* posune kotvu obrázku v galerii o oranžový pruh dolu, aby se pod ním obrázek neschovával */ /* https://stackoverflow.com/questions/10732690/offsetting-an-html-anchor-to-adjust-for-fixed-header */ .kotva_obrazku { - display: block; - position: relative; + position: absolute; width: 0; height: 55px; /* viz #title */ margin-top: -55px; /* viz #title */ From 91cf18fa9e0e04a8f34b43f226344ad64a01c998 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 29 Nov 2022 00:40:18 +0100 Subject: [PATCH 068/120] =?UTF-8?q?Odsazen=C3=AD=20pomoc=C3=AD=20tabul?= =?UTF-8?q?=C3=A1tor=C5=AF=20[NOT=20CHECKED]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pustil jsem na velkou část kódu skript, zatím nevím, co jsem rozbil. Prosím o review :-) --- header_fotky/context_processors.py | 84 +++---- korektury/views.py | 34 +-- mamweb/admin.py | 32 +-- mamweb/middleware.py | 152 ++++++------ mamweb/settings_common.py | 386 ++++++++++++++--------------- mamweb/settings_local.py | 96 +++---- mamweb/settings_prod.py | 20 +- mamweb/settings_test.py | 28 +-- seminar/models/base.py | 18 +- seminar/models/novinky.py | 42 ++-- seminar/models/odevzdavatko.py | 234 ++++++++--------- soustredeni/admin.py | 50 ++-- treenode/admin.py | 90 +++---- treenode/permissions.py | 4 +- various/autentizace/utils.py | 24 +- various/log_filters.py | 8 +- 16 files changed, 651 insertions(+), 651 deletions(-) diff --git a/header_fotky/context_processors.py b/header_fotky/context_processors.py index 0040cb5f..7f73faa7 100644 --- a/header_fotky/context_processors.py +++ b/header_fotky/context_processors.py @@ -12,46 +12,46 @@ from header_fotky.models import FotkaUrlVazba def vzhled(request): - """ - Podle času přidá do contextu, zdali je nebo není noc. Dále podle dení - doby a url přidá do contextu správnou fotku. - - url adresu nejprve vyzkouší celou, pak postupně odřezává věci za - lomítkem, dokud nenajde url, pro kterou existuje alespoň jedna fotka. - Z fotek pro toto url zkusí vybrat tu ve správné denní době a až poté - libovolnou. (Z více možných fotek pro 1 url a 1 dobu vybírá náhodně.) - """ - hodin = datetime.now().hour - if (hodin <= 6) or (hodin >= 20): - noc = True - nedoba = 'den' - doba = 'noc' - else: - noc = False - nedoba = 'noc' - doba = 'den' - url = request.path - - fotky = FotkaUrlVazba.objects.exclude(denni_doba=nedoba) - fotka = None - - # TODO rychlejší patternmatch? - while (fotka is None) and (url != ''): - presne = fotky.filter(url__exact=url) - if presne.count() > 0: - presne_doba = presne.filter(denni_doba=doba) - if presne_doba.count() > 0: - fotka = random.choice(presne_doba).url_fotky() - else: - fotka = random.choice(presne).url_fotky() - - url = url[:-1] - index = url.rfind('/') - if index != -1: - url = url[:index+1] - - if fotka is None: - fotka = settings.STATIC_URL + "images/header/vikendovka.jpg" - - return {'noc': noc, 'fotka': fotka} + """ + Podle času přidá do contextu, zdali je nebo není noc. Dále podle dení + doby a url přidá do contextu správnou fotku. + + url adresu nejprve vyzkouší celou, pak postupně odřezává věci za + lomítkem, dokud nenajde url, pro kterou existuje alespoň jedna fotka. + Z fotek pro toto url zkusí vybrat tu ve správné denní době a až poté + libovolnou. (Z více možných fotek pro 1 url a 1 dobu vybírá náhodně.) + """ + hodin = datetime.now().hour + if (hodin <= 6) or (hodin >= 20): + noc = True + nedoba = 'den' + doba = 'noc' + else: + noc = False + nedoba = 'noc' + doba = 'den' + url = request.path + + fotky = FotkaUrlVazba.objects.exclude(denni_doba=nedoba) + fotka = None + + # TODO rychlejší patternmatch? + while (fotka is None) and (url != ''): + presne = fotky.filter(url__exact=url) + if presne.count() > 0: + presne_doba = presne.filter(denni_doba=doba) + if presne_doba.count() > 0: + fotka = random.choice(presne_doba).url_fotky() + else: + fotka = random.choice(presne).url_fotky() + + url = url[:-1] + index = url.rfind('/') + if index != -1: + url = url[:index+1] + + if fotka is None: + fotka = settings.STATIC_URL + "images/header/vikendovka.jpg" + + return {'noc': noc, 'fotka': fotka} diff --git a/korektury/views.py b/korektury/views.py index d8d78c24..efeab19d 100644 --- a/korektury/views.py +++ b/korektury/views.py @@ -30,28 +30,28 @@ class KorekturyListView(generic.ListView): template_name = 'korektury/seznam.html' class KorekturyAktualniListView(KorekturyListView): - def get_queryset(self, *args, **kwargs): - queryset=super().get_queryset() - queryset=queryset.exclude(status="zastarale") - return queryset + def get_queryset(self, *args, **kwargs): + queryset=super().get_queryset() + queryset=queryset.exclude(status="zastarale") + return queryset - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['selected'] = 'aktualni' - return context + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['selected'] = 'aktualni' + return context class KorekturyZastaraleListView(KorekturyListView): - def get_queryset(self, *args, **kwargs): - queryset=super().get_queryset() - queryset=queryset.filter(status="zastarale").order_by("-cas") - return queryset - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['selected'] = 'zastarale' - return context + def get_queryset(self, *args, **kwargs): + queryset=super().get_queryset() + queryset=queryset.filter(status="zastarale").order_by("-cas") + return queryset + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['selected'] = 'zastarale' + return context class KorekturySeskupeneListView(KorekturyAktualniListView): template_name = 'korektury/seskupeny_seznam.html' diff --git a/mamweb/admin.py b/mamweb/admin.py index 2ad1aaaa..b6924468 100644 --- a/mamweb/admin.py +++ b/mamweb/admin.py @@ -17,14 +17,14 @@ from ckeditor_uploader.widgets import CKEditorUploadingWidget class FlatpageForm(FlatpageFormOld): - content = forms.CharField(widget=CKEditorUploadingWidget()) - class Meta: - model = FlatPage # this is not automatically inherited from FlatpageFormOld - exclude = [] + content = forms.CharField(widget=CKEditorUploadingWidget()) + class Meta: + model = FlatPage # this is not automatically inherited from FlatpageFormOld + exclude = [] class FlatPageAdmin(FlatPageAdminOld): - form = FlatpageForm + form = FlatpageForm # We have to unregister the normal admin, and then reregister ours @@ -36,19 +36,19 @@ 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. - """ + """ + 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())) + 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())) + # 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 + return app_list AdminSite.get_app_list = get_app_list diff --git a/mamweb/middleware.py b/mamweb/middleware.py index c1014257..7109423e 100644 --- a/mamweb/middleware.py +++ b/mamweb/middleware.py @@ -6,83 +6,83 @@ from django.http import HttpResponse, HttpResponseRedirect class LoggedInHintCookieMiddleware(object): - """Middleware to securely help with 'logged-in' detection for dual HTTP/HTTPS sites. - - On insecure requests: Checks for a (non-secure) cookie settings.LOGGED_IN_HINT_COOKIE_NAME - and if present, redirects to HTTPS (same adress). - Note this usually breaks non-GET (POST) requests. - - On secure requests: Updates cookie settings.LOGGED_IN_HINT_COOKIE_NAME to reflect - whether an user is logged in in the current session (cookie set to 'True' or cleared). - The cookie is set to expire at the same time as the sessionid cookie. - - By default, LOGGED_IN_HINT_COOKIE_NAME = 'logged_in_hint'. - """ - - def __init__(self): - if hasattr(settings, 'LOGGED_IN_HINT_COOKIE_NAME'): - self.cookie_name = settings.LOGGED_IN_HINT_COOKIE_NAME - else: self.cookie_name = 'logged_in_hint' - self.cookie_value = 'True' - - def cookie_correct(self, request): - return self.cookie_name in request.COOKIES and request.COOKIES[self.cookie_name] == self.cookie_value - - def process_request(self, request): - if not request.is_secure(): - if self.cookie_correct(request): - # redirect insecure (assuming http) requests with hint cookie to https - url = request.build_absolute_uri() - assert url[:5] == 'http:' - return HttpResponseRedirect('https:' + url[5:]) - return None - - def process_response(self, request, response): - if request.is_secure(): - # assuming full session info (as the conn. is secure) - try: - user = request.user - except AttributeError: # no user - ajax or other special request - return response - if user.is_authenticated(): - if not self.cookie_correct(request): - expiry = None if request.session.get_expire_at_browser_close() else request.session.get_expiry_date() - response.set_cookie(self.cookie_name, value=self.cookie_value, expires=expiry, secure=False) - else: - if self.cookie_name in request.COOKIES: - response.delete_cookie(self.cookie_name) - return response + """Middleware to securely help with 'logged-in' detection for dual HTTP/HTTPS sites. + + On insecure requests: Checks for a (non-secure) cookie settings.LOGGED_IN_HINT_COOKIE_NAME + and if present, redirects to HTTPS (same adress). + Note this usually breaks non-GET (POST) requests. + + On secure requests: Updates cookie settings.LOGGED_IN_HINT_COOKIE_NAME to reflect + whether an user is logged in in the current session (cookie set to 'True' or cleared). + The cookie is set to expire at the same time as the sessionid cookie. + + By default, LOGGED_IN_HINT_COOKIE_NAME = 'logged_in_hint'. + """ + + def __init__(self): + if hasattr(settings, 'LOGGED_IN_HINT_COOKIE_NAME'): + self.cookie_name = settings.LOGGED_IN_HINT_COOKIE_NAME + else: self.cookie_name = 'logged_in_hint' + self.cookie_value = 'True' + + def cookie_correct(self, request): + return self.cookie_name in request.COOKIES and request.COOKIES[self.cookie_name] == self.cookie_value + + def process_request(self, request): + if not request.is_secure(): + if self.cookie_correct(request): + # redirect insecure (assuming http) requests with hint cookie to https + url = request.build_absolute_uri() + assert url[:5] == 'http:' + return HttpResponseRedirect('https:' + url[5:]) + return None + + def process_response(self, request, response): + if request.is_secure(): + # assuming full session info (as the conn. is secure) + try: + user = request.user + except AttributeError: # no user - ajax or other special request + return response + if user.is_authenticated(): + if not self.cookie_correct(request): + expiry = None if request.session.get_expire_at_browser_close() else request.session.get_expiry_date() + response.set_cookie(self.cookie_name, value=self.cookie_value, expires=expiry, secure=False) + else: + if self.cookie_name in request.COOKIES: + response.delete_cookie(self.cookie_name) + return response class vzhled: - def process_request(self, request): - return None - - def process_view(self, request, view_func, view_args, view_kwargs): - #print "====== process_request ======" - #print view_func - #print view_args - #print view_kwargs - #print "=============================" - return None - - def process_template_response(self, request, response): - hodin = datetime.now().hour - if (hodin <= 6) or (hodin >= 14): # TODO 20 - response.context_data['noc'] = True - else: - response.context_data['noc'] = False - return response - - def process_response(self, request, response): - #hodin = datetime.now().hour - #if (hodin <= 6) or (hodin >= 14): # TODO 20 - #response.context_data['noc'] = True - #else: - #response.context_data['noc'] = False - return response - - - ##def process_exception(request, exception): - #pass + def process_request(self, request): + return None + + def process_view(self, request, view_func, view_args, view_kwargs): + #print "====== process_request ======" + #print view_func + #print view_args + #print view_kwargs + #print "=============================" + return None + + def process_template_response(self, request, response): + hodin = datetime.now().hour + if (hodin <= 6) or (hodin >= 14): # TODO 20 + response.context_data['noc'] = True + else: + response.context_data['noc'] = False + return response + + def process_response(self, request, response): + #hodin = datetime.now().hour + #if (hodin <= 6) or (hodin >= 14): # TODO 20 + #response.context_data['noc'] = True + #else: + #response.context_data['noc'] = False + return response + + + ##def process_exception(request, exception): + #pass diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 5a610795..139190fa 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -40,8 +40,8 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media') STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + 'django.contrib.staticfiles.finders.FileSystemFinder', ) # Where redirect for login required services @@ -57,41 +57,41 @@ DOBA_ODHLASENI_PRI_ZASKRTNUTI_NEODHLASOVAT = 365 * 24 * 3600 # rok # Modules configuration AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', + 'django.contrib.auth.backends.ModelBackend', ) MIDDLEWARE = ( # 'reversion.middleware.RevisionMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', # FIXME: rozbilo se při přechodu na Django 2.0, nevím, jestli # se to dá zahodit bez náhrady # 'mamweb.middleware.LoggedInHintCookieMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', ) TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': ( - 'django.contrib.auth.context_processors.auth', + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': ( + 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.request', - 'django.contrib.messages.context_processors.messages', - 'sekizai.context_processors.sekizai', - 'header_fotky.context_processors.vzhled', - 'various.context_processors.rozliseni', - 'various.context_processors.april', - ) - }, - }, + 'django.contrib.messages.context_processors.messages', + 'sekizai.context_processors.sekizai', + 'header_fotky.context_processors.vzhled', + 'various.context_processors.rozliseni', + 'various.context_processors.april', + ) + }, + }, ] @@ -99,59 +99,59 @@ TEMPLATES = [ INSTALLED_APPS = ( - # Basic - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.sites', - 'django.contrib.staticfiles', - 'django.contrib.auth', - - # Utilities - 'sekizai', - 'reversion', - 'django_countries', - 'solo', - 'ckeditor', - 'ckeditor_uploader', - 'taggit', - 'dal', - 'dal_select2', - - 'crispy_forms', - 'django_comments', - - 'django.contrib.flatpages', - 'django.contrib.humanize', - - 'sitetree', - - 'imagekit', - - 'polymorphic', - - 'webpack_loader', - 'rest_framework', - 'rest_framework.authtoken', - - # MaMweb - 'mamweb', - 'seminar', - 'galerie', - 'korektury', - 'prednasky', - 'header_fotky', - 'various', - 'various.autentizace', - 'api', - 'aesop', - 'odevzdavatko', - 'vysledkovky', - 'personalni', - 'soustredeni', - 'treenode', - - # Admin upravy: + # Basic + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.sites', + 'django.contrib.staticfiles', + 'django.contrib.auth', + + # Utilities + 'sekizai', + 'reversion', + 'django_countries', + 'solo', + 'ckeditor', + 'ckeditor_uploader', + 'taggit', + 'dal', + 'dal_select2', + + 'crispy_forms', + 'django_comments', + + 'django.contrib.flatpages', + 'django.contrib.humanize', + + 'sitetree', + + 'imagekit', + + 'polymorphic', + + 'webpack_loader', + 'rest_framework', + 'rest_framework.authtoken', + + # MaMweb + 'mamweb', + 'seminar', + 'galerie', + 'korektury', + 'prednasky', + 'header_fotky', + 'various', + 'various.autentizace', + 'api', + 'aesop', + 'odevzdavatko', + 'vysledkovky', + 'personalni', + 'soustredeni', + 'treenode', + + # Admin upravy: # 'material', # 'material.admin', @@ -159,76 +159,76 @@ INSTALLED_APPS = ( # 'admin_tools.theming', # 'admin_tools.menu', # 'admin_tools.dashboard', - 'django.contrib.admin', + 'django.contrib.admin', - # Nechat na konci (INSTALLED_APPS je uspořádané): - 'django_cleanup.apps.CleanupConfig', # Uklízí media/ + # Nechat na konci (INSTALLED_APPS je uspořádané): + 'django_cleanup.apps.CleanupConfig', # Uklízí media/ ) DEBUG_TOOLBAR_CONFIG = { - 'SHOW_COLLAPSED': True, + 'SHOW_COLLAPSED': True, } SUMMERNOTE_CONFIG = { - 'iframe': False, - 'airMode': False, - 'attachment_require_authentication': True, - 'width': '80%', + 'iframe': False, + 'airMode': False, + 'attachment_require_authentication': True, + 'width': '80%', # 'height': '30em', - 'toolbar': [ - ['style', ['style']], - ['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']], - ['color', ['color']], - ['para', ['ul', 'ol', 'paragraph']], - ['table', ['table']], - ['insert', ['link', 'picture', 'hr']], - ['view', ['fullscreen', 'codeview']], - ['help', ['help']], - ] + 'toolbar': [ + ['style', ['style']], + ['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']], + ['color', ['color']], + ['para', ['ul', 'ol', 'paragraph']], + ['table', ['table']], + ['insert', ['link', 'picture', 'hr']], + ['view', ['fullscreen', 'codeview']], + ['help', ['help']], + ] } CKEDITOR_UPLOAD_PATH = "uploads/" CKEDITOR_IMAGE_BACKEND = 'pillow' #CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js' CKEDITOR_CONFIGS = { - 'default': { - 'entities': False, - 'toolbar': [ - ['Source', 'ShowBlocks', '-', 'Maximize'], - ['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'], - ['NumberedList','BulletedList','-','Blockquote','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], - ['Link', 'Unlink', 'Anchor', '-', 'Image', 'Table', 'HorizontalRule'], - ['Format'], - - ], + 'default': { + 'entities': False, + 'toolbar': [ + ['Source', 'ShowBlocks', '-', 'Maximize'], + ['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'], + ['NumberedList','BulletedList','-','Blockquote','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], + ['Link', 'Unlink', 'Anchor', '-', 'Image', 'Table', 'HorizontalRule'], + ['Format'], + + ], # 'toolbar': 'full', - 'height': '40em', - 'width': '100%', - 'toolbarStartupExpanded': False, - 'allowedContent' : True, - }, + 'height': '40em', + 'width': '100%', + 'toolbarStartupExpanded': False, + 'allowedContent' : True, + }, } # Webpack loader VUE_FRONTEND_DIR = os.path.join(BASE_DIR, 'vue_frontend') WEBPACK_LOADER = { - 'DEFAULT': { - 'CACHE': False, - 'BUNDLE_DIR_NAME': 'vue/', # must end with slash - 'STATS_FILE': os.path.join(VUE_FRONTEND_DIR, 'webpack-stats.json'), - 'POLL_INTERVAL': 0.1, - 'TIMEOUT': None, - 'IGNORE': [r'.+\.hot-update.js', r'.+\.map'] - } + 'DEFAULT': { + 'CACHE': False, + 'BUNDLE_DIR_NAME': 'vue/', # must end with slash + 'STATS_FILE': os.path.join(VUE_FRONTEND_DIR, 'webpack-stats.json'), + 'POLL_INTERVAL': 0.1, + 'TIMEOUT': None, + 'IGNORE': [r'.+\.hot-update.js', r'.+\.map'] + } } # Dajngo REST Framework REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', - 'PAGE_SIZE': 100 + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 100 } @@ -236,22 +236,22 @@ REST_FRAMEWORK = { # Create file 'django.secret' in every install (it is not kept in git) try: - with open(os.path.join(os.path.dirname(__file__), '..', 'django.secret')) as f: - SECRET_KEY = f.readline().strip() + with open(os.path.join(os.path.dirname(__file__), '..', 'django.secret')) as f: + SECRET_KEY = f.readline().strip() except: - SECRET_KEY = '12345zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzdqwkdlkj' + SECRET_KEY = '12345zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzdqwkdlkj' # Logging LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, + 'version': 1, + 'disable_existing_loggers': False, - 'formatters': { - 'verbose': { - 'format': '%(levelname)s %(asctime)s %(module)s (logger %(name)s): %(message)s' - }, - }, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s (logger %(name)s): %(message)s' + }, + }, 'filters': { 'Http404AsInfo': { @@ -262,76 +262,76 @@ LOGGING = { }, }, - 'loggers': { - - 'django': { - 'handlers': ['console'], - 'level': 'DEBUG', - 'filters': ['StripSensitiveFormData'], - }, - 'django.security.csrf': { - 'handlers': ['console'], - 'level': 'DEBUG', - 'filters': ['StripSensitiveFormData'], - }, - 'django.request': { - 'handlers': ['console'], - 'level': 'DEBUG', + 'loggers': { + + 'django': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'filters': ['StripSensitiveFormData'], + }, + 'django.security.csrf': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'filters': ['StripSensitiveFormData'], + }, + 'django.request': { + 'handlers': ['console'], + 'level': 'DEBUG', 'filters': ['Http404AsInfo'], - }, + }, - 'seminar.prihlaska.form':{ + 'seminar.prihlaska.form':{ 'handlers': ['console','registration_logfile'], 'level': 'INFO' }, - 'seminar.prihlaska.problem':{ + 'seminar.prihlaska.problem':{ 'handlers': ['console','mail_registration','registration_error_log'], 'level': 'INFO' }, - # Catch-all logger - '': { - 'handlers': ['console'], # Add 'mail_admins' in prod and test - 'level': 'DEBUG', - 'filters': ['StripSensitiveFormData'], - }, - - }, - - 'handlers': { - - 'console': { - 'level': 'WARNING', ## Set to 'DEBUG' in local - 'class': 'logging.StreamHandler', - 'formatter': 'verbose', - }, - - 'mail_admins': { - 'level': 'WARNING', - 'class': 'django.utils.log.AdminEmailHandler', - 'formatter': 'verbose', - 'filters': ['StripSensitiveFormData'], - }, - 'mail_registration': { - 'level': 'WARNING', - 'class': 'django.utils.log.AdminEmailHandler', - 'formatter': 'verbose', - }, - 'registration_logfile':{ - 'level': 'INFO', + # Catch-all logger + '': { + 'handlers': ['console'], # Add 'mail_admins' in prod and test + 'level': 'DEBUG', + 'filters': ['StripSensitiveFormData'], + }, + + }, + + 'handlers': { + + 'console': { + 'level': 'WARNING', ## Set to 'DEBUG' in local + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + + 'mail_admins': { + 'level': 'WARNING', + 'class': 'django.utils.log.AdminEmailHandler', + 'formatter': 'verbose', + 'filters': ['StripSensitiveFormData'], + }, + 'mail_registration': { + 'level': 'WARNING', + 'class': 'django.utils.log.AdminEmailHandler', + 'formatter': 'verbose', + }, + 'registration_logfile':{ + 'level': 'INFO', 'class': 'logging.FileHandler', # filename declared in specific configuration files - 'formatter': 'verbose', + 'formatter': 'verbose', }, - 'registration_error_log':{ - 'level': 'INFO', + 'registration_error_log':{ + 'level': 'INFO', 'class': 'logging.FileHandler', # filename declared in specific configuration files - 'formatter': 'verbose', + 'formatter': 'verbose', }, - }, - } + }, + } # Permissions for uploads FILE_UPLOAD_PERMISSIONS = 0o0644 @@ -352,14 +352,14 @@ POSLI_MAILOVOU_NOTIFIKACI = False # Logování chyb class InvalidTemplateVariable(str): - def __mod__(self, variable): - import logging - logger = logging.getLogger(__name__) - for line in traceback.walk_stack(None): - if 'context' in line[0].f_locals and 'request' in line[0].f_locals['context']: - logger.warning("Proměnná '%s' neexistuje: %s" % (variable, line[0].f_locals['context']['request'])) - break - return '' + def __mod__(self, variable): + import logging + logger = logging.getLogger(__name__) + for line in traceback.walk_stack(None): + if 'context' in line[0].f_locals and 'request' in line[0].f_locals['context']: + logger.warning("Proměnná '%s' neexistuje: %s" % (variable, line[0].f_locals['context']['request'])) + break + return '' TEMPLATES[0]['OPTIONS']['string_if_invalid'] = InvalidTemplateVariable('%s') # Django 3.2 vyžaduje explicitní nastavení autoklíče, zatím nechápu proč diff --git a/mamweb/settings_local.py b/mamweb/settings_local.py index 4cf76dc2..540c0453 100644 --- a/mamweb/settings_local.py +++ b/mamweb/settings_local.py @@ -11,16 +11,16 @@ import os.path from .settings_common import * MIDDLEWARE += ( - 'debug_toolbar.middleware.DebugToolbarMiddleware', - ) + 'debug_toolbar.middleware.DebugToolbarMiddleware', + ) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ INSTALLED_APPS += ( - 'debug_toolbar', - 'django_extensions', - ) + 'debug_toolbar', + 'django_extensions', + ) # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -37,10 +37,10 @@ ALLOWED_HOSTS.append('localhost') # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db-local.sqlite3'), - } + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db-local.sqlite3'), + } } #DATABASES = { # 'default': { @@ -52,46 +52,46 @@ DATABASES = { # LOGGING LOGGING = { - 'version': 1, - 'disable_existing_loggers': True, - 'filters': { - 'require_debug_false': { - '()': 'django.utils.log.RequireDebugFalse' - } - }, - 'formatters': { - 'simple': { - 'format': '%(asctime)s - %(name)s - %(levelname)-8s - %(message)s', - }, - }, - 'handlers': { - 'dummy': { - 'class': 'logging.NullHandler', - }, - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'simple', - }, - }, - 'loggers': { + 'version': 1, + 'disable_existing_loggers': True, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'formatters': { + 'simple': { + 'format': '%(asctime)s - %(name)s - %(levelname)-8s - %(message)s', + }, + }, + 'handlers': { + 'dummy': { + 'class': 'logging.NullHandler', + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'simple', + }, + }, + 'loggers': { # Vypisovani databazovych dotazu do konzole - #'django.db.backends': { - # 'level': 'DEBUG', - # 'handlers': ['console'], - # 'propagate': False, - #}, - 'werkzeug': { - 'handlers': ['console'], - 'level': 'DEBUG', - 'propagate': True, - }, - '': { - 'handlers': ['console'], - 'level': 'DEBUG', - 'propagate': False, - }, - }, + #'django.db.backends': { + # 'level': 'DEBUG', + # 'handlers': ['console'], + # 'propagate': False, + #}, + 'werkzeug': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'propagate': True, + }, + '': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'propagate': False, + }, + }, } # set to 'DEBUG' for EXTRA verbose output diff --git a/mamweb/settings_prod.py b/mamweb/settings_prod.py index 6a20ff8c..3a81c8c4 100644 --- a/mamweb/settings_prod.py +++ b/mamweb/settings_prod.py @@ -16,8 +16,8 @@ from .settings_common import * # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ INSTALLED_APPS += ( - 'django_extensions', - ) + 'django_extensions', + ) # SECURITY WARNING: keep the secret key used in production secret! assert not SECRET_KEY.startswith('12345') @@ -34,14 +34,14 @@ ALLOWED_HOSTS = ['mam.mff.cuni.cz', 'www.mam.mff.cuni.cz', 'atrey.karlin.mff.cun # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'mam_prod', - 'USER': 'mam-web', - 'TEST': { - 'NAME': 'mam-prod-testdb', - }, - }, + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'mam_prod', + 'USER': 'mam-web', + 'TEST': { + 'NAME': 'mam-prod-testdb', + }, + }, } import os diff --git a/mamweb/settings_test.py b/mamweb/settings_test.py index 365664d2..eac5a7b4 100644 --- a/mamweb/settings_test.py +++ b/mamweb/settings_test.py @@ -13,16 +13,16 @@ import os.path from .settings_common import * # zatim nutne, casem snad vyresime # noqa MIDDLEWARE += ( - 'debug_toolbar.middleware.DebugToolbarMiddleware', - ) + 'debug_toolbar.middleware.DebugToolbarMiddleware', + ) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ INSTALLED_APPS += ( - 'debug_toolbar', - 'django_extensions', - ) + 'debug_toolbar', + 'django_extensions', + ) # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = ')^u=i65*zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzv9l+zo)n' @@ -38,21 +38,21 @@ ALLOWED_HOSTS = ['*.mam.mff.cuni.cz', 'atrey.karlin.mff.cuni.cz', 'mam.mff.cuni. # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'mam_test', - 'USER': 'mam-web', - 'TEST': { - 'NAME': 'mam-test-testdb', - }, - }, + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'mam_test', + 'USER': 'mam-web', + 'TEST': { + 'NAME': 'mam-test-testdb', + }, + }, } import os SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz' ADMINS = [ - ('M&M ERRORs', 'mam-errors@mam.mff.cuni.cz'), + ('M&M ERRORs', 'mam-errors@mam.mff.cuni.cz'), ] diff --git a/seminar/models/base.py b/seminar/models/base.py index 77c857a3..1069f165 100644 --- a/seminar/models/base.py +++ b/seminar/models/base.py @@ -4,18 +4,18 @@ from django.db import models class SeminarModelBase(models.Model): - class Meta: - abstract = True + class Meta: + abstract = True - def verejne(self): - return False + def verejne(self): + return False - # def get_absolute_url(self): - # return "https://" + str(get_current_site(None)) + self.verejne_url() + # def get_absolute_url(self): + # return "https://" + str(get_current_site(None)) + self.verejne_url() - def admin_url(self): - model_name = self.__class__.__name__.lower() - return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, )) + def admin_url(self): + model_name = self.__class__.__name__.lower() + return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, )) # def verejne_url(self): # return None diff --git a/seminar/models/novinky.py b/seminar/models/novinky.py index f6ce4161..cee674a8 100644 --- a/seminar/models/novinky.py +++ b/seminar/models/novinky.py @@ -9,30 +9,30 @@ from . import personalni as pm @reversion.register(ignore_duplicates=True) class Novinky(models.Model): - class Meta: - verbose_name = 'Novinka' - verbose_name_plural = 'Novinky' - ordering = ['-datum'] + class Meta: + verbose_name = 'Novinka' + verbose_name_plural = 'Novinky' + ordering = ['-datum'] - datum = models.DateField(auto_now_add=True) + datum = models.DateField(auto_now_add=True) - text = models.TextField('Text novinky', blank=True, null=True) - obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/', - null=True, blank=True) + text = models.TextField('Text novinky', blank=True, null=True) + obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/', + null=True, blank=True) - obrazek_maly = ImageSpecField(source='obrazek', - processors=[ - ResizeToFit(350, 200, upscale=False) - ], - options={'quality': 95}) + obrazek_maly = ImageSpecField(source='obrazek', + processors=[ + ResizeToFit(350, 200, upscale=False) + ], + options={'quality': 95}) - autor = models.ForeignKey(pm.Organizator, verbose_name='Autor novinky', null=True, - on_delete=models.SET_NULL) + autor = models.ForeignKey(pm.Organizator, verbose_name='Autor novinky', null=True, + on_delete=models.SET_NULL) - zverejneno = models.BooleanField('Zveřejněno', default=False) + zverejneno = models.BooleanField('Zveřejněno', default=False) - def __str__(self): - if self.text: - return '[' + str(self.datum) + '] ' + self.text[0:50] - else: - return '[' + str(self.datum) + '] ' + def __str__(self): + if self.text: + return '[' + str(self.datum) + '] ' + self.text[0:50] + else: + return '[' + str(self.datum) + '] ' diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py index 9ae161c5..c286558c 100644 --- a/seminar/models/odevzdavatko.py +++ b/seminar/models/odevzdavatko.py @@ -18,68 +18,68 @@ from seminar.models import base as bm @reversion.register(ignore_duplicates=True) class Reseni(bm.SeminarModelBase): - class Meta: - db_table = 'seminar_reseni' - verbose_name = 'Řešení' - verbose_name_plural = 'Řešení' - #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. - ordering = ['-cas_doruceni'] + class Meta: + db_table = 'seminar_reseni' + verbose_name = 'Řešení' + verbose_name_plural = 'Řešení' + #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. + ordering = ['-cas_doruceni'] - # Interní ID - id = models.AutoField(primary_key = True) + # Interní ID + id = models.AutoField(primary_key = True) - # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. - problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', - through='Hodnoceni') + # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. + problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', + through='Hodnoceni') - resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení', - help_text='Seznam autorů řešení', through='Reseni_Resitele') + resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení', + help_text='Seznam autorů řešení', through='Reseni_Resitele') - cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) + cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) - FORMA_PAPIR = 'papir' - FORMA_EMAIL = 'email' - FORMA_UPLOAD = 'upload' - FORMA_CHOICES = [ - (FORMA_PAPIR, 'Papírové řešení'), - (FORMA_EMAIL, 'Emailem'), - (FORMA_UPLOAD, 'Upload přes web'), - ] - forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, - default=FORMA_EMAIL) + FORMA_PAPIR = 'papir' + FORMA_EMAIL = 'email' + FORMA_UPLOAD = 'upload' + FORMA_CHOICES = [ + (FORMA_PAPIR, 'Papírové řešení'), + (FORMA_EMAIL, 'Emailem'), + (FORMA_UPLOAD, 'Upload přes web'), + ] + forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, + default=FORMA_EMAIL) - text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení', - blank=True, null=True, related_name="reseni_cely_set", - on_delete=models.PROTECT) + text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení', + blank=True, null=True, related_name="reseni_cely_set", + on_delete=models.PROTECT) - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k řešení (plain text)') + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k řešení (plain text)') - zverejneno = models.BooleanField('řešení zveřejněno', default=False, - help_text='Udává, zda je řešení zveřejněno') + zverejneno = models.BooleanField('řešení zveřejněno', default=False, + help_text='Udává, zda je řešení zveřejněno') - def verejne_url(self): - return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) + def verejne_url(self): + return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) - def absolute_url(self): - return "https://" + str(get_current_site(None)) + self.verejne_url() + def absolute_url(self): + return "https://" + str(get_current_site(None)) + self.verejne_url() - # má OneToOneField s: - # Konfera + # má OneToOneField s: + # Konfera - # má ForeignKey s: - # Hodnoceni + # má ForeignKey s: + # Hodnoceni - def sum_body(self): - return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] + def sum_body(self): + return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] - def __str__(self): - return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) - # NOTE: Potenciální DB HOG (bez select_related) + def __str__(self): + return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) + # NOTE: Potenciální DB HOG (bez select_related) - def deadline_reseni(self): - return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() + def deadline_reseni(self): + return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() ## Pravdepodobne uz nebude potreba: # def save(self, *args, **kwargs): @@ -89,112 +89,112 @@ class Reseni(bm.SeminarModelBase): # super(Reseni, self).save(*args, **kwargs) class Hodnoceni(bm.SeminarModelBase): - class Meta: - db_table = 'seminar_hodnoceni' - verbose_name = 'Hodnocení' - verbose_name_plural = 'Hodnocení' + class Meta: + db_table = 'seminar_hodnoceni' + verbose_name = 'Hodnocení' + verbose_name_plural = 'Hodnocení' - # Interní ID - id = models.AutoField(primary_key = True) + # Interní ID + id = models.AutoField(primary_key = True) - body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', - blank=True, null=True) + body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', + blank=True, null=True) - cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', - related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) + cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', + related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) - # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body - deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', - related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) + # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body + deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', + related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) - reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) + reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) - problem = models.ForeignKey(am.Problem, verbose_name='problém', - related_name='hodnoceni', on_delete=models.PROTECT) + problem = models.ForeignKey(am.Problem, verbose_name='problém', + related_name='hodnoceni', on_delete=models.PROTECT) - feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') + feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') - def __str__(self): - return "{}, {}, {}".format(self.problem, self.reseni, self.body) + def __str__(self): + return "{}, {}, {}".format(self.problem, self.reseni, self.body) def generate_filename(self, filename): - return os.path.join( - settings.SEMINAR_RESENI_DIR, - am.aux_generate_filename(self, filename) - ) + return os.path.join( + settings.SEMINAR_RESENI_DIR, + am.aux_generate_filename(self, filename) + ) @reversion.register(ignore_duplicates=True) class PrilohaReseni(bm.SeminarModelBase): - class Meta: - db_table = 'seminar_priloha_reseni' - verbose_name = 'Příloha řešení' - verbose_name_plural = 'Přílohy řešení' - ordering = ['reseni', 'vytvoreno'] + class Meta: + db_table = 'seminar_priloha_reseni' + verbose_name = 'Příloha řešení' + verbose_name_plural = 'Přílohy řešení' + ordering = ['reseni', 'vytvoreno'] - # Interní ID - id = models.AutoField(primary_key = True) + # Interní ID + id = models.AutoField(primary_key = True) - reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', - on_delete=models.CASCADE) + reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', + on_delete=models.CASCADE) - vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) + vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) - soubor = models.FileField('soubor', upload_to = generate_filename) + soubor = models.FileField('soubor', upload_to = generate_filename) - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') - res_poznamka = models.TextField('poznámka řešitele', blank=True, - help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje') + res_poznamka = models.TextField('poznámka řešitele', blank=True, + help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje') - def __str__(self): - return str(self.soubor) + def __str__(self): + return str(self.soubor) - def split(self): - "Vrátí cestu rozsekanou po složkách. To se hodí v templatech" - # Věřím, že tohle funguje, případně použít os.path nebo pathlib. - return self.soubor.url.split('/') + def split(self): + "Vrátí cestu rozsekanou po složkách. To se hodí v templatech" + # Věřím, že tohle funguje, případně použít os.path nebo pathlib. + return self.soubor.url.split('/') # Vazebna tabulka. Mozna se generuje automaticky. @reversion.register(ignore_duplicates=True) class Reseni_Resitele(models.Model): - class Meta: - db_table = 'seminar_reseni_resitele' - verbose_name = 'Řešení řešitelů' - verbose_name_plural = 'Řešení řešitelů' - ordering = ['reseni', 'resitele'] + class Meta: + db_table = 'seminar_reseni_resitele' + verbose_name = 'Řešení řešitelů' + verbose_name_plural = 'Řešení řešitelů' + ordering = ['reseni', 'resitele'] - # Interní ID - id = models.AutoField(primary_key = True) + # Interní ID + id = models.AutoField(primary_key = True) - resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) + resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) - reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) + reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) - # podil - jakou merou se ktery resitel podilel na danem reseni - # - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni + # podil - jakou merou se ktery resitel podilel na danem reseni + # - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni - def __str__(self): - return '{} od {}'.format(self.reseni, self.resitel) - # NOTE: Poteciální DB HOG bez select_related + def __str__(self): + return '{} od {}'.format(self.reseni, self.resitel) + # NOTE: Poteciální DB HOG bez select_related class ReseniNode(tm.TreeNode): - class Meta: - db_table = 'seminar_nodes_otistene_reseni' - verbose_name = 'Otištěné řešení (Node)' - verbose_name_plural = 'Otištěná řešení (Node)' - reseni = models.ForeignKey(Reseni, - on_delete=models.PROTECT, - verbose_name = 'reseni') - - def aktualizuj_nazev(self): - self.nazev = "ReseniNode: "+str(self.reseni) - - def getOdkazStr(self): - return str(self.reseni) + class Meta: + db_table = 'seminar_nodes_otistene_reseni' + verbose_name = 'Otištěné řešení (Node)' + verbose_name_plural = 'Otištěná řešení (Node)' + reseni = models.ForeignKey(Reseni, + on_delete=models.PROTECT, + verbose_name = 'reseni') + + def aktualizuj_nazev(self): + self.nazev = "ReseniNode: "+str(self.reseni) + + def getOdkazStr(self): + return str(self.reseni) diff --git a/soustredeni/admin.py b/soustredeni/admin.py index 11cb8d1d..091f9c59 100644 --- a/soustredeni/admin.py +++ b/soustredeni/admin.py @@ -6,38 +6,38 @@ from seminar.models import soustredeni as m class SoustredeniUcastniciInline(admin.TabularInline): - model = m.Soustredeni_Ucastnici - extra = 1 - fields = ['resitel','poznamka'] - autocomplete_fields = ['resitel'] - ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni'] - formfield_overrides = { - models.TextField: {'widget': widgets.TextInput} - } + model = m.Soustredeni_Ucastnici + extra = 1 + fields = ['resitel','poznamka'] + autocomplete_fields = ['resitel'] + ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni'] + formfield_overrides = { + models.TextField: {'widget': widgets.TextInput} + } - def get_queryset(self,request): - qs = super().get_queryset(request) - return qs.select_related('resitel','soustredeni') + def get_queryset(self,request): + qs = super().get_queryset(request) + return qs.select_related('resitel','soustredeni') class SoustredeniOrganizatoriInline(admin.TabularInline): - model = m.Soustredeni.organizatori.through - extra = 1 - fields = ['organizator','poznamka'] - autocomplete_fields = ['organizator'] - ordering = ['organizator__osoba__jmeno','organizator__prijmeni'] - formfield_overrides = { - models.TextField: {'widget': widgets.TextInput} - } + model = m.Soustredeni.organizatori.through + extra = 1 + fields = ['organizator','poznamka'] + autocomplete_fields = ['organizator'] + ordering = ['organizator__osoba__jmeno','organizator__prijmeni'] + formfield_overrides = { + models.TextField: {'widget': widgets.TextInput} + } - def get_queryset(self,request): - qs = super().get_queryset(request) - return qs.select_related('organizator', 'soustredeni') + def get_queryset(self,request): + qs = super().get_queryset(request) + return qs.select_related('organizator', 'soustredeni') @admin.register(m.Soustredeni) class SoustredeniAdmin(admin.ModelAdmin): - model = m.Soustredeni - inline_type = 'tabular' - inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] + model = m.Soustredeni + inline_type = 'tabular' + inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] diff --git a/treenode/admin.py b/treenode/admin.py index d2ff4409..92c85cd5 100644 --- a/treenode/admin.py +++ b/treenode/admin.py @@ -9,80 +9,80 @@ import seminar.models as m @admin.register(m.TreeNode) class TreeNodeAdmin(PolymorphicParentModelAdmin): - base_model = m.TreeNode - child_models = [ - m.RocnikNode, - m.CisloNode, - m.MezicisloNode, - m.TemaVCisleNode, - m.UlohaZadaniNode, - m.PohadkaNode, - m.UlohaVzorakNode, - m.TextNode, - m.CastNode, - m.OrgTextNode, - ] - - actions = ['aktualizuj_nazvy'] - - # XXX: nejspíš je to totální DB HOG, nechcete to použít moc často. - def aktualizuj_nazvy(self, request, queryset): - newqs = queryset.get_real_instances() - for tn in newqs: - tn.aktualizuj_nazev() - tn.save() - self.message_user(request, "Názvy aktualizovány.") - aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" + base_model = m.TreeNode + child_models = [ + m.RocnikNode, + m.CisloNode, + m.MezicisloNode, + m.TemaVCisleNode, + m.UlohaZadaniNode, + m.PohadkaNode, + m.UlohaVzorakNode, + m.TextNode, + m.CastNode, + m.OrgTextNode, + ] + + actions = ['aktualizuj_nazvy'] + + # XXX: nejspíš je to totální DB HOG, nechcete to použít moc často. + def aktualizuj_nazvy(self, request, queryset): + newqs = queryset.get_real_instances() + for tn in newqs: + tn.aktualizuj_nazev() + tn.save() + self.message_user(request, "Názvy aktualizovány.") + aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" @admin.register(m.RocnikNode) class RocnikNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.RocnikNode - show_in_index = True + base_model = m.RocnikNode + show_in_index = True @admin.register(m.CisloNode) class CisloNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.CisloNode - show_in_index = True + base_model = m.CisloNode + show_in_index = True @admin.register(m.MezicisloNode) class MezicisloNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.MezicisloNode - show_in_index = True + base_model = m.MezicisloNode + show_in_index = True @admin.register(m.TemaVCisleNode) class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.TemaVCisleNode - show_in_index = True + base_model = m.TemaVCisleNode + show_in_index = True @admin.register(m.UlohaZadaniNode) class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.UlohaZadaniNode - show_in_index = True + base_model = m.UlohaZadaniNode + show_in_index = True @admin.register(m.PohadkaNode) class PohadkaNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.PohadkaNode - show_in_index = True + base_model = m.PohadkaNode + show_in_index = True @admin.register(m.UlohaVzorakNode) class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.UlohaVzorakNode - show_in_index = True + base_model = m.UlohaVzorakNode + show_in_index = True @admin.register(m.TextNode) class TextNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.TextNode - show_in_index = True + base_model = m.TextNode + show_in_index = True @admin.register(m.CastNode) class TextNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.CastNode - show_in_index = True - fields = ('nadpis',) + base_model = m.CastNode + show_in_index = True + fields = ('nadpis',) @admin.register(m.OrgTextNode) class TextNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.OrgTextNode - show_in_index = True + base_model = m.OrgTextNode + show_in_index = True diff --git a/treenode/permissions.py b/treenode/permissions.py index 5503832f..bc5c68a8 100644 --- a/treenode/permissions.py +++ b/treenode/permissions.py @@ -2,6 +2,6 @@ from rest_framework.permissions import BasePermission class AllowWrite(BasePermission): - def has_permission(self, request, view): - return request.user.has_perm('auth.org') + def has_permission(self, request, view): + return request.user.has_perm('auth.org') diff --git a/various/autentizace/utils.py b/various/autentizace/utils.py index 2341fbdd..d8bea060 100644 --- a/various/autentizace/utils.py +++ b/various/autentizace/utils.py @@ -6,16 +6,16 @@ from django.utils.http import urlsafe_base64_encode def posli_reset_hesla(u, request=None): - uid = urlsafe_base64_encode(force_bytes(u.pk)) - token = PasswordResetTokenGenerator().make_token(u) - url = "https://%s%s" % ( - str(get_current_site(request)), - str(reverse_lazy("reset_password_confirm", args=[uid, token])) - ) + uid = urlsafe_base64_encode(force_bytes(u.pk)) + token = PasswordResetTokenGenerator().make_token(u) + url = "https://%s%s" % ( + str(get_current_site(request)), + str(reverse_lazy("reset_password_confirm", args=[uid, token])) + ) - u.email_user( - subject="Vítej mezi řešiteli M&M!", - message="""Milý řešiteli, milá řešitelko, + u.email_user( + subject="Vítej mezi řešiteli M&M!", + message="""Milý řešiteli, milá řešitelko, tvůj e-mail byl právě zaregistrován na mam.matfyz.cz. Heslo si prosím nastav na: %s @@ -26,6 +26,6 @@ Organizátoři M&M Tento e-mail byl vygenerován automaticky, chceš-li nás kontaktovat, napiš nám na adresu mam@matfyz.cz. """ % url, - # TODO: templates/autentizace a django/contrib/auth/forms.py říkají, jak na to lépe - from_email="registrace@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? - ) \ No newline at end of file + # TODO: templates/autentizace a django/contrib/auth/forms.py říkají, jak na to lépe + from_email="registrace@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? + ) \ No newline at end of file diff --git a/various/log_filters.py b/various/log_filters.py index f20e032c..f037dcde 100644 --- a/various/log_filters.py +++ b/various/log_filters.py @@ -2,10 +2,10 @@ from logging import Filter, INFO from django.urls import reverse class Http404AsInfoFilter(Filter): - def filter(self, record): - if record.name == 'django.request' and record.status_code == 404: - record.levelno = INFO - return 1 # Keep the log record + def filter(self, record): + if record.name == 'django.request' and record.status_code == 404: + record.levelno = INFO + return 1 # Keep the log record class StripSensitiveFormDataFilter(Filter): def filter(self, record): From 4a0e8e61be8cdc529a5eb2e76faf990a56d41de4 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 1 Dec 2022 13:07:09 +0100 Subject: [PATCH 069/120] =?UTF-8?q?Druh=C3=A1=20v=C3=A1rka=20zarovn=C3=A1n?= =?UTF-8?q?=C3=AD=20tabul=C3=A1tory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/views/autocomplete.py | 2 +- seminar/testutils.py | 18 +++++++++--------- seminar/views/views_all.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api/views/autocomplete.py b/api/views/autocomplete.py index 33e083eb..838c7dba 100644 --- a/api/views/autocomplete.py +++ b/api/views/autocomplete.py @@ -12,7 +12,7 @@ from .helpers import LoginRequiredAjaxMixin class SkolaAutocomplete(autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """ def get_queryset(self): - # Don't forget to filter out results depending on the visitor ! + # Don't forget to filter out results depending on the visitor ! qs = m.Skola.objects.all() if self.q: words = self.q.split(' ') #TODO re split podle bileho znaku diff --git a/seminar/testutils.py b/seminar/testutils.py index a13e82c8..7076d5f0 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -474,15 +474,15 @@ def get_text(): def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): tema = Tema.objects.create( - nazev=nazev, - stav=Problem.STAV_ZADANY, - zamereni="M", - autor=rnd.choice(organizatori), - garant=rnd.choice(organizatori), - kod=str(kod), - tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], - rocnik=rocnik, - abstrakt = lorem.paragraph() + nazev=nazev, + stav=Problem.STAV_ZADANY, + zamereni="M", + autor=rnd.choice(organizatori), + garant=rnd.choice(organizatori), + kod=str(kod), + tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], + rocnik=rocnik, + abstrakt = lorem.paragraph() ) # Generování struktury k tématu diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index c5631430..3f2cdf01 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -686,7 +686,7 @@ def formularOKView(request, text=''): ] context = { 'odkazy': odkazy, - 'text': text, + 'text': text, } return render(request, template_name, context) From 5178d95a44c89a73ea20905da793afd8d0fb144c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Sun, 4 Dec 2022 19:58:34 +0100 Subject: [PATCH 070/120] =?UTF-8?q?add:=20title=20u=20obr=C3=A1zkov=C3=BDc?= =?UTF-8?q?h=20odkaz=C5=AF=20v=20detailu=20=C5=99e=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 9f6d31ac..207e2976 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -109,14 +109,14 @@ $(document).ready(function(){ {{ subform.body }} {{ subform.deadline_body }} {{ subform.feedback }} - Smazat + Smazat {% endfor %} - Přidat hodnocení
+ Přidat hodnocení
@@ -125,7 +125,7 @@ $(document).ready(function(){ - + From 52904575b070667037507ff7404212bcc17dcbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Sun, 4 Dec 2022 20:14:59 +0100 Subject: [PATCH 071/120] =?UTF-8?q?hotfix:=20opraveno=20zobrazov=C3=A1n?= =?UTF-8?q?=C3=AD=20smazat=20hodnocen=C3=AD=20v=20detailu=20=C5=99e=C5=A1e?= =?UTF-8?q?n=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 7 +++++++ odevzdavatko/templates/odevzdavatko/detail.html | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 236072f5..f4d8c0f8 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -425,6 +425,13 @@ textarea.feedback { margin: 5px; } +/* td obsahující křížek v detailu řešení se nesmí smrštit na 0 */ +/* FIXME až bude firefox příčetný, nahradit td:has(.smazat_hodnoceni) */ +.has_smazat_hodnoceni { + min-width: 20px; + padding: 3px; +} + /* titulni stranka */ diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 207e2976..a3f986d5 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -109,7 +109,7 @@ $(document).ready(function(){ {{ subform.body }} {{ subform.deadline_body }} {{ subform.feedback }} - Smazat + Smazat {% endfor %} @@ -125,7 +125,7 @@ $(document).ready(function(){ {{ form.empty_form.body }} {{ form.empty_form.deadline_body }} {{ form.empty_form.feedback }} - Smazat + Smazat From baf08c4f0484c220ea3f6fc23b7ed14577cd8613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 5 Dec 2022 20:15:31 +0100 Subject: [PATCH 072/120] =?UTF-8?q?hotfix:=20n=C3=A1zev=20u=20korekturovan?= =?UTF-8?q?=C3=A9ho=20pdf=20mohl=20b=C3=BDt=20pr=C3=A1zdn=C3=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0019_auto_20221205_2014.py | 18 ++++++++++++++++++ korektury/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 korektury/migrations/0019_auto_20221205_2014.py diff --git a/korektury/migrations/0019_auto_20221205_2014.py b/korektury/migrations/0019_auto_20221205_2014.py new file mode 100644 index 00000000..6860e626 --- /dev/null +++ b/korektury/migrations/0019_auto_20221205_2014.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-12-05 19:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0018_korekturovanepdf_poslat_mail'), + ] + + operations = [ + migrations.AlterField( + model_name='korekturovanepdf', + name='nazev', + field=models.CharField(help_text='Název (např. 22.1 verze 4) korekturovaného PDF', max_length=50, verbose_name='název PDF'), + ), + ] diff --git a/korektury/models.py b/korektury/models.py index 34caba9a..ac82c14e 100644 --- a/korektury/models.py +++ b/korektury/models.py @@ -55,7 +55,7 @@ class KorekturovanePDF(models.Model): cas = models.DateTimeField(u'čas vložení PDF',default=timezone.now,help_text = 'Čas vložení PDF') - nazev = models.CharField(u'název PDF',blank = True,max_length=50, help_text='Název (např. 22.1 verze 4) korekturovaného PDF') + nazev = models.CharField(u'název PDF',blank = False,max_length=50, help_text='Název (např. 22.1 verze 4) korekturovaného PDF') komentar = models.TextField(u'komentář k PDF',blank = True, help_text='Komentář ke korekturovanému PDF (např. na co se zaměřit)') From 1b521049af6c8d98770da2508fe54f5c0f2b0477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 5 Dec 2022 20:53:22 +0100 Subject: [PATCH 073/120] =?UTF-8?q?hotfix:=20tabulka=20od.=20=C5=99e=C5=A1?= =?UTF-8?q?en=C3=AD=20na=20celou=20obrazovku?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 7 +++++++ odevzdavatko/templates/odevzdavatko/tabulka.html | 2 ++ 2 files changed, 9 insertions(+) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index f4d8c0f8..0eef6964 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -1216,6 +1216,13 @@ div.gdpr { } /* tabulka odevzdaných a došlých řešení */ + +/* Roztáhne obsah z containeru na celou šířku obrazovky: */ +.full_width { + width: 100vw; + margin-left: calc(-50vw + 485px); +} + .dosla_reseni tr th, .dosla_reseni tr td { padding: 1px 10px 1px 10px; border-collapse: collapse; diff --git a/odevzdavatko/templates/odevzdavatko/tabulka.html b/odevzdavatko/templates/odevzdavatko/tabulka.html index 419b4473..6d1232d2 100644 --- a/odevzdavatko/templates/odevzdavatko/tabulka.html +++ b/odevzdavatko/templates/odevzdavatko/tabulka.html @@ -4,6 +4,7 @@ {% block content %} +
{{ filtr.resitele }} {{ filtr.problemy }} @@ -64,4 +65,5 @@ Do data (včetně): {{ filtr.reseni_do }} location.assign(redirect); } +
{% endblock %} From 2e06960f51406d7e658f6264e0e731aef1135066 Mon Sep 17 00:00:00 2001 From: Jonas Havelka Date: Tue, 6 Dec 2022 00:16:40 +0100 Subject: [PATCH 074/120] =?UTF-8?q?To=C5=BE=20jsme=20to=20upravili?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vyvoj.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/vyvoj.rst b/docs/vyvoj.rst index 256f5eec..0d23972a 100644 --- a/docs/vyvoj.rst +++ b/docs/vyvoj.rst @@ -7,7 +7,7 @@ vlastních úpravách nezávisle na ostatních. Potřebné vybavení ------- -Tento soupis cílí na nějakého typického vývojáře webu – Linuxáka. Jistě je +Tento soupis cílí na Linuxáky. Jistě je spousta dalších možností, které zde nejsou postihnuty – poraď se s webaři, pokud si nejsi jistý. (Speciálně lze nějak vyvíjet na Windows, leč lze často narazit na odlišné chování od Linuxu.) @@ -27,7 +27,7 @@ Nutné - Ideálně ve verzi 3.9 (to je to, co je aktuálně (2022) na Gimlim) - Včetně pip-u (na Ubuntu balíček ``python3-pip``), venvu (``python3-venv``) a knihoven pro kompilaci Cčkových rozšíření (``python3-dev``) -- Knihovna pro práci s PostgreSQL (``libpq-dev``) +- Knihovna pro práci s PostgreSQL (``libpq-dev``, protože jsou potřeba všechny db backendy) - Webový prohlížeč - \*NIXový shell (typicky ``bash``) From 0e99cf286f6b1d3a10bee10d9b59d68f99e94581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 6 Dec 2022 01:02:38 +0100 Subject: [PATCH 075/120] =?UTF-8?q?fix:=20tohle=20p=C5=99ed=C4=9Bl=C3=A1n?= =?UTF-8?q?=C3=AD=20mezer=20na=20taby=20vypadalo=20podez=C5=99ele,=20tak?= =?UTF-8?q?=20jsem=20to=20upravil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/views/autocomplete.py | 2 +- treenode/permissions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/views/autocomplete.py b/api/views/autocomplete.py index 838c7dba..4558ad4a 100644 --- a/api/views/autocomplete.py +++ b/api/views/autocomplete.py @@ -12,7 +12,7 @@ from .helpers import LoginRequiredAjaxMixin class SkolaAutocomplete(autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """ def get_queryset(self): - # Don't forget to filter out results depending on the visitor ! + # Don't forget to filter out results depending on the visitor ! qs = m.Skola.objects.all() if self.q: words = self.q.split(' ') #TODO re split podle bileho znaku diff --git a/treenode/permissions.py b/treenode/permissions.py index bc5c68a8..b4022e19 100644 --- a/treenode/permissions.py +++ b/treenode/permissions.py @@ -2,6 +2,6 @@ from rest_framework.permissions import BasePermission class AllowWrite(BasePermission): - def has_permission(self, request, view): - return request.user.has_perm('auth.org') + def has_permission(self, request, view): + return request.user.has_perm('auth.org') From 9a3f51ca6bfafd76282a709b7473b936a3094716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Wed, 14 Dec 2022 22:41:00 +0100 Subject: [PATCH 076/120] =?UTF-8?q?hotfix:=20tabulka=20do=C5=A1l=C3=BDch?= =?UTF-8?q?=20=C5=99e=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/views.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 6c232172..1e27d4f1 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -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): From 332e5e88d50e941c4f2bd357d18df6457be5ca05 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Fri, 16 Dec 2022 23:44:56 +0100 Subject: [PATCH 077/120] =?UTF-8?q?Korekturov=C3=A1tko:=20ne-irelevantn?= =?UTF-8?q?=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- korektury/templates/korektury/opraf.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/korektury/templates/korektury/opraf.html b/korektury/templates/korektury/opraf.html index c7a97317..d2ae11aa 100644 --- a/korektury/templates/korektury/opraf.html +++ b/korektury/templates/korektury/opraf.html @@ -146,7 +146,7 @@ {% endif %} {% if o.status != 'neni_chyba' %} - {% endif %} From 4378c05e3e08be18c7e390e6385ce050ea676044 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Fri, 16 Dec 2022 23:51:41 +0100 Subject: [PATCH 078/120] =?UTF-8?q?Korekturov=C3=A1tko:=20Zru=C5=A1en?= =?UTF-8?q?=C3=AD=20irelevance=20i=20z=20n=C3=A1pov=C4=9Bdy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/flat.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/flat.json b/data/flat.json index 9dba5402..75a275ad 100644 --- a/data/flat.json +++ b/data/flat.json @@ -181,7 +181,7 @@ }, { "fields": { - "content": "
\r\n

Nápověda ke korekturovátku

\r\n\r\n

Korekturovátko slouží k přidávání korektur do PDF souborů. Umožňuje přidávat a komentovat korektury a označovat je jako k zanesení, zanesené nebo irelevantní. Rovněž umožňuje o PDF říci, že jsou právě zanášeny korektury nebo že je zastaralé.

\r\n\r\n

Použití

\r\n\r\n

Kliknu do PDF tam, kam chci zadat korekturu, napíši text a kliknu na Oprav! (nebo Ctrl-Enter). Korektura se zobrazí na pravé straně červeně. Pokud chci korekturu okomentovat, kliknu na ikonu , napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod původní korekturou.

\r\n\r\n

Tlačítka u korektury

\r\n\r\n
    \r\n\t
  • – smazat korekturu
  • \r\n\t
  • – označt koreturu jako zanesenou
  • \r\n\t
  • – označit korekturu jako irelevantní (není to chyba, nebude zaneseno)
  • \r\n\t
  • – označt koreturu jako připravenou k zanesení
  • \r\n\t
  • – upravit text korektury
  • \r\n\t
  • – okomentovat korekturu
  • \r\n\t
  • – srolovat korekturu
  • \r\n
\r\n\r\n

Stavy

\r\n\r\n

Korektura

\r\n\r\n
    \r\n\t
  • K vyřešení (červená) – bug report či návrh úpravy, probíhá diskuze, zatím nerozhodnuto
  • \r\n\t
  • Zanesená (modrá) – zanesená v TeXu
  • \r\n\t
  • Irelevantní (šedá) – není to chyba, nebude zanesena
  • \r\n\t
  • K zanesení (zelená) – rozhodnuto, čeká na zanesení do TeXu
  • \r\n
\r\n\r\n

PDF

\r\n\r\n
    \r\n\t
  • Přidávání – probíhá přidávání korektur
  • \r\n\t
  • Zanášení (žluté pozadí) – probíhá zanášení korektur do TeXu
  • \r\n\t
  • Zastaralé (červené pozadí) – PDF je zastaralé, nepřidávat nové korektury
  • \r\n
\r\n
", + "content": "
\r\n

Nápověda ke korekturovátku

\r\n\r\n

Korekturovátko slouží k přidávání korektur do PDF souborů. Umožňuje přidávat a komentovat korektury a označovat je jako k zanesení, zanesené nebo že se nebude text měnit. Rovněž umožňuje o PDF říci, že jsou právě zanášeny korektury nebo že je zastaralé.

\r\n\r\n

Použití

\r\n\r\n

Kliknu do PDF tam, kam chci zadat korekturu, napíši text a kliknu na Oprav! (nebo Ctrl-Enter). Korektura se zobrazí na pravé straně červeně. Pokud chci korekturu okomentovat, kliknu na ikonu , napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod původní korekturou.

\r\n\r\n

Tlačítka u korektury

\r\n\r\n
    \r\n\t
  • – smazat korekturu
  • \r\n\t
  • – označt koreturu jako zanesenou
  • \r\n\t
  • – označit, že se text nemá měnit (není to chyba)
  • \r\n\t
  • – označt koreturu jako připravenou k zanesení
  • \r\n\t
  • – upravit text korektury
  • \r\n\t
  • – okomentovat korekturu
  • \r\n\t
  • – srolovat korekturu
  • \r\n
\r\n\r\n

Stavy

\r\n\r\n

Korektura

\r\n\r\n
    \r\n\t
  • K vyřešení (červená) – bug report či návrh úpravy, probíhá diskuze, zatím nerozhodnuto
  • \r\n\t
  • Zanesená (modrá) – zanesená v TeXu
  • \r\n\t
  • Irelevantní (šedá) – není to chyba, nebude zanesena
  • \r\n\t
  • K zanesení (zelená) – rozhodnuto, čeká na zanesení do TeXu
  • \r\n
\r\n\r\n

PDF

\r\n\r\n
    \r\n\t
  • Přidávání – probíhá přidávání korektur
  • \r\n\t
  • Zanášení (žluté pozadí) – probíhá zanášení korektur do TeXu
  • \r\n\t
  • Zastaralé (červené pozadí) – PDF je zastaralé, nepřidávat nové korektury
  • \r\n
\r\n
", "enable_comments": false, "registration_required": false, "sites": [ From c2ad4c560d5b304431ccb18249685653d85c76ce Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sat, 17 Dec 2022 00:11:00 +0100 Subject: [PATCH 079/120] =?UTF-8?q?sync=5Fprod=5Fflatpages=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/flat.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/flat.json b/data/flat.json index 75a275ad..0bb3b175 100644 --- a/data/flat.json +++ b/data/flat.json @@ -16,7 +16,7 @@ }, { "fields": { - "content": "

Co je M&M?

\r\n\r\n

M&M je mezioborový korespondenční seminář pro středoškoláky

\r\n\r\n

Ptáš se, co že je to ten korespondenční seminář? Je to jednoduché, zamysli se nad zadanou úlohou či problémem, odevzdej řešení, my ti jej opravíme a pošleme zpět. Nabízíme témata z různých oblastí matematiky, fyziky, informatiky i dalších oborů, takže si u nás určitě najdeš to svoje. Při řešení si můžeš vyzkoušet, jak vypadá vědecká práce, a mnoho zajímavého se naučit. Podívej se na tipy v sekci Jak řešit a pusť se do toho! Můžeš se připojit kdykoliv během roku.

\r\n\r\n

M&M jsou témata

\r\n\r\n

Jádro M&M tvoří takzvaná témata, tedy texty doplněné o úlohy a náměty vyzývající k přemýšlení a experimentování. Přijímáme i originální přístup k problémům, takže můžeš například vyřešit fyzikální problém naprogramováním simulace. V minulých letech řešitelé sestavovali spektrometry, pomocí laserových ukazovátek měřili index lomu různých látek, zkoumali křivky popisující vývoj počtu cestujících v tramvaji v průběhu cesty nebo programovali strategie v jednoduché hře a pak je nechávali soupeřit v turnajích. Podívej se, jaká témata nabízíme právě teď.

\r\n\r\n

M&M je časopis

\r\n\r\n

Zadání a texty od organizátorů vydáváme formou časopisu. Kromě toho zde rovněž otiskujeme řešení a příspěvky našich řešitelů k tématům, články shrnující výsledky konfer – projektů, kterými se zabývali na soustředění, i výsledky jejich vlastního výzkumu. Díky tomu jsme si například mohli přečíst o Lichtenbergových obrazcích či gravitaci v placatém světě. Během ročníku obvykle vyjde šest čísel časopisu, prohlédnout si je můžeš v Archivu.

\r\n\r\n

M&M je soutěž

\r\n\r\n

Ke všem příspěvkům posíláme řešitelům zpětnou vazbu a udělujeme za ně body. Vítězové semináře se mohou těšit na zajímavé knihy a deskové hry, autora nejlepšího otištěného článku každoročně odměňujeme dortem. Úspěšným řešitelům jsou navíc prominuty přijímací zkoušky na MFF UK.

\r\n\r\n

M&M jsou soustředění

\r\n\r\n

Aktivní řešitelé mají možnost s námi za odměnu jet dvakrát do roka na soustředění. Pojeď taky! Budeš mít možnost strávit týden v přírodě plný odborného programu a nejrůznější zábavy s partou kamarádů. Čekají tě malé vědecké projekty, přednášky, workshopy, šifrovačka, výlet a mnoho dalšího. Chceš-li se dozvědět víc, přečti si stránku Soustředění nebo se podívej na fotky.

\r\n\r\n

M&M jsme my

\r\n\r\n

Spolu s řešiteli jsme tu i my, organizátoři. Většina z nás jsou studenti Matfyzu, tedy Matematicko-fyzikální fakulty Univerzity Karlovy. Společně se snažíme předávat dál radost z poznávání zajímavých zákoutí matematiky, fyziky a informatiky a budovat komunitu aktivních lidí, kteří se budou rádi scházet, ať už na soustředěních, víkendovkách či jiných akcích. Chceš nás poznat blíž? Podívej se na stránku Organizátoři nebo rovnou něco vyřeš a pojeď s námi na soustředění!

\r\n\r\n

Zaujali jsme tě? Zapoj se! Přečti si jak řešit a podívej se na aktuální zadání. Už se těšíme na tvé příspěvky!

", + "content": "

Co je M&M?

\r\n\r\n

M&M je mezioborový korespondenční seminář pro středoškoláky

\r\n\r\n

Ptáš se, co že je to ten korespondenční seminář? Je to jednoduché, zamysli se nad zadanou úlohou či problémem, odevzdej řešení, my ti jej opravíme a pošleme zpět. Nabízíme témata z různých oblastí matematiky, fyziky, informatiky i dalších oborů, takže si u nás určitě najdeš to svoje. Při řešení si můžeš vyzkoušet, jak vypadá vědecká práce, a mnoho zajímavého se naučit. Podívej se na tipy v sekci Jak řešit a pusť se do toho! Můžeš se připojit kdykoliv během roku.

\r\n\r\n

M&M jsou témata

\r\n\r\n

Jádro M&M tvoří takzvaná témata, tedy texty doplněné o úlohy a náměty vyzývající k přemýšlení a experimentování. Přijímáme i originální přístup k problémům, takže můžeš například vyřešit fyzikální problém naprogramováním simulace. V minulých letech řešitelé sestavovali spektrometry, pomocí laserových ukazovátek měřili index lomu různých látek, zkoumali křivky popisující vývoj počtu cestujících v tramvaji v průběhu cesty nebo programovali strategie v jednoduché hře a pak je nechávali soupeřit v turnajích. Podívej se, jaká témata nabízíme právě teď.

\r\n\r\n

M&M je časopis

\r\n\r\n

Zadání a texty od organizátorů vydáváme formou časopisu. Kromě toho zde rovněž otiskujeme řešení a příspěvky našich řešitelů k tématům, články shrnující výsledky konfer – projektů, kterými se zabývali na soustředění, i výsledky jejich vlastního výzkumu. Díky tomu jsme si například mohli přečíst o Lichtenbergových obrazcích či gravitaci v placatém světě. Během ročníku obvykle vyjde šest čísel časopisu, prohlédnout si je můžeš v Archivu.

\r\n\r\n

M&M je soutěž

\r\n\r\n

Ke všem příspěvkům posíláme řešitelům zpětnou vazbu a udělujeme za ně body. Vítězové semináře se mohou těšit na zajímavé knihy a deskové hry, autora nejlepšího otištěného článku každoročně odměňujeme dortem. Úspěšným řešitelům jsou navíc prominuty přijímací zkoušky na MFF UK.

\r\n\r\n

M&M jsou soustředění

\r\n\r\n

Aktivní řešitelé mají možnost s námi za odměnu jet dvakrát do roka na soustředění. Pojeď taky! Budeš mít možnost strávit týden v přírodě plný odborného programu a nejrůznější zábavy s partou kamarádů. Čekají tě malé vědecké projekty, přednášky, workshopy, šifrovačka, výlet a mnoho dalšího. Chceš-li se dozvědět víc, přečti si stránku Soustředění nebo se podívej na fotky.

\r\n\r\n

M&M jsme my

\r\n\r\n

Spolu s řešiteli jsme tu i my, organizátoři. Většina z nás jsou studenti Matfyzu, tedy Matematicko-fyzikální fakulty Univerzity Karlovy. Společně se snažíme předávat dál radost z poznávání zajímavých zákoutí matematiky, fyziky a informatiky a budovat komunitu aktivních lidí, kteří se budou rádi scházet, ať už na soustředěních, víkendovkách či jiných akcích. Chceš nás poznat blíž? Podívej se na stránku Organizátoři nebo rovnou něco vyřeš a pojeď s námi na soustředění!

\r\n\r\n

Zaujali jsme tě? Zapoj se! Přečti si jak řešit a podívej se na aktuální zadání. Už se těšíme na tvé příspěvky!

", "enable_comments": false, "registration_required": false, "sites": [ @@ -91,7 +91,7 @@ }, { "fields": { - "content": "

Jarní soustředění

\r\n\r\n

se uskuteční 2. – 10. dubna 2022.

", + "content": "

Vánoční víkendovka

\r\n\r\n

se uskuteční 9. – 11. prosince 2022. Přihlašovat se na ni můžete do 5. prosince pomocí formuláře, ve kterém také naleznete doplňující informace.

\r\n\r\n

Jarní soustředění

\r\n\r\n

se uskuteční 25. března – 2. dubna 2023. Bližší informace sdělíme přihlášeným účastníkům.

\r\n\r\n

 

\r\n\r\n

Další akce

\r\n\r\n

Odkaz na M&M Google kalendář: https://calendar.google.com/calendar/embed?src=casopis.mam%40gmail.com&ctz=Europe%2FPrague

", "enable_comments": false, "registration_required": false, "sites": [ @@ -166,7 +166,7 @@ }, { "fields": { - "content": "

Jak řešit

\r\n\r\n

V našem semináři simulujeme vědeckou práci. Vydáváme časopis, v němž se věnujeme různým tématům z oblasti matematiky, fyziky, informatiky a občas i jiných oborů.

\r\n\r\n

Témata

\r\n\r\n

Naším cílem je vás prostřednictvím témat seznámit se zajímavými zákoutími matematiky, fyziky a informatiky a inspirovat vás, abyste sami objevovali jejich krásy. Témata většinou obsahují průvodní text od organizátorů. Na něj navazují různé otázky, nad kterými můžeš v průběhu školního roku přemýšlet a bádat. o své výsledky se pak podělíš s námi, my tvou práci ohodnotíme a pošleme ti zpětnou vazbu. Vybrané příspěvky také otiskujeme, takže si je mohou přečíst ostatní řešitelé a reagovat na ně.

\r\n\r\n

Každý ročník vypisujeme zhruba čtyři až šest témat. Pokud si mezi nimi nevybereš, můžeš si položit vlastní otázku, přemýšlet, experimentovat a o svých výsledcích nám napsat článek. Nabízíme ti pár tipů, jak jej napsat pěkně a přehledně. Autora nejlepšího otištěného článku každoročně odměňujeme dortem.

\r\n\r\n

Otázky u jednotlivých témat jsou dvou druhů. Podle toho se bude lišit i formát tvého řešení, jak je popsáno níže.

\r\n\r\n

Úlohy

\r\n\r\n

Jak vypadají?

\r\n\r\n

V rámci témat jsou zadány uzavřené úlohy. Tyto úlohy bývají trochu těžší než obvyklé školní a jejich řešení často vyžaduje hlubší zamyšlení nebo nějaký trik. k jejich vyřešení by ti však měly stačit středoškolské znalosti a pochopení průvodního textu, který tyto znalosti rozvíjí.

\r\n\r\n

Jak na to?

\r\n\r\n

Vyřeš úlohu a pošli nám řešení. Nezapomeň na postup! U každé úlohy je uveden počet bodů za správné řešení, přiměřenou část však lze získat i za řešení neúplné. Neboj se proto poslat své úvahy, i když nedojdeš ke zdárnému konci. Částečné řešení je lepší než žádné. Pokud je naopak tvé řešení velmi elegantní, můžeš získat body navíc.

\r\n\r\n

Používáš-li v řešení nějaké pojmy, které nebyly v rámci témátka definovány a nepatří mezi běžné středoškolské znalosti, nezapomeň jen krátce definovat.

\r\n\r\n

Termín odevzdání

\r\n\r\n

Odevzdávání úloh je omezené termínem odeslání, poté je zveřejněno vzorové řešení. Tím se může stát i tvé pěkně sepsané řešení.

\r\n\r\n

Problémy

\r\n\r\n

Jak vypadají?

\r\n\r\n

Témata poskytují příležitost pro rozsáhlejší zamyšlení a podrobnější rozbor zadaných otázek. Řešení proto obvykle vyžaduje o něco víc prostoru než řešení úlohy. Zároveň je v závislosti na kvalitě štědřeji bodově hodnoceno.

\r\n\r\n

Jak na to?

\r\n\r\n

Řešením problému může být popis (případně videozáznam) provedeného experimentu a diskuze výsledků, popis teoretického výpočtu či napsaného programu, úvaha založená na dosud známých informacích, souhrn dalších (podle tebe zajímavých a nezodpovězených) otázek z daného okruhu nebo jakýkoliv další přístup, který příspívá k řešení problému. Můžeš navrhnout i vlastní problém týkající se tématu a jeho řešení. Řešení konkrétního problému jdoucí do hloubky je bodově hodnoceno výrazně lépe než souhrn krátce zmiňující kdeco.

\r\n\r\n

Řešení problému můžeš sepsat jako článek nebo krátký příspěvek. Napsat článek je trochu náročnější a je tedy lépe bodově hodnocen. Nabízíme ti pár tipů, jak jej pěkně a přehledně napsat. Neboj se však k tématu poslat i méně propracovaný příspěvek, klidně jeden odstavec shrnující tvé myšlenky.

\r\n\r\n

Zaslané řešení vedoucí tématu ohodnotí, případně zkoriguje – probere s tebou, co by bylo vhodné vylepšit, doplnit či upřesnit. Na závěr článek publikujeme na webu či dokonce v čísle.

\r\n\r\n

Inspiruj se a reaguj!

\r\n\r\n

Velmi důležitou vlastností témat je, že můžeš na články ostatních reagovat – rozvíjet je, nebo naopak bořit autorovy představy. Přesně to se děje i při skutečné vědecké práci. Proto se hodí poslat také částečná řešení, postřehy nebo nápady ohledně dalších otázek k tématu, které třeba nezvládneš vyřešit sám. Takovéto příspěvky dávají prostor ostatním a mohou je dál inspirovat a přivést k novým nápadům – ostatní zase svými příspěvky mohou inspirovat či navést k zajímavému nápadu tebe.

\r\n\r\n

Termín odevzdání

\r\n\r\n

Nad problémy k tématům sice můžeš většinou přemýšlet celý rok až do termínu odevzdání poslední série úloh, ale čím dříve nám článek pošleš, tím dříve na něj ostatní budou moci zareagovat. Zároveň se ti nestane, že ti tvůj nápad někdo vyfoukne.

\r\n\r\n

Jak poslat řešení

\r\n\r\n

Řešení jednotlivých témat sepiš samostatně, aby si je mohli vedoucí rozdělit. Nezapomeň uvést své jméno a číslo tématu i jednotlivých úloh a problémů. Svá řešení odevzdej elektronicky v odevzdávátku. Textové řešení ve formátu PDF můžeš doplnit libovolnou přílohou, například videem nebo kódem. Pokud bys měl s odevzdáním nějaký problém, tak nám neváhej napsat na mam@matfyz.cz.

\r\n\r\n

Termíny odevzdání

\r\n\r\n

Každé číslo má dva deadliny pro odevzdání řešení úloh, které jsou v něm otištěné. Pokud pošleš řešení do 1. deadlinu, stihneme ti poslat opravená řešení již s následujícím číslem časopisu, ve kterém se zároveň objeví body, které jsi za toto řešení dostal.

\r\n\r\n

Když tedy v prvním čísle zašleš řešení úlohy do 1. deadlinu, pak se body za něj získané objeví ve druhém čísle. Opravené řešení ti přijde e-mailem před vydáním druhého čísla nebo v obálce s druhým číslem.

\r\n\r\n

Tvá řešení se snažíme opravit co nejdříve a rovnou ti je poslat opravená e-mailem. Pokud ti přijde opravené řešení před 2. deadlinem, máš možnost si jej ještě vylepšit a poslat nám další verzi. Čím dříve své řešení pošleš, tím větší je šance, že jej stihneme opravit s předstihem. Nemůžeme ti to však zcela slíbit.

\r\n\r\n

Úlohy poslané mezi 1. a 2. deadlinem pravděpodobně do následujícího čísla opravit nestihneme, výjimečně se to ale stát může. 2. deadline je finální a úlohy, které přijdou po něm, už nebudou hodnoceny.

\r\n\r\n

Příklad: Pokud tedy úlohu zadanou v prvním čísle pošleš mezi 1. a 2. deadlinem, opravené řešení a body typicky dostaneš až se třetím číslem.

\r\n\r\n

Články

\r\n\r\n

Pokud se rozhodneš poslat řešení formou článku, budeme rádi, pokud to uděláš co nejdřív. Může se totiž stát, že bude potřeba abychom jej před vydáním společně ještě trochu upravili. Na to se hodí, abychom měli my i ty dostatek času. Vždy záleží na konkrétním případu, ale pokud pošleš článek až po 1. deadlinu, je velmi nepravděpodobné, že by se stihl objevit už v následujícím čísle.

\r\n\r\n

Spolupracuj

\r\n\r\n

Protože víc hlav víc ví, můžeš na tématech spolupracovat s ostatními řešiteli. i opravdové vědecké práce často publikuje kolektiv autorů. Pokud se na zaslaném řešení podílí n autorů, dostane každý z nich 3b/(n + 2) bodů, kde b je počet bodů, které by řešení získalo, pokud by mělo jednoho autora.

\r\n\r\n

Ke spolupráci můžeš využít e-mailové konference. Pokud pošleš e-mail na její adresu, přijde všem vedoucím tématu a řešitelům, kteří se jím také zabývají. Pošleš-li k danému tématu alespoň nějaké řešení, automaticky tě přidáme do příslušné konference. Pokud bys chtěl do konference přidat, i když jsi zatím nic neposlal, neboj se nám ozvat.

", + "content": "

Jak řešit

\r\n\r\n

V našem semináři simulujeme vědeckou práci. Vydáváme časopis, v němž se věnujeme různým tématům z oblasti matematiky, fyziky, informatiky a občas i jiných oborů.

\r\n\r\n

Témata

\r\n\r\n

Naším cílem je vás prostřednictvím témat seznámit se zajímavými zákoutími matematiky, fyziky a informatiky a inspirovat vás, abyste sami objevovali jejich krásy. Témata většinou obsahují průvodní text od organizátorů. Na něj navazují různé otázky, nad kterými můžeš v průběhu školního roku přemýšlet a bádat. o své výsledky se pak podělíš s námi, my tvou práci ohodnotíme a pošleme ti zpětnou vazbu. Vybrané příspěvky také otiskujeme, takže si je mohou přečíst ostatní řešitelé a reagovat na ně.

\r\n\r\n

Každý ročník vypisujeme zhruba čtyři až šest témat. Pokud si mezi nimi nevybereš, můžeš si položit vlastní otázku, přemýšlet, experimentovat a o svých výsledcích nám napsat článek. Nabízíme ti pár tipů, jak jej napsat pěkně a přehledně. Autora nejlepšího otištěného článku každoročně odměňujeme dortem.

\r\n\r\n

Otázky u jednotlivých témat jsou dvou druhů. Podle toho se bude lišit i formát tvého řešení, jak je popsáno níže.

\r\n\r\n

Úlohy

\r\n\r\n

Jak vypadají?

\r\n\r\n

V rámci témat jsou zadány uzavřené úlohy. Tyto úlohy bývají trochu těžší než obvyklé školní a jejich řešení často vyžaduje hlubší zamyšlení nebo nějaký trik. k jejich vyřešení by ti však měly stačit středoškolské znalosti a pochopení průvodního textu, který tyto znalosti rozvíjí.

\r\n\r\n

Jak na to?

\r\n\r\n

Vyřeš úlohu a pošli nám řešení. Nezapomeň na postup! U každé úlohy je uveden počet bodů za správné řešení, přiměřenou část však lze získat i za řešení neúplné. Neboj se proto poslat své úvahy, i když nedojdeš ke zdárnému konci. Částečné řešení je lepší než žádné. Pokud je naopak tvé řešení velmi elegantní, můžeš získat body navíc.

\r\n\r\n

Používáš-li v řešení nějaké pojmy, které nebyly v rámci témátka definovány a nepatří mezi běžné středoškolské znalosti, nezapomeň jen krátce definovat.

\r\n\r\n

Termín odevzdání

\r\n\r\n

Odevzdávání úloh je omezené termínem odeslání, poté je zveřejněno vzorové řešení. Tím se může stát i tvé pěkně sepsané řešení.

\r\n\r\n

Problémy

\r\n\r\n

Jak vypadají?

\r\n\r\n

Témata poskytují příležitost pro rozsáhlejší zamyšlení a podrobnější rozbor zadaných otázek. Řešení proto obvykle vyžaduje o něco víc prostoru než řešení úlohy. Zároveň je v závislosti na kvalitě štědřeji bodově hodnoceno.

\r\n\r\n

Jak na to?

\r\n\r\n

Řešením problému může být popis (případně videozáznam) provedeného experimentu a diskuze výsledků, popis teoretického výpočtu či napsaného programu, úvaha založená na dosud známých informacích, souhrn dalších (podle tebe zajímavých a nezodpovězených) otázek z daného okruhu nebo jakýkoliv další přístup, který příspívá k řešení problému. Můžeš navrhnout i vlastní problém týkající se tématu a jeho řešení. Řešení konkrétního problému jdoucí do hloubky je bodově hodnoceno výrazně lépe než souhrn krátce zmiňující kdeco.

\r\n\r\n

Řešení problému můžeš sepsat jako článek nebo krátký příspěvek. Napsat článek je trochu náročnější a je tedy lépe bodově hodnocen. Nabízíme ti pár tipů, jak jej pěkně a přehledně napsat. Neboj se však k tématu poslat i méně propracovaný příspěvek, klidně jeden odstavec shrnující tvé myšlenky.

\r\n\r\n

Zaslané řešení vedoucí tématu ohodnotí, případně zkoriguje – probere s tebou, co by bylo vhodné vylepšit, doplnit či upřesnit. Na závěr článek publikujeme na webu či dokonce v čísle.

\r\n\r\n

Inspiruj se a reaguj!

\r\n\r\n

Velmi důležitou vlastností témat je, že můžeš na články ostatních reagovat – rozvíjet je, nebo naopak bořit autorovy představy. Přesně to se děje i při skutečné vědecké práci. Proto se hodí poslat také částečná řešení, postřehy nebo nápady ohledně dalších otázek k tématu, které třeba nezvládneš vyřešit sám. Takovéto příspěvky dávají prostor ostatním a mohou je dál inspirovat a přivést k novým nápadům – ostatní zase svými příspěvky mohou inspirovat či navést k zajímavému nápadu tebe.

\r\n\r\n

Termín odevzdání

\r\n\r\n

Nad problémy k tématům sice můžeš většinou přemýšlet celý rok až do termínu odevzdání poslední série úloh, ale čím dříve nám článek pošleš, tím dříve na něj ostatní budou moci zareagovat. Zároveň se ti nestane, že ti tvůj nápad někdo vyfoukne.

\r\n\r\n

Jak poslat řešení

\r\n\r\n

Řešení jednotlivých témat sepiš samostatně, aby si je mohli vedoucí rozdělit. Nezapomeň uvést své jméno a číslo tématu i jednotlivých úloh a problémů. Svá řešení odevzdej elektronicky v odevzdávátku. Textové řešení ve formátu PDF můžeš doplnit libovolnou přílohou, například videem nebo kódem. Pokud bys měl s odevzdáním nějaký problém, tak nám neváhej napsat na mam@matfyz.cz.

\r\n\r\n

Termíny odevzdání

\r\n\r\n

Každé číslo má dva deadliny pro odevzdání řešení úloh, které jsou v něm otištěné. Pokud pošleš řešení do 1. deadlinu, stihneme ti poslat opravená řešení již s následujícím číslem časopisu, ve kterém se zároveň objeví body, které jsi za toto řešení dostal.

\r\n\r\n

Když tedy v prvním čísle zašleš řešení úlohy do 1. deadlinu, pak se body za něj získané objeví ve druhém čísle. Opravené řešení ti přijde e-mailem před vydáním druhého čísla nebo v obálce s druhým číslem.

\r\n\r\n

Tvá řešení se snažíme opravit co nejdříve a rovnou ti je poslat opravená e-mailem. Pokud ti přijde opravené řešení před 2. deadlinem, máš možnost si jej ještě vylepšit a poslat nám další verzi. Čím dříve své řešení pošleš, tím větší je šance, že jej stihneme opravit s předstihem. Nemůžeme ti to však zcela slíbit.

\r\n\r\n

Úlohy poslané mezi 1. a 2. deadlinem pravděpodobně do následujícího čísla opravit nestihneme, výjimečně se to ale stát může. 2. deadline je finální a úlohy, které přijdou po něm, už nebudou hodnoceny.

\r\n\r\n

Příklad: Pokud tedy úlohu zadanou v prvním čísle pošleš mezi 1. a 2. deadlinem, opravené řešení a body typicky dostaneš až se třetím číslem.

\r\n\r\n

Články

\r\n\r\n

Pokud se rozhodneš poslat řešení formou článku, budeme rádi, pokud to uděláš co nejdřív. Může se totiž stát, že bude potřeba abychom jej před vydáním společně ještě trochu upravili. Na to se hodí, abychom měli my i ty dostatek času. Vždy záleží na konkrétním případu, ale pokud pošleš článek až po 1. deadlinu, je velmi nepravděpodobné, že by se stihl objevit už v následujícím čísle.

\r\n\r\n

Spolupracuj

\r\n\r\n

Protože víc hlav víc ví, můžeš na tématech spolupracovat s ostatními řešiteli. i opravdové vědecké práce často publikuje kolektiv autorů. Pokud se na zaslaném řešení podílí n autorů, dostane každý z nich 3b/(n + 2) bodů, kde b je počet bodů, které by řešení získalo, pokud by mělo jednoho autora.

\r\n\r\n

Ke komunikaci s ostatními řešiteli i organizátory můžeš využít náš Discord.

", "enable_comments": false, "registration_required": false, "sites": [ From 3b6e238323806b309f25cc3fe20767d6043df917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 20:43:05 +0100 Subject: [PATCH 080/120] =?UTF-8?q?Fix:=20lep=C5=A1=C3=AD=20hled=C3=A1n?= =?UTF-8?q?=C3=AD=20podle=20p=C5=99ezd=C3=ADvky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/views/autocomplete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views/autocomplete.py b/api/views/autocomplete.py index ab0114b3..10e0bca9 100644 --- a/api/views/autocomplete.py +++ b/api/views/autocomplete.py @@ -56,7 +56,7 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer ).exclude( prezdivka_resitele="" ).filter( - prezdivka_resitele__startswith=self.q + prezdivka_resitele__icontains=self.q ).all() return qs From 485c4180da3178fc3001d5c606f3d563554ace70 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 19 Dec 2022 21:09:15 +0100 Subject: [PATCH 081/120] =?UTF-8?q?make/lib:=20podpora=20pro=20zat=C3=ADm?= =?UTF-8?q?=20neexistuj=C3=ADc=C3=AD=20v=C4=9Btve?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (pokud jsem to napsal správně.) --- make/lib.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index 9dc3d6c5..0a5ff4ee 100644 --- a/make/lib.sh +++ b/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 "$REMOTE_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 "$REMOTE_BRANCH":"$SCRIPT")" then echo >&2 "Změna v $SCRIPT, prosím pullni manuálně" exit 1 From 5690dc297e7cc3a7b1b00276d736b85ff65d670c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 21:15:56 +0100 Subject: [PATCH 082/120] =?UTF-8?q?hotfix:=201b521049=20rozbil=20tabulku?= =?UTF-8?q?=20na=20=C3=BAzk=C3=A9=20obrazovce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 0eef6964..63c5f527 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -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; From 58f05724e16107cbf14cbc588d92d15aecc71144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 21:25:37 +0100 Subject: [PATCH 083/120] =?UTF-8?q?hotfix:=20485c4180=20m=C3=A1=20=C5=A1pa?= =?UTF-8?q?tn=C4=9B=20prom=C4=9Bnnou?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/lib.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/lib.sh b/make/lib.sh index 0a5ff4ee..dd56ef73 100644 --- a/make/lib.sh +++ b/make/lib.sh @@ -85,12 +85,12 @@ function safe_checkout_branch { # 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 "$REMOTE_BRANCH":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 "$REMOTE_BRANCH":"$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 From 9bb0bcf3ca9699af6288f17c7ee239a0ab3351cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 22:51:49 +0100 Subject: [PATCH 084/120] =?UTF-8?q?fix:=20p=C5=99eklep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/static/odevzdavatko/check_for_detail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odevzdavatko/static/odevzdavatko/check_for_detail.js b/odevzdavatko/static/odevzdavatko/check_for_detail.js index 3be7e1e9..b694dc88 100644 --- a/odevzdavatko/static/odevzdavatko/check_for_detail.js +++ b/odevzdavatko/static/odevzdavatko/check_for_detail.js @@ -2,7 +2,7 @@ function zkontroluj_hodnoceni() { const pocet = $('.hodnoceni').length; - if (pocet === 1) { // vydím pouze plusko + if (pocet === 1) { // vidím pouze plusko const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?"); if (!vysledek) { event.preventDefault(); From d14b6bb7996efdc889c1661851fb35e8cebf89b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 23:00:48 +0100 Subject: [PATCH 085/120] sync_prod_flatpages --- data/flat.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/flat.json b/data/flat.json index 0bb3b175..7ee6d671 100644 --- a/data/flat.json +++ b/data/flat.json @@ -91,7 +91,7 @@ }, { "fields": { - "content": "

Vánoční víkendovka

\r\n\r\n

se uskuteční 9. – 11. prosince 2022. Přihlašovat se na ni můžete do 5. prosince pomocí formuláře, ve kterém také naleznete doplňující informace.

\r\n\r\n

Jarní soustředění

\r\n\r\n

se uskuteční 25. března – 2. dubna 2023. Bližší informace sdělíme přihlášeným účastníkům.

\r\n\r\n

 

\r\n\r\n

Další akce

\r\n\r\n

Odkaz na M&M Google kalendář: https://calendar.google.com/calendar/embed?src=casopis.mam%40gmail.com&ctz=Europe%2FPrague

", + "content": "

Jarní soustředění

\r\n\r\n

se uskuteční 25. března – 2. dubna 2023. Bližší informace sdělíme přihlášeným účastníkům.

\r\n\r\n

 

\r\n\r\n

Další akce

\r\n\r\n

Odkaz na M&M Google kalendář: https://calendar.google.com/calendar/embed?src=casopis.mam%40gmail.com&ctz=Europe%2FPrague

", "enable_comments": false, "registration_required": false, "sites": [ From abdd2d65dd52a9c92a29056f33334d5619ca0159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 23:04:28 +0100 Subject: [PATCH 086/120] http -> https --- data/flat.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/flat.json b/data/flat.json index 7ee6d671..e621e266 100644 --- a/data/flat.json +++ b/data/flat.json @@ -121,7 +121,7 @@ }, { "fields": { - "content": "

Odměny

\r\n\r\n

Největší odměnou za řešení M&M je účast na soustředění, kromě toho však každý rok oceňujeme pět nejlepších řešitelů knihou a deskovou hrou dle jejich výběru. Letos můžeš být mezi nimi i ty, stačí pilně řešit!

\r\n\r\n

\"\"

\r\n\r\n

Tituly

\r\n\r\n

Za pilné řešení semináře můžeš postupně získat různé titulyMM. Titul u tvého jména v časopisu značí, jakých úspěchů jsi za celou svoji kariéru v M&M zatím dosáhl/a. Kromě toho se s jeho dosažením vždy pojí nějaká drobná či větší odměna.

\r\n\r\n
    \r\n\t
  • Bc.MM (20 bodů) – propiska
  • \r\n\t
  • Mgr.MM (50 bodů) – reflexní páska
  • \r\n\t
  • Dr.MM (100 bodů) – hrneček
  • \r\n\t
  • Doc.MM (200 bodů) – deka
  • \r\n\t
  • Prof.MM (500 bodů) – mikina
  • \r\n\t
  • Akad.MM (1000 bodů) – tabule s nápisem Jsi fakt borec podepsaná všemi organizátory
  • \r\n
\r\n\r\n

\"\"

\r\n\r\n

Přijímací zkoušky na MFF

\r\n\r\n

Matematicko-fyzikální fakulta Univerzity Karlovy se rozhodla úspěšným řešitelům našeho korespondenčního semináře odpustit přijímací zkoušky. Konkrétně se to týká těch řešitelů, kteří získají za rok alespoň 100 bodů. Ti od nás dostanou „osvědčení úspěšného řešitele“, které pak mohou předložit fakultě.

\r\n\r\n

Dort za článek

\r\n\r\n

Autorovi nebo autorům nejlepšího otištěného článku v každém ročníku upečeme lahodný dort.

\r\n\r\n

 

\r\n\r\n

\"\"

", + "content": "

Odměny

\r\n\r\n

Největší odměnou za řešení M&M je účast na soustředění, kromě toho však každý rok oceňujeme pět nejlepších řešitelů knihou a deskovou hrou dle jejich výběru. Letos můžeš být mezi nimi i ty, stačí pilně řešit!

\r\n\r\n

\"\"

\r\n\r\n

Tituly

\r\n\r\n

Za pilné řešení semináře můžeš postupně získat různé titulyMM. Titul u tvého jména v časopisu značí, jakých úspěchů jsi za celou svoji kariéru v M&M zatím dosáhl/a. Kromě toho se s jeho dosažením vždy pojí nějaká drobná či větší odměna.

\r\n\r\n
    \r\n\t
  • Bc.MM (20 bodů) – propiska
  • \r\n\t
  • Mgr.MM (50 bodů) – reflexní páska
  • \r\n\t
  • Dr.MM (100 bodů) – hrneček
  • \r\n\t
  • Doc.MM (200 bodů) – deka
  • \r\n\t
  • Prof.MM (500 bodů) – mikina
  • \r\n\t
  • Akad.MM (1000 bodů) – tabule s nápisem Jsi fakt borec podepsaná všemi organizátory
  • \r\n
\r\n\r\n

\"\"

\r\n\r\n

Přijímací zkoušky na MFF

\r\n\r\n

Matematicko-fyzikální fakulta Univerzity Karlovy se rozhodla úspěšným řešitelům našeho korespondenčního semináře odpustit přijímací zkoušky. Konkrétně se to týká těch řešitelů, kteří získají za rok alespoň 100 bodů. Ti od nás dostanou „osvědčení úspěšného řešitele“, které pak mohou předložit fakultě.

\r\n\r\n

Dort za článek

\r\n\r\n

Autorovi nebo autorům nejlepšího otištěného článku v každém ročníku upečeme lahodný dort.

\r\n\r\n

 

\r\n\r\n

\"\"

", "enable_comments": false, "registration_required": false, "sites": [ @@ -181,7 +181,7 @@ }, { "fields": { - "content": "
\r\n

Nápověda ke korekturovátku

\r\n\r\n

Korekturovátko slouží k přidávání korektur do PDF souborů. Umožňuje přidávat a komentovat korektury a označovat je jako k zanesení, zanesené nebo že se nebude text měnit. Rovněž umožňuje o PDF říci, že jsou právě zanášeny korektury nebo že je zastaralé.

\r\n\r\n

Použití

\r\n\r\n

Kliknu do PDF tam, kam chci zadat korekturu, napíši text a kliknu na Oprav! (nebo Ctrl-Enter). Korektura se zobrazí na pravé straně červeně. Pokud chci korekturu okomentovat, kliknu na ikonu , napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod původní korekturou.

\r\n\r\n

Tlačítka u korektury

\r\n\r\n
    \r\n\t
  • – smazat korekturu
  • \r\n\t
  • – označt koreturu jako zanesenou
  • \r\n\t
  • – označit, že se text nemá měnit (není to chyba)
  • \r\n\t
  • – označt koreturu jako připravenou k zanesení
  • \r\n\t
  • – upravit text korektury
  • \r\n\t
  • – okomentovat korekturu
  • \r\n\t
  • – srolovat korekturu
  • \r\n
\r\n\r\n

Stavy

\r\n\r\n

Korektura

\r\n\r\n
    \r\n\t
  • K vyřešení (červená) – bug report či návrh úpravy, probíhá diskuze, zatím nerozhodnuto
  • \r\n\t
  • Zanesená (modrá) – zanesená v TeXu
  • \r\n\t
  • Irelevantní (šedá) – není to chyba, nebude zanesena
  • \r\n\t
  • K zanesení (zelená) – rozhodnuto, čeká na zanesení do TeXu
  • \r\n
\r\n\r\n

PDF

\r\n\r\n
    \r\n\t
  • Přidávání – probíhá přidávání korektur
  • \r\n\t
  • Zanášení (žluté pozadí) – probíhá zanášení korektur do TeXu
  • \r\n\t
  • Zastaralé (červené pozadí) – PDF je zastaralé, nepřidávat nové korektury
  • \r\n
\r\n
", + "content": "
\r\n

Nápověda ke korekturovátku

\r\n\r\n

Korekturovátko slouží k přidávání korektur do PDF souborů. Umožňuje přidávat a komentovat korektury a označovat je jako k zanesení, zanesené nebo že se nebude text měnit. Rovněž umožňuje o PDF říci, že jsou právě zanášeny korektury nebo že je zastaralé.

\r\n\r\n

Použití

\r\n\r\n

Kliknu do PDF tam, kam chci zadat korekturu, napíši text a kliknu na Oprav! (nebo Ctrl-Enter). Korektura se zobrazí na pravé straně červeně. Pokud chci korekturu okomentovat, kliknu na ikonu , napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod původní korekturou.

\r\n\r\n

Tlačítka u korektury

\r\n\r\n
    \r\n\t
  • – smazat korekturu
  • \r\n\t
  • – označt koreturu jako zanesenou
  • \r\n\t
  • – označit, že se text nemá měnit (není to chyba)
  • \r\n\t
  • – označt koreturu jako připravenou k zanesení
  • \r\n\t
  • – upravit text korektury
  • \r\n\t
  • – okomentovat korekturu
  • \r\n\t
  • – srolovat korekturu
  • \r\n
\r\n\r\n

Stavy

\r\n\r\n

Korektura

\r\n\r\n
    \r\n\t
  • K vyřešení (červená) – bug report či návrh úpravy, probíhá diskuze, zatím nerozhodnuto
  • \r\n\t
  • Zanesená (modrá) – zanesená v TeXu
  • \r\n\t
  • Irelevantní (šedá) – není to chyba, nebude zanesena
  • \r\n\t
  • K zanesení (zelená) – rozhodnuto, čeká na zanesení do TeXu
  • \r\n
\r\n\r\n

PDF

\r\n\r\n
    \r\n\t
  • Přidávání – probíhá přidávání korektur
  • \r\n\t
  • Zanášení (žluté pozadí) – probíhá zanášení korektur do TeXu
  • \r\n\t
  • Zastaralé (červené pozadí) – PDF je zastaralé, nepřidávat nové korektury
  • \r\n
\r\n
", "enable_comments": false, "registration_required": false, "sites": [ From 1f16f194f7c6ea669ffc12512473f387f0f7930a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 23:12:23 +0100 Subject: [PATCH 087/120] add: Pro jistotu v DetailView i kontrola u orga --- odevzdavatko/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 3b29d397..834b7624 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -259,8 +259,9 @@ class EditReseniView(DetailReseniView): return ctx def check_access(self): - """ Na orga máme nároky už v urls.py """ - pass + # Na orga máme nároky už v urls.py ale better safe then sorry + if not self.request.user.je_org: + raise PermissionDenied() def hodnoceniReseniView(request, pk, *args, **kwargs): From abcfa996bd5473dff9ea069fc03cb835ca042ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 23:38:41 +0100 Subject: [PATCH 088/120] =?UTF-8?q?fix:=20=20->=20=20a=20zbaven?= =?UTF-8?q?=C3=AD=20se=20vyk=C5=99i=C4=8Dn=C3=ADk=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/nahraj_reseni.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html b/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html index 07529720..739340c3 100644 --- a/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html +++ b/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html @@ -13,7 +13,7 @@

Když řešení různých témátek vložíš každé zvlášť, lépe se v nich vyznáme a třeba ti je i rychleji opravíme.

-

Pokud řešíte ve více lidech, je nutné přidat tyto lidi jako „Autory řešení“! V tomto poli se vyhledává podle přezdívek, které si lze nastavit v „Osobní údaje“. Sebe vyplňovat nemusíte a za skupinu odevzdávejte pouze jednou (ne každý sám)!

+

Pokud řešíte ve více lidech, je nutné přidat tyto lidi jako „Autory řešení“. V tomto poli se vyhledává podle přezdívek, které si lze nastavit v „Osobní údaje“. Sebe vyplňovat nemusíte a za skupinu odevzdávejte pouze jednou (ne každý sám).

{% csrf_token %} From 98ac3f63bf274b9ff65d0aef1b294d446cb40b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 23:52:11 +0100 Subject: [PATCH 089/120] =?UTF-8?q?fix:=20kontrola=20duplicitn=C3=AD=20p?= =?UTF-8?q?=C5=99ezd=C3=ADvky=20p=C5=99i=20editaci=20osobn=C3=ADch=20?= =?UTF-8?q?=C3=BAdaj=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/forms.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/personalni/forms.py b/personalni/forms.py index 5f0d27f6..2946745d 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -208,11 +208,8 @@ class ProfileEditForm(forms.Form): prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') if prezdivka_resitele == '': return prezdivka_resitele - try: - Resitel.objects.get(prezdivka_resitele=prezdivka_resitele) + if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).exclude(osoba__user__username=self.username).count() > 0: raise forms.ValidationError('Přezdívka je již použita') - except ObjectDoesNotExist: - pass return prezdivka_resitele def clean_email(self): From 932ea700f38924b4eb24cb51d1d13a041cd3a010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 19 Dec 2022 23:55:24 +0100 Subject: [PATCH 090/120] =?UTF-8?q?fix:=20lep=C5=A1=C3=AD=20count=20ne?= =?UTF-8?q?=C5=BE=20try-catch=20p=C5=99i=20kontrole=20duplicitn=C3=AD=20p?= =?UTF-8?q?=C5=99ezd=C3=ADvky=20v=20p=C5=99ihl=C3=A1=C5=A1ce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/forms.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/personalni/forms.py b/personalni/forms.py index 2946745d..f9d90182 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -110,11 +110,8 @@ class PrihlaskaForm(PasswordResetForm): prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') if prezdivka_resitele == '': return prezdivka_resitele - try: - Resitel.objects.get(prezdivka_resitele=prezdivka_resitele) + if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).count() > 0: raise forms.ValidationError('Přezdívka je již použita') - except ObjectDoesNotExist: - pass return prezdivka_resitele def clean_zasilat(self): From 5dedba2937510b3896b8a8a011c4ea885fe440e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 20 Dec 2022 12:29:19 +0100 Subject: [PATCH 091/120] =?UTF-8?q?fix:=20=E2=80=9Erozl=C3=A1m=C3=A1n?= =?UTF-8?q?=C3=AD=E2=80=9C=20dlouh=C3=A9ho=20=C5=99=C3=A1dku?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index b16a5a28..a024fa07 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -12,7 +12,7 @@

Řešené problémy: {{ object.problem.all | join:", " }}

-

Řešitelé: {% for r in object.resitele.all %}{{ r }}{% if edit %}({{ r.osoba.email }}){% endif %}{% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

+

Řešitelé: {% for r in object.resitele.all %}{{ r }}{# mailto -> #}{% if edit %} ({{ r.osoba.email }}){% endif %}{# <- mailto #}{# čárky -> #}{% if forloop.revcounter0 != 0 %}, {% endif %}{# <- čárky #}{% endfor %}

{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}

Forma: {{ object.get_forma_display }}

From 0b835ef9d496991b070bd795efc748a848ab473f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 20 Dec 2022 12:40:55 +0100 Subject: [PATCH 092/120] =?UTF-8?q?fix:=20=C5=99e=C5=A1itel=C3=A9=20maj?= =?UTF-8?q?=C3=AD=20b=C3=BDt=20vid=C4=9Bt=20jen=20ti,=20kte=C5=99=C3=AD=20?= =?UTF-8?q?neodmaturovali?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/views/autocomplete.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/views/autocomplete.py b/api/views/autocomplete.py index 10e0bca9..601f4e35 100644 --- a/api/views/autocomplete.py +++ b/api/views/autocomplete.py @@ -51,7 +51,10 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer především v odevzdávátku. """ def get_queryset(self): + letos = m.Nastaveni.get_solo().aktualni_rocnik qs = m.Resitel.objects.filter( + rok_maturity__gte=letos.druhy_rok() + ).filter( prezdivka_resitele__isnull=False ).exclude( prezdivka_resitele="" From f2764a26f399a88bc0d91133e77f03dacd72a9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 20 Dec 2022 14:26:43 +0100 Subject: [PATCH 093/120] =?UTF-8?q?fix:=20je=C5=A1t=C4=9B=20jeden=20n?= =?UTF-8?q?=C3=A1vrh=20na=20zal=C3=A1m=C3=A1n=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index a024fa07..324de443 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -12,7 +12,13 @@

Řešené problémy: {{ object.problem.all | join:", " }}

-

Řešitelé: {% for r in object.resitele.all %}{{ r }}{# mailto -> #}{% if edit %} ({{ r.osoba.email }}){% endif %}{# <- mailto #}{# čárky -> #}{% if forloop.revcounter0 != 0 %}, {% endif %}{# <- čárky #}{% endfor %}

+{% if edit %} +

Řešitelé: + {% for r in object.resitele.all %}{{ r }}({{ r.osoba.email }}){% if forloop.revcounter0 != 0 %}, {% endif %}{% endfor %} +

+{% else %} +

Řešitelé: {{ object.resitele.all | join:", " }}

+{% endif %} {# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}

Forma: {{ object.get_forma_display }}

From 09e2df75ce20de22894fd5b3c3dc9af541400b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 20 Dec 2022 14:29:04 +0100 Subject: [PATCH 094/120] =?UTF-8?q?fix:=20mezera=20p=C5=99ed=20z=C3=A1vork?= =?UTF-8?q?ou?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 324de443..06f69609 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -14,7 +14,7 @@ {% if edit %}

Řešitelé: - {% for r in object.resitele.all %}{{ r }}({{ r.osoba.email }}){% if forloop.revcounter0 != 0 %}, {% endif %}{% endfor %} + {% for r in object.resitele.all %}{{ r }} ({{ r.osoba.email }}){% if forloop.revcounter0 != 0 %}, {% endif %}{% endfor %}

{% else %}

Řešitelé: {{ object.resitele.all | join:", " }}

From e10a8e0b6d13386ec860373e5950836c5e16c944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 2 Jan 2023 20:46:05 +0100 Subject: [PATCH 095/120] =?UTF-8?q?fix:=20=C5=99azen=C3=AD=20=C5=99e=C5=A1?= =?UTF-8?q?itel=C5=AF=20v=20adminu=20podle=20p=C5=99=C3=ADjmen=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/personalni/admin.py b/personalni/admin.py index 8befe589..fc3cadd4 100644 --- a/personalni/admin.py +++ b/personalni/admin.py @@ -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'] From ea7075f707bcc74a28379232fad381ed10e145ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 2 Jan 2023 21:09:56 +0100 Subject: [PATCH 096/120] =?UTF-8?q?fix:=20=C5=99azen=C3=AD=20=C5=99e=C5=A1?= =?UTF-8?q?itel=C5=AF=20v=20odm=C4=9Bn=C3=A1ch=20podle=20p=C5=99=C3=ADjmen?= =?UTF-8?q?=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/views/views_all.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 3f2cdf01..01c561eb 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -450,9 +450,9 @@ class OdmenyView(generic.TemplateView): 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: From 3c3047b5486b2e696853de318e94d74497f7a8b9 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 2 Jan 2023 21:13:15 +0100 Subject: [PATCH 097/120] =?UTF-8?q?Autodoc:=20Ignorov=C3=A1n=C3=AD=20modul?= =?UTF-8?q?=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/conf.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index d597faf2..038e7428 100644 --- a/docs/conf.py +++ b/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) From 5977f472c78d1342ae8146d7f5ce2f90b68d0253 Mon Sep 17 00:00:00 2001 From: MaM Web user Date: Mon, 2 Jan 2023 21:18:03 +0100 Subject: [PATCH 098/120] Fix! --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 038e7428..75bca8d3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -89,7 +89,7 @@ source_suffix = { # 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): +def ignorovat(app, what, name, obj, skip, options): blacklist = ( # typ (what), name ('module', 'settings.mamweb_prod'), From 72b72899fdbf1423b2b6c753c0d0c07d67aae8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 2 Jan 2023 21:20:00 +0100 Subject: [PATCH 099/120] =?UTF-8?q?fix:=20odm=C4=9Bny=20maj=C3=AD=20h?= =?UTF-8?q?=C3=A1zet=20alespo=C5=88=20404=20na=20=C5=A1patn=C3=A9=20=C4=8D?= =?UTF-8?q?=C3=ADslo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/views/views_all.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 01c561eb..16f8ae55 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -442,8 +442,8 @@ 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): From 246f63d6e02b0a283f7ce80091316ffd6ca5b73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 2 Jan 2023 21:40:07 +0100 Subject: [PATCH 100/120] =?UTF-8?q?add:=20typov=C3=A1=20anotace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vysledkovky/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vysledkovky/utils.py b/vysledkovky/utils.py index f363abfd..3ff59fb1 100644 --- a/vysledkovky/utils.py +++ b/vysledkovky/utils.py @@ -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() From c76fcb363aeb5948f35e39ebf63399b821ee44d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 2 Jan 2023 21:52:19 +0100 Subject: [PATCH 101/120] =?UTF-8?q?fix:=20=E2=80=A6.objects.get(=E2=80=A6)?= =?UTF-8?q?=20->=20get=5For=5F404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/views.py | 2 +- seminar/views/views_all.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 664482bd..0100ef24 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -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(): diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 16f8ae55..4627989e 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -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) ) ) @@ -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) From a666a3fea454140159c3c9d08f1b3e50f18dccb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 3 Jan 2023 19:04:45 +0100 Subject: [PATCH 102/120] =?UTF-8?q?hotfix:=20nemohl=20odevzd=C3=A1vat=20?= =?UTF-8?q?=C5=99e=C5=A1itel=20samotn=C3=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/forms.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/odevzdavatko/forms.py b/odevzdavatko/forms.py index a31122dd..223d807f 100644 --- a/odevzdavatko/forms.py +++ b/odevzdavatko/forms.py @@ -82,6 +82,13 @@ class NahrajReseniForm(forms.ModelForm): ) } + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # FIXME Z nějakého důvodu se do této třídy dostaneme i bez resitele + if 'resitele' in self.fields: + # FIXME Mnohem hezčí by to bylo u definice resitele výše, ale nepodařilo se mi to. + self.fields['resitele'].required = False + ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, form = NahrajReseniForm, fields = ('soubor','res_poznamka'), From af6628367f546a6a2264d8ae5e5dc57666d017da Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 4 Jan 2023 01:51:02 +0100 Subject: [PATCH 103/120] =?UTF-8?q?Testy:=20Schov=C3=A1v=C3=A1me=20si=20da?= =?UTF-8?q?tab=C3=A1zi=20nap=C5=99=C3=AD=C4=8D=20b=C4=9Bhy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Při běžení testů nejdéle trvá namigrovat (prázdnou) databázi. Toto tento krok přeskočí. By default django pro testy používá in-memory SQLite3 databázi, která se schovat přirozeně nedá. Používání souborů trvá déle (data níž), ale další běhy už jsou rychlé. Zatím nevím, jestli se někde nemůže omylem schovávat nějaký nežádoucí stav, ale testy mi i opakovaně běží, takže se to asi nerozbíjí úplně moc. Na první pohled je uložená databáze prázdná. Pro produkci a CI bych klidně běžel testy od nuly, tam nevadí čekat pár desítek sekund až jednotky minut na výsledek. Tato optimalizace je důležitá jen pro lokální vývoj, kde je žádoucí mít testy co nejrychlejší. V .gitignore už přesně toto jméno souboru je. Nevím proč, ale možná to tak bylo by default v některém dávném Djangu. Data --- Spouštěl jsem příkaz `time ./manage.py test [--keepdb] api`. Běhy byly relativně konzistentní (±1 s) a trvaly u mě: - In memory SQLite (default): 26 s - První spuštění s db na disku (HDD): 44 s - Následná spuštění: 7.7 s Data jsou nejspíš zkreslena tím, že všechno je nejspíš nacachované v systému, ale i tak je vidět zřetelné zrychlení. Původní motivace: úplně triviální a nedatabázový test na starém notebooku běžel kolem 3:14, což je zoufale nepoužitelné když si chci napsat testy jako pomůcku pro vývoj. --- make/test | 2 +- mamweb/settings_local.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/make/test b/make/test index 155d03fa..e3fa9f52 100755 --- a/make/test +++ b/make/test @@ -6,4 +6,4 @@ set -exuo pipefail ensure_web_installed trap - ERR # Testy nejspíš selžou, ale nechceme kolem toho dělat další chybovou hlášku. -./manage.py test -v2 +./manage.py test -v2 --keepdb diff --git a/mamweb/settings_local.py b/mamweb/settings_local.py index 540c0453..4d98cd38 100644 --- a/mamweb/settings_local.py +++ b/mamweb/settings_local.py @@ -40,7 +40,10 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db-local.sqlite3'), - } + 'TEST': { + 'NAME': os.path.join(BASE_DIR, 'db-test.sqlite3'), + }, + }, } #DATABASES = { # 'default': { From 3a1aade3a2bd215510b361eeb19c88fb2454628a Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 4 Jan 2023 02:11:15 +0100 Subject: [PATCH 104/120] Testy: bereme parametry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Umožňuje testovat konkrétní moduly a nespouštět vždy všechno. --- make/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/test b/make/test index e3fa9f52..45de21f1 100755 --- a/make/test +++ b/make/test @@ -6,4 +6,4 @@ set -exuo pipefail ensure_web_installed trap - ERR # Testy nejspíš selžou, ale nechceme kolem toho dělat další chybovou hlášku. -./manage.py test -v2 --keepdb +./manage.py test -v2 --keepdb "$@" From e6b8998edd4a67dbd9b72e6145bffa7445af0ad0 Mon Sep 17 00:00:00 2001 From: MaM Web user Date: Wed, 4 Jan 2023 21:30:52 +0100 Subject: [PATCH 105/120] =?UTF-8?q?Jid=C3=A1=C5=A1:=20hotfix:=20smaz=C3=A1?= =?UTF-8?q?n=C3=AD=20p=C5=99ezd=C3=ADvky=20v=20osobn=C3=ADch=20=C3=BAdaj?= =?UTF-8?q?=C3=ADch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/personalni/views.py b/personalni/views.py index d7d52540..a52f7f5f 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -160,7 +160,7 @@ def resitelEditView(request): if resitel_edit: ## Změny v řešiteli - resitel_edit.prezdivka_resitele = fcd['prezdivka_resitele'] + resitel_edit.prezdivka_resitele = fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != '' else None resitel_edit.skola = fcd['skola'] resitel_edit.rok_maturity = fcd['rok_maturity'] resitel_edit.zasilat = fcd['zasilat'] From 6994db438bd0977f1f9051627b8b0255af29629e Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 5 Jan 2023 04:40:16 +0100 Subject: [PATCH 106/120] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20tagu=20{%=20ma?= =?UTF-8?q?illink=20%}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vyrábí odkazy, které vedou na poslání mailu. Psal jsem to spíš po paměti, nejsem si jistý, že to takhle je přesně podle příslušného RFC, ale jako PoC dobrý a když to nebude fungovat, tak se implementace opraví. Všimněte si, že to je otestované, takže když někdo opraví testy (=předpis chování), tak je pak snadné z diffu a všeho odvodit úpravu. V Django dokumentaci se píše něco o tom, že by se měl použít spíš `format_html` a `conditional_escape`, ale zatím jsem to víc nezkoumal. Je žádoucí z tagu {% maillink %} odddělit i tag {% mailurl %}, který by vracel samotnou URL. Obojí dává smysl umět (speciálně bastlení odkazů z URL je stejně strašně nepřehledné, takže je lepší to zavřít do {% maillink %} a nikdy nevidět), ale zatím to oddělené není… (Ale jsou na to testy, takže by se mělo aspoň dát poznat, že rozdělení nerozbije chování.) --- various/templatetags/__init__.py | 0 various/templatetags/mail.py | 30 ++++++++++++++++++++++ various/tests.py | 43 +++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 various/templatetags/__init__.py create mode 100644 various/templatetags/mail.py diff --git a/various/templatetags/__init__.py b/various/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/various/templatetags/mail.py b/various/templatetags/mail.py new file mode 100644 index 00000000..fe11d218 --- /dev/null +++ b/various/templatetags/mail.py @@ -0,0 +1,30 @@ +from django import template +from django.utils.safestring import mark_safe +from urllib.request import quote as urlencode +register = template.Library() + +@register.simple_tag +def maillink(text: str, subject=None, body=None, to=[], attrs=None): + """TODO: Dokumentace""" + if isinstance(to, str): + to = [to] + assert isinstance(to, list) + parts = [ + f'mailto:{str.join(",", to)}', + ] + if len(to) < 1: + raise ValueError('Cannot mail to empty set of people') + + if subject: + parts.append(f'subject={urlencode(subject)}') + if body: + parts.append(f'body={urlencode(body)}') + + if len(parts) > 1: + url = parts[0] + '?' + str.join('&', parts[1:]) + else: + url = parts[0] + if not attrs: attrs = '' + mezera = ' '*bool(attrs) + full_link = f'{text}' + return mark_safe(full_link) diff --git a/various/tests.py b/various/tests.py index 7ce503c2..7884e618 100644 --- a/various/tests.py +++ b/various/tests.py @@ -1,3 +1,44 @@ from django.test import TestCase +# TODO: Možná vyrobit separátní soubory v tests/… než mít všechny testy v jednom souboru? +from various.templatetags.mail import maillink -# Create your tests here. +class MailTagsTest(TestCase): + """Testuje template tagy ohledně mailů.""" + def test_maillink(self): + # Tohle nedává smysl dělit do víc funkcí, bylo by v nich víc boilerplatu než užitečného kódu. + self.assertEquals(maillink('Hello', to='some@body.test'), r'Hello') + self.assertEquals(maillink('Hello', to=['some@body.test']), r'Hello') + self.assertEquals( + maillink('Hello', to=['alice@test.test', 'bob@jinde.test']), + r'Hello', + ) + self.assertEquals( + maillink('Hello', to='some@body.test', attrs='class="trida" id="id"'), + r'Hello', + ) + # Následující test toho testuje moc zároveň, měly by předcházet dedikované testy… (kašlu na ně :-P) + self.assertEquals( + maillink('Text odkazu', to='prijemce@wtf.test', subject="Předmět", body="Čau"), + r'Text odkazu', + ) + self.assertRaises(ValueError, lambda: maillink('Nemám příjemce')) + self.assertRaises(TypeError, lambda: maillink()) # Nemá text, takže to shodí python + + def test_render_in_template(self): + # Pomocná funkce: vykreslí template do stringu + # Ref: https://stackoverflow.com/a/1690879 + def render_template(template, context=None): + from django.template import Template, Context + context = context or {} + context = Context(context) + return Template(template).render(context) + + template=( + r'{% load mail %}' + # TODO: Vyzkoušet i víc adresátů. (Nepamatuji si z hlavy syntaxi…) + r'{% maillink "Text" to="alice@test.test" subject="Oprava řešení" %}' + ) + self.assertEquals( + render_template(template), + r'Text', + ) From ff996c2924ee8d0612a5326f4c0b03763763cf97 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 8 Jan 2023 08:51:01 +0100 Subject: [PATCH 107/120] =?UTF-8?q?P=C5=99ejmenov=C3=A1n=C3=AD=20hodnocen?= =?UTF-8?q?=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Označení bylo zavádějící, protože se vůbec nejedná o objekt Hodnocení. Neříkám, že nové jméno je nějak úchvatné, ale aspoň mě nemate a na proměnnou s životností dva řádky je to stejně jedno… --- odevzdavatko/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 0100ef24..e983860a 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -235,8 +235,8 @@ class DetailReseniView(DetailView): def get_context_data(self, **kw): self.check_access() ctx = super().get_context_data(**kw) - hodnoceni = self.aktualni_hodnoceni() - ctx["hodnoceni"] = hodnoceni + detaily_hodnoceni = self.aktualni_hodnoceni() + ctx["hodnoceni"] = detaily_hodnoceni return ctx def get(self, request, *args, **kwargs): From efe1b4bb5a47de3d749f1bdfc7cedac9f2150a66 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 8 Jan 2023 08:52:01 +0100 Subject: [PATCH 108/120] =?UTF-8?q?Pou=C5=BEit=C3=AD=20{%maillink%}=20v=20?= =?UTF-8?q?detailu=20=C5=99e=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten řádek má sice pořád 117 znaků, ale IMHO je to o dost lepší. Mně to i správně vyplňuje subjecty v Thunderbirdu, takže můj kód asi není úplně mimo :-) --- odevzdavatko/templates/odevzdavatko/detail.html | 6 +++++- odevzdavatko/views.py | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 06f69609..379bdc68 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -1,6 +1,7 @@ {% extends "base.html" %} {% load static %} {% load deadliny %} +{% load mail %} {% block content %} @@ -14,7 +15,10 @@ {% if edit %}

Řešitelé: - {% for r in object.resitele.all %}{{ r }} ({{ r.osoba.email }}){% if forloop.revcounter0 != 0 %}, {% endif %}{% endfor %} + {% for r in object.resitele.all %} + {{ r }} + ({% maillink r.osoba.email to=r.osoba.email subject=mailsubject %}){% if forloop.revcounter0 != 0 %}, {% endif %} + {% endfor %}

{% else %}

Řešitelé: {{ object.resitele.all | join:", " }}

diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index e983860a..3100eb9c 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -237,6 +237,9 @@ class DetailReseniView(DetailView): ctx = super().get_context_data(**kw) detaily_hodnoceni = self.aktualni_hodnoceni() ctx["hodnoceni"] = detaily_hodnoceni + + # Subject případného mailu (template neumí použitelně spojovat řetězce: https://stackoverflow.com/q/4386168) + ctx["mailsubject"] = "Oprava řešení M&M "+self.reseni.problem.first().hlavni_problem.nazev return ctx def get(self, request, *args, **kwargs): From 4623adb249974a09e341acef2b29083ca1404b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 24 Jan 2023 20:57:32 +0100 Subject: [PATCH 109/120] =?UTF-8?q?fix:=20od=C5=99=C3=A1dkov=C3=A1n=C3=AD?= =?UTF-8?q?=20ve=20feedbacku=20v=20hodnot=C3=ADku?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 06f69609..f1591725 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -90,7 +90,7 @@ {{ h.problem }} {{ h.body }} - {{ h.feedback }} + {{ h.feedback | linebreaks }} {% endfor %} From fad2a1bc47d5848baa2093e0e5edbc45846fb6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 30 Jan 2023 20:27:42 +0100 Subject: [PATCH 110/120] =?UTF-8?q?Nikam=20->=20Nezas=C3=ADlat=20pap=C3=AD?= =?UTF-8?q?rov=C4=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0111_nikam2nezasilat_papirove.py | 18 ++++++++++++++++++ seminar/models/personalni.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 seminar/migrations/0111_nikam2nezasilat_papirove.py diff --git a/seminar/migrations/0111_nikam2nezasilat_papirove.py b/seminar/migrations/0111_nikam2nezasilat_papirove.py new file mode 100644 index 00000000..683d71d7 --- /dev/null +++ b/seminar/migrations/0111_nikam2nezasilat_papirove.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2023-01-30 19:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0110_resitel_prezdivka'), + ] + + operations = [ + migrations.AlterField( + model_name='resitel', + name='zasilat', + field=models.CharField(choices=[('domu', 'Domů'), ('do_skoly', 'Do školy'), ('nikam', 'Nezasílat papírově')], default='domu', max_length=32, verbose_name='kam zasílat'), + ), + ] diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py index 3e64af45..9ac85e51 100644 --- a/seminar/models/personalni.py +++ b/seminar/models/personalni.py @@ -229,7 +229,7 @@ class Resitel(SeminarModelBase): ZASILAT_CHOICES = [ (ZASILAT_DOMU, 'Domů'), (ZASILAT_DO_SKOLY, 'Do školy'), - (ZASILAT_NIKAM, 'Nikam'), + (ZASILAT_NIKAM, 'Nezasílat papírově'), ] zasilat = models.CharField('kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU) From 3dab39645029e9651ffbef0c8174a6b8d19d5d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 30 Jan 2023 20:58:37 +0100 Subject: [PATCH 111/120] =?UTF-8?q?E-mail=20p=C5=99ipom=C3=ADnaj=C3=ADc?= =?UTF-8?q?=C3=AD=20=C5=99e=C5=A1itel=C5=AFm=20mo=C5=BEnost=20Nezas=C3=ADl?= =?UTF-8?q?at=20pap=C3=ADrov=C4=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models/tvorba.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py index 7e04b213..f2c10e3e 100644 --- a/seminar/models/tvorba.py +++ b/seminar/models/tvorba.py @@ -270,21 +270,27 @@ class Cislo(SeminarModelBase): 'Vaše M&M\n'.format(odkaz) # Prijemci e-mailu - emaily = map(lambda r: r.osoba.email, filter(lambda r: r.zasilat_cislo_emailem, aktivniResitele(self))) - - if not settings.POSLI_MAILOVOU_NOTIFIKACI: - print("Poslal bych upozornění na tyto adresy: ", " ".join(emaily)) - return + resitele_vsichni = aktivniResitele(self).filter(zasilat_cislo_emailem=True) + + def posli(text, resitele): + emaily = map(lambda resitel: resitel.osoba.email, resitele) + if not settings.POSLI_MAILOVOU_NOTIFIKACI: + print("Poslal bych upozornění na tyto adresy: ", " ".join(emaily)) + return + + email = EmailMessage( + subject=predmet, + body=text, + from_email=poslat_z_mailu, + bcc=list(emaily) + #bcc = příjemci skryté kopie + ) - email = EmailMessage( - subject=predmet, - body=text_mailu, - from_email=poslat_z_mailu, - bcc=list(emaily) - #bcc = příjemci skryté kopie - ) + email.send() - email.send() + posli(text_mailu, resitele_vsichni.filter(zasilat=pm.Resitel.ZASILAT_NIKAM)) + posli(text_mailu + 'P. S. Také by vám brzy měla přijít papírová verze. Připomínáme, že pokud papírovou verzi čísla nevyužijete, můžete v https://mam.mff.cuni.cz/resitel/osobni-udaje/ zaškrtnout, abychom vám ji neposílali. Děkujeme. (Čísla vždy můžete nalézt v našem archivu a dál vám budou chodit e-mailem.)\n', + resitele_vsichni.exclude(zasilat=pm.Resitel.ZASILAT_NIKAM)) def save(self, *args, **kwargs): super().save(*args, **kwargs) From 0956b0780aa56a028c8bc6a3b295436aab28ddda Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 6 Feb 2023 20:13:29 +0100 Subject: [PATCH 112/120] =?UTF-8?q?Odd=C4=9Blen=C3=AD=20tagu=20{%mailurl%}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- various/templatetags/mail.py | 7 ++++++- various/tests.py | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/various/templatetags/mail.py b/various/templatetags/mail.py index fe11d218..972040f6 100644 --- a/various/templatetags/mail.py +++ b/various/templatetags/mail.py @@ -4,7 +4,7 @@ from urllib.request import quote as urlencode register = template.Library() @register.simple_tag -def maillink(text: str, subject=None, body=None, to=[], attrs=None): +def mailurl(*, subject=None, body=None, to=[]): """TODO: Dokumentace""" if isinstance(to, str): to = [to] @@ -24,6 +24,11 @@ def maillink(text: str, subject=None, body=None, to=[], attrs=None): url = parts[0] + '?' + str.join('&', parts[1:]) else: url = parts[0] + return url + +@register.simple_tag +def maillink(text, subject=None, body=None, to=[], attrs=None): + url = mailurl(subject=subject, body=body, to=to) if not attrs: attrs = '' mezera = ' '*bool(attrs) full_link = f'{text}' diff --git a/various/tests.py b/various/tests.py index 7884e618..0abf4e26 100644 --- a/various/tests.py +++ b/various/tests.py @@ -1,6 +1,6 @@ from django.test import TestCase # TODO: Možná vyrobit separátní soubory v tests/… než mít všechny testy v jednom souboru? -from various.templatetags.mail import maillink +from various.templatetags.mail import maillink, mailurl class MailTagsTest(TestCase): """Testuje template tagy ohledně mailů.""" @@ -24,6 +24,16 @@ class MailTagsTest(TestCase): self.assertRaises(ValueError, lambda: maillink('Nemám příjemce')) self.assertRaises(TypeError, lambda: maillink()) # Nemá text, takže to shodí python + def test_mailurl(self): + self.assertEquals(mailurl(to='some@body.test'), r'mailto:some@body.test') + self.assertEquals(mailurl(to=['some@body.test']), r'mailto:some@body.test') + self.assertEquals(mailurl(to=['alice@test.test', 'bob@jinde.test']), r'mailto:alice@test.test,bob@jinde.test') + self.assertEquals( + mailurl(to='some@body.test', body='Tělo', subject='Předmět'), + r'mailto:some@body.test?subject=P%C5%99edm%C4%9Bt&body=T%C4%9Blo', + ) + self.assertRaises(ValueError, lambda: mailurl()) + def test_render_in_template(self): # Pomocná funkce: vykreslí template do stringu # Ref: https://stackoverflow.com/a/1690879 @@ -33,7 +43,7 @@ class MailTagsTest(TestCase): context = Context(context) return Template(template).render(context) - template=( + template = ( r'{% load mail %}' # TODO: Vyzkoušet i víc adresátů. (Nepamatuji si z hlavy syntaxi…) r'{% maillink "Text" to="alice@test.test" subject="Oprava řešení" %}' @@ -42,3 +52,9 @@ class MailTagsTest(TestCase): render_template(template), r'Text', ) + + mailurltemplate = ( + r'{% load mail %}' + r'{% mailurl to="alice@test.test" subject="Čau Alice" %}' + ) + self.assertEquals(render_template(mailurltemplate), r'mailto:alice@test.test?subject=%C4%8Cau%20Alice') From 65cd15ecbbc972e3b6d5642c1c3404396fe6617c Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 6 Feb 2023 20:27:19 +0100 Subject: [PATCH 113/120] =?UTF-8?q?Koment=C3=A1=C5=99=20k=20tomu,=20kde=20?= =?UTF-8?q?se=20vyr=C3=A1b=C3=AD=20mailsubject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 1 + 1 file changed, 1 insertion(+) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 379bdc68..74352509 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -17,6 +17,7 @@

Řešitelé: {% for r in object.resitele.all %} {{ r }} + {# DjangoTemplates neumí spojovat řetězce (https://stackoverflow.com/q/4386168), tak si necháváme vyrobit subject mailu ve view. #} ({% maillink r.osoba.email to=r.osoba.email subject=mailsubject %}){% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

From f6cb669277d2143338b67f64475e804a85f9378b Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 6 Feb 2023 20:27:35 +0100 Subject: [PATCH 114/120] =?UTF-8?q?P=C5=99ejmenov=C3=A1n=C3=AD=20mailsubje?= =?UTF-8?q?ctu=20do=20=C4=8De=C5=A1tiny?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/templates/odevzdavatko/detail.html | 2 +- odevzdavatko/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 74352509..468b0322 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -18,7 +18,7 @@ {% for r in object.resitele.all %} {{ r }} {# DjangoTemplates neumí spojovat řetězce (https://stackoverflow.com/q/4386168), tak si necháváme vyrobit subject mailu ve view. #} - ({% maillink r.osoba.email to=r.osoba.email subject=mailsubject %}){% if forloop.revcounter0 != 0 %}, {% endif %} + ({% maillink r.osoba.email to=r.osoba.email subject=predmetmailu %}){% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

{% else %} diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 3100eb9c..57822bf4 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -239,7 +239,7 @@ class DetailReseniView(DetailView): ctx["hodnoceni"] = detaily_hodnoceni # Subject případného mailu (template neumí použitelně spojovat řetězce: https://stackoverflow.com/q/4386168) - ctx["mailsubject"] = "Oprava řešení M&M "+self.reseni.problem.first().hlavni_problem.nazev + ctx["predmetmailu"] = "Oprava řešení M&M "+self.reseni.problem.first().hlavni_problem.nazev return ctx def get(self, request, *args, **kwargs): From 04c3c6257cfee4dd9723a47db425d4a3ef237369 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 6 Feb 2023 21:56:06 +0100 Subject: [PATCH 115/120] =?UTF-8?q?Podpora=20cc=20a=20bcc=20v=20{%maillink?= =?UTF-8?q?%}=20[neotestov=C3=A1no]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/odevzdavatko/detail.html | 3 +++ odevzdavatko/views.py | 1 + various/templatetags/mail.py | 23 +++++++++++++++---- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 468b0322..73265563 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -21,6 +21,9 @@ ({% maillink r.osoba.email to=r.osoba.email subject=predmetmailu %}){% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}

+

+ {% maillink "Poslat mail všem řešitelům" bcc=maily_vsech_resitelu subject=predmetmailu %} +

{% else %}

Řešitelé: {{ object.resitele.all | join:", " }}

{% endif %} diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 57822bf4..9ac1ac29 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -240,6 +240,7 @@ class DetailReseniView(DetailView): # Subject případného mailu (template neumí použitelně spojovat řetězce: https://stackoverflow.com/q/4386168) ctx["predmetmailu"] = "Oprava řešení M&M "+self.reseni.problem.first().hlavni_problem.nazev + ctx["maily_vsech_resitelu"] = [y for x in self.reseni.resitele.all().values_list('osoba__email') for y in x] return ctx def get(self, request, *args, **kwargs): diff --git a/various/templatetags/mail.py b/various/templatetags/mail.py index 972040f6..ecbb2a39 100644 --- a/various/templatetags/mail.py +++ b/various/templatetags/mail.py @@ -4,21 +4,34 @@ from urllib.request import quote as urlencode register = template.Library() @register.simple_tag -def mailurl(*, subject=None, body=None, to=[]): - """TODO: Dokumentace""" +def mailurl(*, subject=None, body=None, to=[], cc=[], bcc=[]): + """Tag na vytváření správně zakódované mailto: adresy + + Ref: RFC 6068, """ if isinstance(to, str): to = [to] + if isinstance(cc, str): + cc = [cc] + if isinstance(bcc, str): + bcc = [bcc] assert isinstance(to, list) + assert isinstance(cc, list) + assert isinstance(bcc, list) + # FIXME: adresa není správně zakódovaná, rozbije se to na adresách s divnými znaky parts = [ f'mailto:{str.join(",", to)}', ] - if len(to) < 1: + if len(to) + len(cc) + len(bcc) < 1: raise ValueError('Cannot mail to empty set of people') if subject: parts.append(f'subject={urlencode(subject)}') if body: parts.append(f'body={urlencode(body)}') + if len(cc) > 0: + parts.append(f'cc={str.join(",", cc)}') + if len(bcc) > 0: + parts.append(f'bcc={str.join(",", bcc)}') if len(parts) > 1: url = parts[0] + '?' + str.join('&', parts[1:]) @@ -27,8 +40,8 @@ def mailurl(*, subject=None, body=None, to=[]): return url @register.simple_tag -def maillink(text, subject=None, body=None, to=[], attrs=None): - url = mailurl(subject=subject, body=body, to=to) +def maillink(text, subject=None, body=None, to=[], cc=[], bcc=[], attrs=None): + url = mailurl(subject=subject, body=body, to=to, cc=cc, bcc=bcc) if not attrs: attrs = '' mezera = ' '*bool(attrs) full_link = f'{text}' From fdbbc9c242a68d8c2926f0ba60b1730c054483dd Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 6 Feb 2023 21:58:16 +0100 Subject: [PATCH 116/120] =?UTF-8?q?=C3=9Aprava=20testu=20na=20autocomplete?= =?UTF-8?q?,=20aby=20nefailoval?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … moc nahlas. --- api/tests/test_skola_autocomplete.py | 31 +++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/api/tests/test_skola_autocomplete.py b/api/tests/test_skola_autocomplete.py index 91e5a82b..a469e338 100644 --- a/api/tests/test_skola_autocomplete.py +++ b/api/tests/test_skola_autocomplete.py @@ -3,6 +3,7 @@ from django.urls import reverse import seminar.models as m import seminar.views as v from seminar.utils import sync_skoly +from unittest import expectedFailure class OrgSkolyAutocompleteTestCase(TestCase): @classmethod @@ -14,16 +15,16 @@ class OrgSkolyAutocompleteTestCase(TestCase): cls.spravna_data = [ ('gymnázium kolín', 53), ('kolín', 53), - ('gasoš', 96), + #('gasoš', 96), ('Rokycany', 96), - ('gasoš Rokycany', 96), - ('SPŠE Pardubice', 815), + #('gasoš Rokycany', 96), + #('SPŠE Pardubice', 815), ('Jaroše', 164), - ("Gymnázium, Brno, tř. Kpt. Jaroše", 164), + #("Gymnázium, Brno, tř. Kpt. Jaroše", 164), ("Jírovcova", 157), ('České Budějovice', 157), ("Gymnázium, České Budějovice, Jírovcova 8", 157), - ("první soukromé", 2), + #("první soukromé", 2), ("Gymnázium Elgartova", 147), ("Jihlava", 45), ('Milevsko', 223), @@ -37,6 +38,16 @@ class OrgSkolyAutocompleteTestCase(TestCase): ("J. S. Machar", 15), ("Brandýs", 15), ] + # Následující aktuálně neumíme matchnout. Jsou v separátním + # testu jako expected failure, kdyby / až začnou fungovat, tak + # je odsud odeberme a výš odkomentujme. + cls.failujici_data = [ + ('gasoš', 96), + ('gasoš Rokycany', 96), + ('SPŠE Pardubice', 815), + ("Gymnázium, Brno, tř. Kpt. Jaroše", 164), + ("první soukromé", 2), + ] def test_view_funguje(self): """Jen se pokusí udělat na ten view dotaz a kouká na odpověď""" @@ -52,6 +63,16 @@ class OrgSkolyAutocompleteTestCase(TestCase): resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() ids = [int(x['id']) for x in resp['results']] self.assertIn(spravna_skola.id, ids, f"Škola nenalezena v odpovědi") + @expectedFailure + def test_skoly_vraceny_failujici(self): + """Orgové mají své školy v autocomplete (Expected Failure)""" + for pfx, id in self.failujici_data: + with self.subTest(prefix=pfx, spravne_id=id): + spravna_skola = m.Skola.objects.get(id=id) + # Zeptáme se view, co si myslí + resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() + ids = [int(x['id']) for x in resp['results']] + self.assertIn(spravna_skola.id, ids, f"Škola nenalezena v odpovědi") def test_skoly_pocet(self): """Testuje, že se pro dané prefixy nevrací moc škol""" From e0eb12cf9e3645cd8265b2c4b454bca12ce16a13 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Mon, 6 Feb 2023 22:31:42 +0100 Subject: [PATCH 117/120] =?UTF-8?q?=C5=A0koly=20jen=20zakomentovat,=20neps?= =?UTF-8?q?at=20expectedFailure=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Podle toho, jak dopadne PR se to buď aplikuje takto, nebo se tenhle commit revertne. --- api/tests/test_skola_autocomplete.py | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/api/tests/test_skola_autocomplete.py b/api/tests/test_skola_autocomplete.py index a469e338..9fc4aee6 100644 --- a/api/tests/test_skola_autocomplete.py +++ b/api/tests/test_skola_autocomplete.py @@ -3,7 +3,6 @@ from django.urls import reverse import seminar.models as m import seminar.views as v from seminar.utils import sync_skoly -from unittest import expectedFailure class OrgSkolyAutocompleteTestCase(TestCase): @classmethod @@ -12,6 +11,7 @@ class OrgSkolyAutocompleteTestCase(TestCase): sync_skoly('https://mam.mff.cuni.cz/') # Správné školy podle toho, co orgové poslali: (prefix, ID školy) # NOTE: Pozor, jedná se o databázové indexy. Pokud se to někdy rozbije, bude potřeba je přepsat nebo předělat na IZO + # TODO: Opravit zakomentované školy. cls.spravna_data = [ ('gymnázium kolín', 53), ('kolín', 53), @@ -38,16 +38,6 @@ class OrgSkolyAutocompleteTestCase(TestCase): ("J. S. Machar", 15), ("Brandýs", 15), ] - # Následující aktuálně neumíme matchnout. Jsou v separátním - # testu jako expected failure, kdyby / až začnou fungovat, tak - # je odsud odeberme a výš odkomentujme. - cls.failujici_data = [ - ('gasoš', 96), - ('gasoš Rokycany', 96), - ('SPŠE Pardubice', 815), - ("Gymnázium, Brno, tř. Kpt. Jaroše", 164), - ("první soukromé", 2), - ] def test_view_funguje(self): """Jen se pokusí udělat na ten view dotaz a kouká na odpověď""" @@ -63,16 +53,6 @@ class OrgSkolyAutocompleteTestCase(TestCase): resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() ids = [int(x['id']) for x in resp['results']] self.assertIn(spravna_skola.id, ids, f"Škola nenalezena v odpovědi") - @expectedFailure - def test_skoly_vraceny_failujici(self): - """Orgové mají své školy v autocomplete (Expected Failure)""" - for pfx, id in self.failujici_data: - with self.subTest(prefix=pfx, spravne_id=id): - spravna_skola = m.Skola.objects.get(id=id) - # Zeptáme se view, co si myslí - resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() - ids = [int(x['id']) for x in resp['results']] - self.assertIn(spravna_skola.id, ids, f"Škola nenalezena v odpovědi") def test_skoly_pocet(self): """Testuje, že se pro dané prefixy nevrací moc škol""" From 13c8c29bb0be85ef39c97607268739c606856245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 13 Feb 2023 20:47:42 +0100 Subject: [PATCH 118/120] =?UTF-8?q?fix:=20dvojn=C3=A1sobky=20v=20tabulce?= =?UTF-8?q?=20do=C5=A1l=C3=BDch=20=C5=99e=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 9ac1ac29..c5a93fe1 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -114,7 +114,7 @@ class TabulkaOdevzdanychReseniView(ListView): 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') + 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').distinct() # 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 From d5cf81c32a455b8b4d669d2c7d85045451fd888c Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 21 Feb 2023 17:08:10 +0100 Subject: [PATCH 119/120] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20titulku=20k=20?= =?UTF-8?q?v=C3=BDpisu=20deadlinu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Po najetí myší řekne, co znamenají symboly… --- seminar/templatetags/deadliny.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seminar/templatetags/deadliny.py b/seminar/templatetags/deadliny.py index 95db664b..199a1eef 100644 --- a/seminar/templatetags/deadliny.py +++ b/seminar/templatetags/deadliny.py @@ -26,7 +26,7 @@ def deadline_html(deadline: m.Deadline): m.Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline', m.Deadline.TYP_CISLA: 'final_deadline', } - return mark_safe(f'{text}') + return mark_safe(f'{text}') @register.filter(name='zkrat_nazev_problemu') def zkrat_nazev_problemu(nazev,width): @@ -35,4 +35,4 @@ def zkrat_nazev_problemu(nazev,width): nazev = nazev[:width-1] + "..." else: nazev = nazev[:width] + "..." - return nazev \ No newline at end of file + return nazev From 01f3537cef1c01001b57c5691880e4b569b49f24 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 22 Feb 2023 07:15:18 +0100 Subject: [PATCH 120/120] =?UTF-8?q?Dovolme=20orgovi=20kouknout=20se=20na?= =?UTF-8?q?=20=C5=99e=C5=A1itelsk=C3=BD=20n=C3=A1hled=20=C5=99e=C5=A1en?= =?UTF-8?q?=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Orgovi se může hodit vidět, jak zpětnou vazbu uvidí řešitel. Možná by taky nebylo špatné odkázat na řešitelskou stránku z orgovské… --- odevzdavatko/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index c5a93fe1..2390d27f 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -253,8 +253,8 @@ class DetailReseniView(DetailView): return response def check_access(self): - """ Řešitel musí být součástí řešení, jinak se na něj nemá co dívat. """ - if not self.object.resitele.filter(osoba__user=self.request.user).exists(): + """ Řešitel musí být součástí řešení, jinak se na něj nemá co dívat. Případně to může být org.""" + if not self.object.resitele.filter(osoba__user=self.request.user).exists() and not self.request.user.je_org: raise PermissionDenied()