Compare commits

..

3 commits

Author SHA1 Message Date
MaM Web user
64bd360716 Přidat hlavičku 2022-03-04 22:55:59 +01:00
MaM Web user
350041c587 fix :-) 2022-03-04 22:44:36 +01:00
Pavel "LEdoian" Turinsky
3a8d5b2207 Export seznamu přednášek 2022-03-04 22:40:13 +01:00
689 changed files with 103272 additions and 64439 deletions

View file

@ -1,24 +0,0 @@
# Univerzální popis našich konvencí pro nastavení editorů.
# Vizte https://editorconfig.org pro detaily
root = true
[*]
charset = utf-8
# Unixové řádky
end_of_line = lf
insert_final_newline = true
[*.{py,css}]
indent_style = tab
# Nenařizujeme konkrétní šířku tabulátoru
indent_size = unset
# Automaticky generované migrace dodržují PEP-8, nemá smysl s tím moc bojovat.
[*/migrations/*.py]
indent_style = space
indent_size = 4
[*.html]
indent_style = space
indent_size = 2

10
.gitignore vendored
View file

@ -31,15 +31,5 @@ TODO
# reversion kvůli historii objektů v reversion
**/reversion
# dokumentace
docs/_build
docs/modules
# logy týracího skriptu (./checklinks.sh)
/wget.log.*
# pro lidi, co programují v nástrojích od JetBrains
.idea
# Mac users
.DS_Store

158
Makefile
View file

@ -1,9 +1,153 @@
# Existence tohohle Makefile je tu jen proto, aby fungovala svalová paměť. Pokud můžete, použijte rovnou `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/$*
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 "."
default:
@cat make/README.md
.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
.PHONY: default
# 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 <Python.h>, 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
# 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
# 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'

112
README.md
View file

@ -1,58 +1,74 @@
Web M&M
======
Basic commands for web development
==================================
Tohle je repozitář s kódem M&Mího webu. Pokud zde hledáte web samotný nebo
informace o semináři, najdete je na <https://mam.matfyz.cz> (a upřímně nechápu,
jak jste se dostali k tomuhle textu :-D)
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`.
Pokud jste tu zůstali, tak vás beztak zajímá vývoj webu (a jestli ne, tak
budeme rádi, když začne :-)).
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.
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).
Use git :-)
<!--TODO: Z odstavce výše by ideálně měla být zachována jen první věta a zbytek
by měl být někde v docs s podrobnějším popisem…-->
Quickstart
----------
Run the following commands:
make install_venv
. env/bin/activate
make install_web
Jak si web pořídit
------
Prosím přečti si podrobnější návod v <docs/vyvoj.rst> (tady by bylo zbytečné
ho duplikovat).
After finishing development, run "deactivate".
Jak web vyvíjet
----
<!--TODO: Napsat obšírněji, asi zase do docs/-->
Make commands
-------------
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 :-))
* `make install` (or `make`) - locally install and setup virtualpy, install
required packages. Ran again installs missing packages. Run after changing
`requirements.txt`.
1. Nejprve si stáhni repozitář a rozběhni si lokální web u sebe (viz <docs/vyvoj.rst>).
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 :-)
* `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`.
### Proč pull-requesty?
<!--FIXME: Tohle ale už úplně patří do docs a ne sem, jen je zatím nemám prozkoumané…-->
Úč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 :-))

16
_git_hooks/README.md Normal file
View file

@ -0,0 +1,16 @@
git hooks
=========
Kontrola stylu pythoních zdrojáků pomocí flake8. Kontrolujeme jen změny,
abychom nenutili lidi dělat nesouvisející úpravy, které by rozbíjely historii
(git blame).
pre-commit
----------
* kontrola změn před commitnutím
* instalace: lokálně zkopírovat do .git/hooks (musí být spustitelný)
update
------
* kontrola změn přicházejících s pushem
* instalace: na atreyi zkopírovat do /akce/MaM/MaMweb/mamweb.git/hooks

30
_git_hooks/pre-commit Executable file
View file

@ -0,0 +1,30 @@
#!/bin/sh
#
# Git hook script to verify what is about to be committed.
# Checks that the changes don't introduce new flake8 errors.
TMPDIFF=`tempfile`
FLAKE8="`git rev-parse --show-toplevel`/bin/flake8"
status=0
# select only changed python files which are not migrations
changed=`git diff --cached --name-only | grep 'py$' | grep -v 'migrations/[0-9]'`
if [ -z $changed ] ; then
# Nothing to check. Note the exit is necessary -- we would not pass any
# paths to git diff below and it would output the diff unfiltered.
exit 0
fi
git diff --unified=1 --cached HEAD -- $changed > $TMPDIFF
# only do the check when there are some changes to be commited
# otherwise flake8 would hang waiting for input
if [ -s $TMPDIFF ] ; then
cat $TMPDIFF | $FLAKE8 --diff
status=$?
fi
rm -f $TMPDIFF
exit $status

61
_git_hooks/update Executable file
View file

@ -0,0 +1,61 @@
#!/bin/sh
# git update hook to check that pushed changes don't introduce new flake8
# errors
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
TMPDIR=`mktemp -d`
TMPDIFF=`tempfile`
[ $refname != "refs/heads/master" -a $refname != "refs/heads/stable" ] && exit 0
# select only changed python files which are not migrations
changed=`git diff --name-only $oldrev $newrev | grep 'py$' | grep -v 'migrations/[0-9]'`
if [ -z $changed ] ; then
# Nothing to check. Note the exit is necessary -- we would not pass any
# paths to git diff below and it would output the diff unfiltered.
exit 0
fi
git diff --unified=1 $oldrev $newrev -- $changed >${TMPDIFF}
# there is no working tree in bare git repository, so we recreate it for flake8
git archive $newrev | tar -x -C ${TMPDIR}
cd ${TMPDIR}
# report only errors on lines in diff
# (if threre was flake8 installed on atrey, we could just call flake8)
/akce/MaM/WWW/mamweb-test/bin/flake8 --diff <${TMPDIFF}
status=$?
if [ $status != 0 ] ; then
echo
echo -n "Změny, které se snažíte pushnout, obsahují kód v pythonu "
echo -n "nevyhovující flake8 (viz výše). Opravte je a zkuste to znovu. "
echo -n "Nezapomeňte, že můžete editovat historii (git commit --amend, "
echo -n "git rebase -i). Pokud byste chybu příště raději odhalili už při "
echo "commitu, zkopírujte si pre-commit hook z _git_hooks do .git/hooks."
echo
fi
rm -rf ${TMPDIR}
rm -f ${TMPDIFF}
exit $status

View file

@ -1,5 +1,5 @@
from django.apps import AppConfig
class AesopConfig(AppConfig):
name = 'aesop'
verbose_name = 'Export do AESOPa'

View file

@ -1,5 +1,5 @@
from django.http import HttpResponse
from django.utils.encoding import force_str
from django.utils.encoding import force_text
class OvvpFile:
@ -20,7 +20,7 @@ class OvvpFile:
yield '\t'.join(self.columns) + '\n'
# rows
for r in self.rows:
yield '\t'.join([force_str(r[c]) for c in self.columns]) + '\n'
yield '\t'.join([force_text(r[c]) for c in self.columns]) + '\n'
def to_string(self):
return ''.join(self.to_lines())

View file

@ -5,16 +5,16 @@ urlpatterns = [
path(
'aesop-export/mam-rocnik-<int:prvni_rok>.csv',
views.ExportRocnikView.as_view(),
name='aesop_export_rocnik'
name='seminar_export_rocnik'
),
path(
'aesop-export/mam-sous-<str:datum_zacatku>.csv',
views.ExportSousView.as_view(),
name='aesop_export_sous'
name='seminar_export_sous'
),
path(
'aesop-export/index.csv',
views.ExportIndexView.as_view(),
name='aesop_export_index'
name='seminar_export_index'
),
]

View file

@ -1,6 +1,6 @@
import datetime
from django.utils.encoding import force_str
from django.utils.encoding import force_text
from aesop.ovvpfile import OvvpFile
@ -9,7 +9,7 @@ def default_ovvpfile(event, rocnik):
of = OvvpFile()
of.headers['version'] = '1'
of.headers['event'] = event
of.headers['year'] = force_str(rocnik.prvni_rok)
of.headers['year'] = force_text(rocnik.prvni_rok)
of.headers['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
of.headers['id-scope'] = 'mam'
of.headers['id-generation'] = '1'

View file

@ -3,22 +3,21 @@ from django.shortcuts import get_object_or_404
from django.http import HttpResponse
from django.urls import reverse
from django.views import generic
from django.utils.encoding import force_str
from django.utils.encoding import force_text
from .utils import default_ovvpfile
from soustredeni.models import Soustredeni
from tvorba.models import Rocnik
from seminar.models import Rocnik, Soustredeni
from vysledkovky import utils
from tvorba.utils import aktivniResitele
from seminar.utils import aktivniResitele
class ExportIndexView(generic.View):
def get(self, request):
ls = []
for r in Rocnik.objects.filter(exportovat = True):
url = reverse('aesop_export_rocnik', kwargs={'prvni_rok': r.prvni_rok})
url = reverse('seminar_export_rocnik', kwargs={'prvni_rok': r.prvni_rok})
ls.append(url.split('/')[-1])
for s in Soustredeni.objects.filter(exportovat = True):
url = reverse('aesop_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()})
url = reverse('seminar_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()})
ls.append(url.split('/')[-1])
return HttpResponse('\n'.join(ls) + '\n', content_type='text/plain; charset=utf-8')

View file

@ -3,4 +3,3 @@ from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'
verbose_name = 'Různá webová API'

View file

@ -1,9 +1,9 @@
from django.test import TestCase, tag
from django.test import TestCase
from django.urls import reverse
from personalni.models import Skola
from personalni.utils import sync_skoly
import seminar.models as m
import seminar.views as v
from seminar.utils import sync_skoly
@tag('stejny-model-na-produkci')
class OrgSkolyAutocompleteTestCase(TestCase):
@classmethod
def setUpClass(cls):
@ -11,20 +11,19 @@ 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),
#('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),
@ -48,7 +47,7 @@ class OrgSkolyAutocompleteTestCase(TestCase):
"""Testuje, že pro každého orga je jeho škola ve výsledném QuerySetu"""
for pfx, id in self.spravna_data:
with self.subTest(prefix=pfx, spravne_id=id):
spravna_skola = Skola.objects.get(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']]

View file

@ -1,6 +1,6 @@
from django.urls import path
from . import views
from personalni.utils import org_required
from seminar.utils import org_required
urlpatterns = [
# Export škol
@ -10,12 +10,10 @@ 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'),
path('api/autocomplete/problem/vsechny', views.ProblemAutocomplete.as_view(), name='autocomplete_problem'),
# Ceka na autocomplete v3
# path('autocomplete/organizatori/',
# org_member_required(views.OrganizatorAutocomplete.as_view()),
# name='autocomplete_organizator')
# name='seminar_autocomplete_organizator')
]

View file

@ -1,21 +1,15 @@
"""
Views k :mod:`dal.autocomplete` pro vyhledávání objektů nějaké třídy v databázi.
"""
from dal import autocomplete
from django.shortcuts import get_object_or_404
from django.db.models import Q
from personalni.models import Skola, Resitel
from tvorba.models import Problem
from various.models import Nastaveni
import seminar.models as m
from .helpers import LoginRequiredAjaxMixin
# TODO filosofie - zkratky, jak v databázi, tak ve vyhledávání (SPŠE, GASOŠ, Kpt., soukr)
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 !
qs = Skola.objects.all()
# 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
partq = Q()
@ -31,70 +25,36 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView):
return qs
class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView):
""" View k :mod:`dal.autocomplete` pro vyhledávání řešitelů především v odevzdávátku. """
def get_queryset(self):
qs = Resitel.objects.all()
qs = m.Resitel.objects.all()
if self.q:
parts = self.q.split()
query = Q()
for part in parts:
query &= (
Q(osoba__jmeno__istartswith=part)|
Q(osoba__prijmeni__istartswith=part)|
Q(osoba__prezdivka__istartswith=part)
Q(osoba__jmeno__istartswith=self.q)|
Q(osoba__prijmeni__istartswith=self.q)|
Q(osoba__prezdivka__istartswith=self.q)
)
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):
letos = Nastaveni.get_solo().aktualni_rocnik
qs = Resitel.objects.filter(
rok_maturity__gte=letos.druhy_rok()
).filter(
prezdivka_resitele__isnull=False
).exclude(
prezdivka_resitele=""
).filter(
prezdivka_resitele__icontains=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):
qs = Problem.objects.filter(stav=Problem.STAV_ZADANY)
if self.q:
qs = qs.filter(
Q(nazev__icontains=self.q))
nadproblem_id = int(self.forwarded.get("nadproblem_id", -1))
if nadproblem_id != -1:
# Seřadíme tak, aby ty s nadproblem==None byly dole (větší motivace tam naklikat konkrétní úlohy) a pak nějak rozumně.
# Tohle je řazení pro odevzdávátko, kde je definován nadproblém, proto je to v tomto ifu. (Jinde si to netroufám řadit)
qs = qs.order_by("nadproblem", "kod", "nazev")
qs = list(filter(lambda problem: problem.hlavni_problem.id == nadproblem_id, qs))
return qs
class ProblemAutocomplete(autocomplete.Select2QuerySetView):
""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """
def get_queryset(self):
# FIXME i starší úlohy
nastaveni = get_object_or_404(Nastaveni)
nastaveni = get_object_or_404(m.Nastaveni)
rocnik = nastaveni.aktualni_rocnik
temaQ = Q(Tema___rocnik = rocnik)
ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik)
clanekQ = Q(Clanek___cislo__rocnik=rocnik)
qs = Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev")
# Od tohoto místa dál jsem zkoušel spoustu variací podle https://django-polymorphic.readthedocs.io/en/stable/advanced.html
temaQ = Q(Tema___rocnik = rocnik, stav=m.Problem.STAV_ZADANY)
ulohaQ = Q(Uloha___cislo_zadani__rocnik = rocnik, stav=m.Problem.STAV_ZADANY)
clanekQ = Q(Clanek___cislo__rocnik = rocnik, stav=m.Problem.STAV_ZADANY)
qs = m.Problem.objects.filter(temaQ | ulohaQ | clanekQ)
#print(temata, ulohy, clanky)
#ulohy.union(temata, all=True)
#print(ulohy)
#ulohy.union(clanky, all=True)
#print(ulohy)
#qs = ulohy
print(qs)
if self.q:
qs = qs.filter(
Q(nazev__icontains=self.q))

View file

@ -1,10 +1,7 @@
import personalni.models as m
import seminar.models as m
from django.core import serializers as ser
from django.http import HttpResponse
def exportSkolView(request):
"""
view, který vrací json se seznamem škol, u kterých je uvedeno: 'id', 'izo', 'nazev', 'kratky_nazev', 'ulice', 'mesto', 'psc', 'stat', 'je_zs', 'je_ss'
"""
# Některé fieldy nechceme: Kontaktní osoby, AESOP ID, org poznámky.
fields = ('id', 'izo', 'nazev', 'kratky_nazev', 'ulice', 'mesto', 'psc', 'stat', 'je_zs', 'je_ss')
# TODO: Použít JSONL, aby protistrana mohla číst po řádkách a nesežralo to tunu paměti úplně hned

View file

@ -2,9 +2,6 @@ from django.http import JsonResponse
class LoginRequiredAjaxMixin(object):
"""
Objekt přidávající povinnost být přihlášený k zobrazení daného view.
"""
def dispatch(self, request, *args, **kwargs):
#if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme
if not request.user.is_authenticated:

File diff suppressed because one or more lines are too long

View file

@ -1,655 +0,0 @@
[
{
"fields": {
"name": "org",
"permissions": [
[
"org",
"auth",
"user"
],
[
"add_flatpage",
"flatpages",
"flatpage"
],
[
"change_flatpage",
"flatpages",
"flatpage"
],
[
"delete_flatpage",
"flatpages",
"flatpage"
],
[
"view_flatpage",
"flatpages",
"flatpage"
],
[
"add_galerie",
"galerie",
"galerie"
],
[
"change_galerie",
"galerie",
"galerie"
],
[
"delete_galerie",
"galerie",
"galerie"
],
[
"view_galerie",
"galerie",
"galerie"
],
[
"add_obrazek",
"galerie",
"obrazek"
],
[
"change_obrazek",
"galerie",
"obrazek"
],
[
"delete_obrazek",
"galerie",
"obrazek"
],
[
"view_obrazek",
"galerie",
"obrazek"
],
[
"add_fotkaheader",
"header_fotky",
"fotkaheader"
],
[
"change_fotkaheader",
"header_fotky",
"fotkaheader"
],
[
"view_fotkaheader",
"header_fotky",
"fotkaheader"
],
[
"add_fotkaurlvazba",
"header_fotky",
"fotkaurlvazba"
],
[
"change_fotkaurlvazba",
"header_fotky",
"fotkaurlvazba"
],
[
"view_fotkaurlvazba",
"header_fotky",
"fotkaurlvazba"
],
[
"add_komentar",
"korektury",
"komentar"
],
[
"change_komentar",
"korektury",
"komentar"
],
[
"delete_komentar",
"korektury",
"komentar"
],
[
"view_komentar",
"korektury",
"komentar"
],
[
"add_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"change_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"delete_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"view_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"add_oprava",
"korektury",
"oprava"
],
[
"change_oprava",
"korektury",
"oprava"
],
[
"delete_oprava",
"korektury",
"oprava"
],
[
"view_oprava",
"korektury",
"oprava"
],
[
"add_novinky",
"novinky",
"novinky"
],
[
"change_novinky",
"novinky",
"novinky"
],
[
"delete_novinky",
"novinky",
"novinky"
],
[
"view_novinky",
"novinky",
"novinky"
],
[
"change_organizator",
"personalni",
"organizator"
],
[
"view_organizator",
"personalni",
"organizator"
],
[
"change_osoba",
"personalni",
"osoba"
],
[
"view_osoba",
"personalni",
"osoba"
],
[
"add_prijemce",
"personalni",
"prijemce"
],
[
"change_prijemce",
"personalni",
"prijemce"
],
[
"delete_prijemce",
"personalni",
"prijemce"
],
[
"view_prijemce",
"personalni",
"prijemce"
],
[
"change_resitel",
"personalni",
"resitel"
],
[
"view_resitel",
"personalni",
"resitel"
],
[
"add_skola",
"personalni",
"skola"
],
[
"change_skola",
"personalni",
"skola"
],
[
"delete_skola",
"personalni",
"skola"
],
[
"view_skola",
"personalni",
"skola"
],
[
"view_hlasovani",
"prednasky",
"hlasovani"
],
[
"view_hlasovanioznalostech",
"prednasky",
"hlasovanioznalostech"
],
[
"add_prednaska",
"prednasky",
"prednaska"
],
[
"change_prednaska",
"prednasky",
"prednaska"
],
[
"delete_prednaska",
"prednasky",
"prednaska"
],
[
"view_prednaska",
"prednasky",
"prednaska"
],
[
"add_seznam",
"prednasky",
"seznam"
],
[
"change_seznam",
"prednasky",
"seznam"
],
[
"delete_seznam",
"prednasky",
"seznam"
],
[
"view_seznam",
"prednasky",
"seznam"
],
[
"add_znalost",
"prednasky",
"znalost"
],
[
"change_znalost",
"prednasky",
"znalost"
],
[
"delete_znalost",
"prednasky",
"znalost"
],
[
"view_znalost",
"prednasky",
"znalost"
],
[
"add_konfera",
"soustredeni",
"konfera"
],
[
"change_konfera",
"soustredeni",
"konfera"
],
[
"delete_konfera",
"soustredeni",
"konfera"
],
[
"view_konfera",
"soustredeni",
"konfera"
],
[
"add_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"change_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"delete_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"view_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"add_soustredeni",
"soustredeni",
"soustredeni"
],
[
"change_soustredeni",
"soustredeni",
"soustredeni"
],
[
"delete_soustredeni",
"soustredeni",
"soustredeni"
],
[
"view_soustredeni",
"soustredeni",
"soustredeni"
],
[
"add_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"change_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"delete_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"view_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"add_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"change_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"delete_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"view_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"add_tag",
"taggit",
"tag"
],
[
"change_tag",
"taggit",
"tag"
],
[
"delete_tag",
"taggit",
"tag"
],
[
"view_tag",
"taggit",
"tag"
],
[
"add_taggeditem",
"taggit",
"taggeditem"
],
[
"change_taggeditem",
"taggit",
"taggeditem"
],
[
"delete_taggeditem",
"taggit",
"taggeditem"
],
[
"view_taggeditem",
"taggit",
"taggeditem"
],
[
"add_cislo",
"tvorba",
"cislo"
],
[
"change_cislo",
"tvorba",
"cislo"
],
[
"delete_cislo",
"tvorba",
"cislo"
],
[
"view_cislo",
"tvorba",
"cislo"
],
[
"add_clanek",
"tvorba",
"clanek"
],
[
"change_clanek",
"tvorba",
"clanek"
],
[
"delete_clanek",
"tvorba",
"clanek"
],
[
"view_clanek",
"tvorba",
"clanek"
],
[
"add_deadline",
"tvorba",
"deadline"
],
[
"change_deadline",
"tvorba",
"deadline"
],
[
"view_deadline",
"tvorba",
"deadline"
],
[
"add_pohadka",
"tvorba",
"pohadka"
],
[
"change_pohadka",
"tvorba",
"pohadka"
],
[
"delete_pohadka",
"tvorba",
"pohadka"
],
[
"view_pohadka",
"tvorba",
"pohadka"
],
[
"add_problem",
"tvorba",
"problem"
],
[
"change_problem",
"tvorba",
"problem"
],
[
"delete_problem",
"tvorba",
"problem"
],
[
"view_problem",
"tvorba",
"problem"
],
[
"add_rocnik",
"tvorba",
"rocnik"
],
[
"change_rocnik",
"tvorba",
"rocnik"
],
[
"delete_rocnik",
"tvorba",
"rocnik"
],
[
"view_rocnik",
"tvorba",
"rocnik"
],
[
"add_tema",
"tvorba",
"tema"
],
[
"change_tema",
"tvorba",
"tema"
],
[
"delete_tema",
"tvorba",
"tema"
],
[
"view_tema",
"tvorba",
"tema"
],
[
"add_uloha",
"tvorba",
"uloha"
],
[
"change_uloha",
"tvorba",
"uloha"
],
[
"delete_uloha",
"tvorba",
"uloha"
],
[
"view_uloha",
"tvorba",
"uloha"
],
[
"add_nastaveni",
"various",
"nastaveni"
],
[
"change_nastaveni",
"various",
"nastaveni"
],
[
"delete_nastaveni",
"various",
"nastaveni"
],
[
"view_nastaveni",
"various",
"nastaveni"
]
]
},
"model": "auth.group",
"pk": 1
},
{
"fields": {
"name": "resitel",
"permissions": [
[
"resitel",
"auth",
"user"
]
]
},
"model": "auth.group",
"pk": 2
}
]

View file

@ -73,7 +73,7 @@
"sort_order": 3,
"title": "Aktuální<br/> ročník",
"tree": 1,
"url": "tvorba_aktualni_zadani",
"url": "seminar_aktualni_zadani",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -121,7 +121,7 @@
"sort_order": 5,
"title": "Archiv",
"tree": 1,
"url": "tvorba_archiv_rocniky",
"url": "seminar_archiv_rocniky",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -289,7 +289,7 @@
"sort_order": 43,
"title": "Výsledková listina",
"tree": 1,
"url": "tvorba_aktualni_vysledky",
"url": "seminar_aktualni_vysledky",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -361,7 +361,7 @@
"sort_order": 20,
"title": "Proběhlo",
"tree": 1,
"url": "soustredeni_seznam",
"url": "seminar_seznam_soustredeni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -409,7 +409,7 @@
"sort_order": 23,
"title": "Osobní údaje",
"tree": 1,
"url": "personalni_resitel_edit",
"url": "seminar_resitel_edit",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -437,9 +437,9 @@
"insitetree": true,
"parent": 21,
"sort_order": 36,
"title": "Nahrát řešení",
"title": "Poslat řešení",
"tree": 1,
"url": "odevzdavatko_nahraj_reseni",
"url": "seminar_nahraj_reseni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -463,7 +463,7 @@
"sort_order": 35,
"title": "Témata",
"tree": 1,
"url": "tvorba_archiv_temata",
"url": "seminar_archiv_temata",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -476,9 +476,9 @@
"access_perm_type": 1,
"access_permissions": [
[
"org",
"auth",
"user"
"change_hodnoceni",
"seminar",
"hodnoceni"
]
],
"access_restricted": true,
@ -589,7 +589,7 @@
"sort_order": 15,
"title": "Aktuální číslo",
"tree": 1,
"url": "tvorba_aktualni_zadani",
"url": "seminar_aktualni_zadani",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -613,7 +613,7 @@
"sort_order": 24,
"title": "Čísla",
"tree": 1,
"url": "tvorba_archiv_rocniky",
"url": "seminar_archiv_rocniky",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -719,9 +719,9 @@
"insitetree": true,
"parent": 21,
"sort_order": 36,
"title": "Vložit řešení",
"title": "Nahrát řešení",
"tree": 1,
"url": "odevzdavatko_vloz_reseni",
"url": "seminar_vloz_reseni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -804,7 +804,7 @@
"sort_order": 37,
"title": "Moje řešení",
"tree": 1,
"url": "odevzdavatko_resitel_odevzdana_reseni",
"url": "seminar_resitel_odevzdana_reseni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -828,7 +828,7 @@
"sort_order": 33,
"title": "Aktuální ročník",
"tree": 1,
"url": "tvorba_aktualni_rocnik",
"url": "seminar_aktualni_rocnik",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -900,7 +900,7 @@
"sort_order": 46,
"title": "Ročník {{rocnik.rocnik}}",
"tree": 1,
"url": "tvorba_rocnik rocnik.rocnik",
"url": "seminar_rocnik rocnik.rocnik",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -924,7 +924,7 @@
"sort_order": 47,
"title": "Číslo {{ cislo.rocnik.rocnik }}.{{ cislo.poradi }}",
"tree": 1,
"url": "tvorba_cislo cislo.rocnik.rocnik cislo.poradi",
"url": "seminar_cislo cislo.rocnik.rocnik cislo.poradi",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -1007,18 +1007,7 @@
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [
[
"org",
"auth",
"user"
],
[
"resitel",
"auth",
"user"
]
],
"access_permissions": [],
"access_restricted": true,
"alias": null,
"description": "",
@ -1036,83 +1025,5 @@
},
"model": "sitetree.treeitem",
"pk": 51
},
{
"fields": {
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [
[
"resitel",
"auth",
"user"
]
],
"access_restricted": true,
"alias": null,
"description": "",
"hidden": false,
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 23,
"sort_order": 52,
"title": "Nahrát řešení k nadproblému {{nadproblem_id}}",
"tree": 1,
"url": "odevzdavatko_nahraj_reseni nadproblem_id",
"urlaspattern": true
},
"model": "sitetree.treeitem",
"pk": 52
},
{
"fields": {
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [],
"access_restricted": false,
"alias": null,
"description": "",
"hidden": false,
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 28,
"sort_order": 53,
"title": "Přidat PDF",
"tree": 1,
"url": "/admin/korektury/korekturovanepdf/add/",
"urlaspattern": false
},
"model": "sitetree.treeitem",
"pk": 53
},
{
"fields": {
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [],
"access_restricted": false,
"alias": null,
"description": "",
"hidden": false,
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 20,
"sort_order": 54,
"title": "Export do abstraktů sousu {{ soustredeni.id }}",
"tree": 1,
"url": "soustredeni_abstrakty soustredeni.id",
"urlaspattern": true
},
"model": "sitetree.treeitem",
"pk": 54
}
]
]

View file

@ -0,0 +1,637 @@
[
{
"codename": "org",
"ct_app_label": "auth",
"ct_model": "user"
},
{
"codename": "add_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "change_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "delete_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "view_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "add_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "change_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "delete_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "view_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "add_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "change_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "delete_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "view_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "add_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "change_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "delete_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "view_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "add_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "change_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "delete_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "view_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "add_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "change_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "delete_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "view_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "add_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "change_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "delete_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "view_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "add_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "change_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "delete_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "view_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "add_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "change_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "delete_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "view_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "add_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "change_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "delete_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "view_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "add_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "change_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "delete_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "view_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "add_konfera",
"ct_app_label": "seminar",
"ct_model": "konfera"
},
{
"codename": "change_konfera",
"ct_app_label": "seminar",
"ct_model": "konfera"
},
{
"codename": "delete_konfera",
"ct_app_label": "seminar",
"ct_model": "konfera"
},
{
"codename": "view_konfera",
"ct_app_label": "seminar",
"ct_model": "konfera"
},
{
"codename": "add_konfery_ucastnici",
"ct_app_label": "seminar",
"ct_model": "konfery_ucastnici"
},
{
"codename": "change_konfery_ucastnici",
"ct_app_label": "seminar",
"ct_model": "konfery_ucastnici"
},
{
"codename": "delete_konfery_ucastnici",
"ct_app_label": "seminar",
"ct_model": "konfery_ucastnici"
},
{
"codename": "view_konfery_ucastnici",
"ct_app_label": "seminar",
"ct_model": "konfery_ucastnici"
},
{
"codename": "add_nastaveni",
"ct_app_label": "seminar",
"ct_model": "nastaveni"
},
{
"codename": "change_nastaveni",
"ct_app_label": "seminar",
"ct_model": "nastaveni"
},
{
"codename": "delete_nastaveni",
"ct_app_label": "seminar",
"ct_model": "nastaveni"
},
{
"codename": "view_nastaveni",
"ct_app_label": "seminar",
"ct_model": "nastaveni"
},
{
"codename": "add_novinky",
"ct_app_label": "seminar",
"ct_model": "novinky"
},
{
"codename": "change_novinky",
"ct_app_label": "seminar",
"ct_model": "novinky"
},
{
"codename": "delete_novinky",
"ct_app_label": "seminar",
"ct_model": "novinky"
},
{
"codename": "view_novinky",
"ct_app_label": "seminar",
"ct_model": "novinky"
},
{
"codename": "add_organizator",
"ct_app_label": "seminar",
"ct_model": "organizator"
},
{
"codename": "change_organizator",
"ct_app_label": "seminar",
"ct_model": "organizator"
},
{
"codename": "delete_organizator",
"ct_app_label": "seminar",
"ct_model": "organizator"
},
{
"codename": "view_organizator",
"ct_app_label": "seminar",
"ct_model": "organizator"
},
{
"codename": "add_osoba",
"ct_app_label": "seminar",
"ct_model": "osoba"
},
{
"codename": "change_osoba",
"ct_app_label": "seminar",
"ct_model": "osoba"
},
{
"codename": "delete_osoba",
"ct_app_label": "seminar",
"ct_model": "osoba"
},
{
"codename": "view_osoba",
"ct_app_label": "seminar",
"ct_model": "osoba"
},
{
"codename": "add_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "change_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "delete_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "view_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "add_prijemce",
"ct_app_label": "seminar",
"ct_model": "prijemce"
},
{
"codename": "change_prijemce",
"ct_app_label": "seminar",
"ct_model": "prijemce"
},
{
"codename": "delete_prijemce",
"ct_app_label": "seminar",
"ct_model": "prijemce"
},
{
"codename": "view_prijemce",
"ct_app_label": "seminar",
"ct_model": "prijemce"
},
{
"codename": "add_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "change_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "delete_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "view_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "add_resitel",
"ct_app_label": "seminar",
"ct_model": "resitel"
},
{
"codename": "change_resitel",
"ct_app_label": "seminar",
"ct_model": "resitel"
},
{
"codename": "delete_resitel",
"ct_app_label": "seminar",
"ct_model": "resitel"
},
{
"codename": "view_resitel",
"ct_app_label": "seminar",
"ct_model": "resitel"
},
{
"codename": "add_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "change_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "delete_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "view_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "add_skola",
"ct_app_label": "seminar",
"ct_model": "skola"
},
{
"codename": "change_skola",
"ct_app_label": "seminar",
"ct_model": "skola"
},
{
"codename": "delete_skola",
"ct_app_label": "seminar",
"ct_model": "skola"
},
{
"codename": "view_skola",
"ct_app_label": "seminar",
"ct_model": "skola"
},
{
"codename": "add_soustredeni",
"ct_app_label": "seminar",
"ct_model": "soustredeni"
},
{
"codename": "change_soustredeni",
"ct_app_label": "seminar",
"ct_model": "soustredeni"
},
{
"codename": "delete_soustredeni",
"ct_app_label": "seminar",
"ct_model": "soustredeni"
},
{
"codename": "view_soustredeni",
"ct_app_label": "seminar",
"ct_model": "soustredeni"
},
{
"codename": "add_soustredeni_organizatori",
"ct_app_label": "seminar",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "change_soustredeni_organizatori",
"ct_app_label": "seminar",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "delete_soustredeni_organizatori",
"ct_app_label": "seminar",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "view_soustredeni_organizatori",
"ct_app_label": "seminar",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "add_soustredeni_ucastnici",
"ct_app_label": "seminar",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "change_soustredeni_ucastnici",
"ct_app_label": "seminar",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "delete_soustredeni_ucastnici",
"ct_app_label": "seminar",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "view_soustredeni_ucastnici",
"ct_app_label": "seminar",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "add_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "change_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "delete_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "view_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "add_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "change_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "delete_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "view_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "add_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "change_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "delete_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "view_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "add_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "change_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "delete_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "view_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "add_fotkaheader",
"ct_app_label": "header_fotky",
"ct_model": "fotkaheader"
},
{
"codename": "change_fotkaheader",
"ct_app_label": "header_fotky",
"ct_model": "fotkaheader"
},
{
"codename": "view_fotkaheader",
"ct_app_label": "header_fotky",
"ct_model": "fotkaheader"
},
{
"codename": "add_fotkaurlvazba",
"ct_app_label": "header_fotky",
"ct_model": "fotkaurlvazba"
},
{
"codename": "change_fotkaurlvazba",
"ct_app_label": "header_fotky",
"ct_model": "fotkaurlvazba"
},
{
"codename": "view_fotkaurlvazba",
"ct_app_label": "header_fotky",
"ct_model": "fotkaurlvazba"
}
]

View file

@ -13,6 +13,7 @@ make install_venv
make install
deploy_v2/pre_migration.py
make deploy_test
./manage.py load_org_permissions admin_org_prava.json
./manage.py loaddata data/*
systemctl --user start mamweb-test.service
./manage.py generate_thumbnails

View file

@ -1,21 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
sphinx-apidoc --module-first -o modules .. ../*/migrations --templatedir _templates -f
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -1,8 +0,0 @@
{%- if show_headings %}
{{- [basename, ".py"] | join('') | e | heading }}
{% endif -%}
.. automodule:: {{ qualname }}
{%- for option in automodule_options %}
:{{ option }}:
{%- endfor %}

View file

@ -1,57 +0,0 @@
{%- macro automodule(modname, options) -%}
.. automodule:: {{ modname }}
{%- for option in options %}
:{{ option }}:
{%- endfor %}
{%- endmacro %}
{%- macro toctree(docnames) -%}
.. toctree::
:maxdepth: {{ maxdepth }}
{% for docname in docnames %}
{{ docname }}
{%- endfor %}
{%- endmacro %}
{%- if is_namespace %}
{{- pkgname | e | heading }}
{% else %}
{{- pkgname | e | heading }}
{% endif %}
{%- if is_namespace %}
.. py:module:: {{ pkgname }}
{% endif %}
{%- if modulefirst and not is_namespace %}
{{ automodule(pkgname, automodule_options) }}
{% endif %}
{%- if subpackages %}
{# Subpackages #}
{# ----------- #}
{{ toctree(subpackages) }}
{% endif %}
{%- if submodules %}
{# Submodules #}
{# ---------- #}
{% if separatemodules %}
{{ toctree(submodules) }}
{% else %}
{%- for submodule in submodules %}
{% if show_headings %}
{{- submodule | e | heading(2) }}
{% endif %}
{{ automodule(submodule, automodule_options) }}
{% endfor %}
{%- endif %}
{%- endif %}
{%- if not modulefirst and not is_namespace %}
Module contents
---------------
{{ automodule(pkgname, automodule_options) }}
{% endif %}

View file

@ -1,7 +0,0 @@
{{ "Seznam aplikací" | heading }}
.. toctree::
:maxdepth: {{ maxdepth }}
{% for docname in docnames %}
{{ docname }}
{%- endfor %}

View file

@ -1,5 +0,0 @@
Aprílové nápad
==============
* aprílový easter-egg pro řešitele - vytvořit nějakou vtipnou testovací databázi a nasadit ji místo produkce
* změnit veškerý text na oranžovo

View file

@ -1,102 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
import django
sys.path.insert(0, os.path.abspath('..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'mamweb.settings'
django.setup()
# -- Project information -----------------------------------------------------
project = 'Web M&M'
copyright = '2022, Orgové'
author = 'Orgové'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
'sphinx.ext.autosectionlabel',
'myst_parser',
'sphinxcontrib_django',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'cs'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Provázání s jinými dokumentacemi
intersphinx_mapping = {'python': ('https://docs.python.org/3', None),
'django': ('http://docs.djangoproject.com/en/3.2/',
'http://docs.djangoproject.com/en/3.2/_objects/'),}
# Generování tříd/funkcí/atributů v pořádí jak jsou naprogramované
autodoc_member_order = "bysource"
# Nezobrazování zděděné (ze super tříd) dokumentace TODO nefunguje?
autodoc_inherit_docstrings = False
source_suffix = {
'.rst': 'restructuredtext',
'.md': 'markdown',
}
# Autodoc má ignorovat některé moduly
# Ref: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#event-autodoc-skip-member
# Kudos: https://stackoverflow.com/a/21449475/
def ignorovat(app, what, name, obj, skip, options):
blacklist = (
# typ (what), name
('module', 'settings.mamweb_prod'),
)
ignore = (what, name) in blacklist
return True if ignore else None
def setup(app):
app.connect('autodoc-skip-member', ignorovat)

View file

@ -1,27 +0,0 @@
CSS (a další styly na webu)
===========================
Inspirován `css-trick článkem <https://css-tricks.com/methods-organize-css/>`_ jsem se rozhodl rozdělit
CSSka do
- Konstant (``constants.css``), které jsou využívány na mnoha místech CSSek
- Nastylování html tagů (``base.css``)
- Layoutu (``layout.css``), což je to, co určuje celkové rozložení stránky
- Jednotlivých prvků (``modules.css``)
Dále jsem separoval CSSka pro **galerii** (potřebuje hodně specifických stylů). Stejně tak **korekturovátko** má styly separátně.
Dále web (asi) používá externí frameworky (v separátních složkách mají k sobě i JS a podobné věci):
- bootstrap: dělá nějaké basic stylování, *web je na něm hodně závislý* (například jsem zjistil, že bootstrap kdysi přidával ``font-size:14px``, bez čehož se web úplně rozpadnul) (také na něm běží mobilní meníčko, které navíc vyžaduje Popper, tedy bootstrap.bundle.js místo bootstrap.js)
Pak jsou tu ``mamweb-dev.css`` a ``printtable.css``, co jsem si ještě nerozmyslel, co s tím.
Pár myšlenek
------------
- Až na pár výjimek (galerii a korekturovátko) bych styly držel v jedné složce a málo souborech,
protože CSS šíleně dědí všechno možné
- Chce to dobře pojmenovávat třídy (speciálně aby bylo vidět, co ta třída dělá nebo kde se používá)
- Chce to hodně komentovat kód (speciálně tam, kde není splněn předchozí bod)

View file

@ -1,86 +0,0 @@
Další soubory/složky v kořenovém adresáři
=========================================
media
-----
Složka, kam django nahrává soubory „jako by je nahrávalo do databáze“.
static
------
Složka, kam django nakopíruje všechno ze složek static a pak na to z templatů / kódu jde ukazovat pomocí ``static``.
data
----
Obsahuje data, která patří do databáze, ale jsou přímo součástí webu jako
takového. Aktuálně jsou to statické stránky, meníčko a rozložení obrázků
v pozadí meníčka.
Generuje se za pomocí::
./manage.py dumpdata flatpages > data/flat_new.json
./fix_json.py data/flat_new.json data/flat.json
nebo (v případě meníčka)::
./manage.py dumpdata sitetree --natural-foreign > data/sitetree_new.json
./fix_json.py data/sitetree_new.json data/sitetree.json
deploy_v2
---------
Věci, které byly potřeba při nasazování nového (2021) webu.
docs
----
Zde je dokumentace webu. Viz :ref:`Sphinx na našem webu`.
setup
-----
Tato složka obsahuje různé konfiguráky potřebné k rozběhnutí webu na serveru.
vue_frontend
------------
Obsahuje první pokusy o editory treenodů ve vue.
.gitignore
----------
Klasické `.gitignore`_
.. _.gitignore: https://git-scm.com/docs/gitignore
checklinks.sh
-------------
„Týrací“ skript na kontrolu, že nic, kam se lze proklikat na webu, nehází chybu.
constraints.txt
---------------
Obsahuje omezení na :ref:`requirements.txt`.
convert_spaces_to_tabs.sh
-------------------------
Skript na změnu odsazování.
db-local.sqlite3
----------------
Lokální databáze (na serveru není).
diff_db_backup.sh
-----------------
Nevím. Typoval bych skript na diff záloh (resp. dumpů) databáze.
Makefile
--------
Klasické `Makefile`_. Obsahuje například vytvoření virtual_env, instalaci a nasazování webu.
.. _Makefile: https://www.gnu.org/software/make/manual/make.html
manage.py
---------
Základní soubor djanga.
README.md
---------
Většina je spíš zbytek po bývalých webařích.
requirements.txt
----------------
Seznam balíčků, které jsou potřeba pro běh mamwebu. (Cílem je vytvoření virtualenvu se všemi těmito balíčky, např. pomocí daného příkazu v :ref:`Makefile`.)

View file

@ -1,55 +0,0 @@
Defaultní dokumentace speciálních souborů
=========================================
Drobná nápověda k běžným django souborům. (Do nich jsem to vkládal copy-paste.)
admin.py
--------
Soubor sloužící k definici toho, co bude v adminu. Většinou pouhým zavoláním
funkce :func:`django.contrib.admin.site.register`, v případě, že chceme něco
upravit, tak jako třída rozšiřující :class:`django.contrib.admin.ModelAdmin`
s dekorátorem :func:`django.contrib.admin.register`.
Zde se definuje admin pro:
apps.py
-------
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
forms.py
--------
Formuláře (:class:`django.forms.Form`) umožňují jednoduchou tvorbu formulářů,
které lze pak jednoduše dát do frontendu i zpracovat na backendu.
Pro přidání políčka do formuláře je potřeba
- mít v modelu tu položku, kterou chci upravovat
- přidat do views (prihlaskaView, resitelEditView)
- přidat do forms
- includovat do html
models.py
---------
Tento soubor slouží k definici databázového modelu.
Třídy rozšiřují většinou :class:`django.db.models.Model` a jejich atributy jsou
většinou sloupce v databázi (tj. nastaví se na hodnotu něčeho z :mod:`django.db.models`).
Na výběr jsou:
- :class:`django.db.models.TextField`
- :class:`django.db.models.ForeignKey`
- :class:`django.db.models.DateField`
- :class:`django.db.models.DateTimeField`
- :class:`django.db.models.ImageField`
- :class:`django.db.models.CharField`
testutils.py
------------
Soubor sloužící ke generování testdat.
urls.py
-------
Soubor sloužící jako „router“, tj. zde se definují url adresy a na co ukazují:
views.py
--------

View file

@ -1,45 +0,0 @@
.. Web M&M documentation master file, created by
sphinx-quickstart on Mon Jan 31 21:56:04 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Vítejte v dokumentaci M&Mího webu!
===================================
Tzv. produkce (tedy to, co vidí uživatelé) běží na `<mam.mff.cuni.cz>`_ (resp.
`<mam.matfyz.cz>`_), menu, obrázky v pozadí menu a spousta stránek (ty pouze se
statickým textem/obrázky) se mění přímo na produkci. Testovací verze běží na
`<https://mam-test.ks.matfyz.cz/>`_.
Abychom uměli web vyvíjet, musíme ho většinou nejdřív umět
:doc:`naklonovat a spustit lokálně <vyvoj>`.
:doc:`struktura mamwebu <struktura>` se řídí hlavně djangem, ale snažíme se
také o oddělení jednotlivých částí do :doc:`samostatných aplikací
<modules/modules>`.
Dokumentace (jak v ``docs/``, tak přímo v kódu) je psaná ve
:doc:`sphinxu <sphinx>`.
.. toctree::
:caption: M&M web
:maxdepth: 2
:titlesonly:
vyvoj
zavislosti
sphinx
skripty
zkratky
modules/modules
dalsi_soubory
zapisy/zapisy
Rejstříky a vyhledávání
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -1,108 +0,0 @@
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
``<aplikace>/management/commands/``, případně vestavěné, a volají se pomocí
``./manage.py <příkaz>``. 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/<příkaz>``.
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ě.
.. 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
<skript>``, 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 <složka>``
Otestuje, že skript běží z konkrétní složky. Zejména použitelné s ``gimli_only`` a ``$TESTWEB``
``safe_checkout_branch <větev>``
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 <https://mam-test.ks.matfyz.cz/docs>`_.
``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``.

View file

@ -1,41 +0,0 @@
Sphinx na našem webu
====================
Dokumentace se zkompiluje příkazem ``make html`` ve složce ``docs``. (Musíte mít zapnutý virtualenv)
Složka ``modules`` je automaticiky generována a přegenerovávána. (**Nic v ní neupravovat!**)
Jinak všechny rst, co jsou ve složce ``docs`` a jejích podsložkách nezačínajících podtržítkem, budou v dokumentaci a to je přesně to, co editovat pro změnu dokumentace (kromě dokumentace přímo v Pythonu).
Sphinx se píše v rst: `Návod na syntaxi rst`_ `Cheat sheet`_
Pokud něco chcete protlačit do bočního meníčka, je potřeba to připsat do souboru ``docs/index.rst`` (Zatím není úplně konsensus nad tím, co tam má a nemá být, takže pokud si nejste jistí, cpěte tam *všechno* ☺)
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í::
sphinx-apidoc --module-first -o modules .. ../*/migrations --templatedir _templates -f
- ``--module-first`` říká, že dokumentace modulu má být dřív než to, co obsahuje,
- ``-o`` je výstupní složka příkazu,
- ``..`` prochází složku mamweb,
- ``../*/migrations`` ignoruje migrace
- ``--templatedir _templates`` určuje templaty, podle kterých se vyrábí rst z Pythoní dokumentace a struktury složek a souborů,
- ``-f`` donutí phinx znovu přegenerovat soubory, protože nepozná, že se nějaká dokumentace změnila)
Poté se spustí „samotný sphinx“ a vygenerují se soubory v ``_build/html``.
Templates
---------
Templaty jsou originální s pár změnami:
- Změnil jsem nadpisy
- Odstranil jsem některá slova v nadpisech (module, package, …)
- Odstranil jsem nadpis Subpackages
přišlo mi to takhle lepší. Ale stále nejsem moc spokojen, protože je to pořád nepřehledné.

View file

@ -1,32 +0,0 @@
Co kde najít (mamweb + django)
==============================
Nejdůležitější aplikace z pohledu djanga je ``mamweb``. Tu totiž django pouští
a obsahuje tedy nastavení (tam se přidávají ostatní aplikace, včetně těch
importovaných z djanga, a nastavují se tam různé věci jak v djangu, tak i naše,
například složky, kam se budou věci přidané uživateli ukládat). Dále obsahuje
základní urls, udávající, „na jaké adrese co je“. A nakonec obsahuje obecné
věci jako chybové hlášky a vzhled M&M stránek (menu, patička, atd.). Aktuálně
i veškeré csv.
Další jsou pak jednotlivé aplikace (pokud něco hledáte, tak zřejmě chcete najít
tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url).
**TLDR: Nevšímejte si složek data/ seminar/ a souborů přímo v kořenové složce.**
Kromě věcí potřebných ke gitu, :doc:`ke spuštění <vyvoj>` a fukci djanga,
dalších drobností, lokální databáze a již zmíněných aplikací jsou tu ``data``,
kde je takový ten obsah webu, co by se měl dát snadno měnit (tudíž musí být v
databázi), tj. statické stránky, menu a obrázky v pozadí menu. Ten je třeba
měnit hlavně na produkci a sekundárně tady (může to dělat i newebař a nechcete
přepsat jeho práci). Vše, co nejsou aplikace je popsáno :doc:`tady <dalsi_soubory>`.
Ještě je tu aplikace ``seminar/``, kde bylo původně skoro všechno, a tak nám
tam zbývá spoustu historických migrací (čehož se jen tak nezbavíme).
Základy djanga
--------------
mamweb je psaný téměř čistě v djangu. Což znamená, že to „co je vidět na stránkách“
jsou views.

View file

@ -1,184 +0,0 @@
Lokální vývoj mamwebu
=====================
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 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.)
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 <https://git-scm.com>`_
- `Python <https://python.org>`_
- 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``, protože jsou potřeba všechny db backendy)
- 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 <https://gitea.ks.matfyz.cz>`_, kde
bydlí gitový repozitář s kódem.
.. tip:: Potřebné balíčky v různých distribucích jsou sepsané v :ref:`tabulce
prerekvizit <Alternativní jména balíčků>`.
Doporučené
^^^^^^^^^^
- Python wheel (možná řeší problémy s potřebou ``-dev`` balíčků…)
- Editor / IDE podporující `Editorconfig <https://editorconfig.org/>`_
- Uživatelská zkušenost s `produkční verzí webu <https://mam.matfyz.cz>`_
- Účet v `Kanci <https://kanboard.ledoian.cz>`_
.. 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
<https://gitea.ks.matfyz.cz/mam/mamweb>`_.) 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 `<http://127.0.0.1:8000>`_.
Časté problémy
^^^^^^
- ``make/install_web`` vypíše ``Error: pg_config executable not found.``:
Chybí ``libpq-dev``
- Chybová hláška obsahuje ``#include <Python.h>``: 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 :-)
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é, ostatní najdeš v :ref:`Skripty pro práci s repozitářem`.
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í
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 <soubor/y>`` nahraje data ze souborů do databáze
- ``./manage.py dumpdata <model>`` 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
<https://docs.djangoproject.com/en/3.2/ref/django-admin/#available-commands>`_
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:o````o3:o``
- Řešitelské účty: ``r:r``, ``r1:r````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 :-)
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, …)

View file

@ -116,7 +116,7 @@ Aktuálně: Jakýsi coding style zhruba existuje, není popsaný, šíří se li
- Nesmí být striktně vynucovaný
- Musel by být hodně nastavitelný
- Nechceme mít kód plný `#NOQA: WTF42`
- Nejspíš vždycky bude mít false positives (`tvorba.utils.roman_numerals`) i false negatives (`tvorba.models.Cislo.posli_cislo_mailem`)
- Nejspíš vždycky bude mít false positives (`seminar.utils.roman_numerals`) i false negatives (`seminar.models.tvorba.Cislo.posli_cislo_mailem`)
- Možná dobrý sluha, ale určitě špatný pán (also: špatná zkušenost ☺)
- __Důsledek:__ Hrozí, že těch falešných varování bude moc, čímž to ztratí smysl úplně
- Potenciálně by šlo aplikovat jen lokálně na změny?

View file

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

View file

@ -1,97 +0,0 @@
Závislosti webu
@@@@@@@@@@@@@@@
Web ke svému běhu potřebuje různé další programy. Tahle stránka se snaží je pokrýt.
Stránka je koncipována jako odrážkový seznam balíčků pro Ubuntu s případnými
komentáři, na konci stránky jsou uvedena :ref:`jména balíčků <Alternativní jména
balíčků>` v různých dalších distribucích. (Seznam mj. cílí na lokální
rozchození, proto popisuji Ubuntu a ne Debian. I tak se ale snažíme popsat web
v úplnosti.)
.. I use Arch, btw.
Základ webu
===========
- ``python3`` Ideálně Python 3.9, jenž je na Gimlim
- ``python3-pip`` pro instalaci dalších Pythoních balíčků podle ``requirements.txt``
- ``python3-venv``
- ``gcc`` kompilace Pythoních knihoven ze zdrojových distribucí (sdist), možná (neotestováno) jde jako alternativu použít ``python3-wheel`` a stahovat bdists
- ``python3-dev`` taktéž
- ``libpq-dev`` do třetice…
- ``ghostscript`` TODO konverze PDF v korekturovátku
- ``pdflatex`` FIXME! generování obálek a stvrzenek
- ``git`` používán :ref:`Make skripty`
- ``locales`` pro české formáty
Nasazení na produkci / testweb
==============================
(nejsou nutně potřeba k provozu lokální instance)
- ``rsync``
- ``pg_utils`` FIXME
- ``htpasswd`` FIXME aby testweb nepoužívali náhodní kolemjdoucí
- ``postgresql-server`` TODO
- ``acl`` pro nastavování práv přes ``setfacl``
Pro testweb je potřeba i všechno pro :ref:`dokumentaci <Dokumentace>`, vizte níž.
Předpokládá se nasazení v uWSGI pod Nginxem a služba běžící pod systemd, nicméně to už je spíš záležitost infrastruktury a ne specifikum mamwebu.
Dokumentace
===========
- ``make`` pro zbuildění
- Pythoní balíčky podle příslušné části ``requirements.txt``
Vývojové nástroje
=================
(Nejsou nezbytně nutné, ale předpokládáme jejich užitečnost. Mohou se hodit i na produkci.)
- ``psql`` TODO pro manuální dotazy do PostgreSQL
- ``sqlite3`` TODO totéž pro SQLite3
- ``ssh``
- ``graphviz`` pro vygenerování schématu
- ``rsync``
- ``ipython3`` hezčí interaktivní shell (stačí z ``requirements.txt``)
Potenciální usnadnění života
============================
(Úplně zbytečné, ale sdílíme pozitivní zkušenosti :-))
- ``tea`` CLI klient pro Giteu, aby člověk nepotřeboval otevírat web pro založení PR
Alternativní jména balíčků
==========================
Různé distribuce balí SW různě, takže to, co je v jedné distribuci jeden
balíček může být v jiné rozděleno do víc. Pro usnadnění nasazení je tady
přehled známých alternativních jmen.
TODO: tabulka není úplná. Pokud na něco narazíte, tak ji prosím doplňte.
.. admonition:: Jak se pozná, že web funguje, pro účely tabulky?
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 nefunguje synchronizace flatpages (nemáme SSH klíč),
``make/init_local`` sestřelíme při pokusu o synchronizaci 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."

View file

@ -1,86 +0,0 @@
Zkratky aplikací ve zdrojácích
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Ve zdrojácích (zejména různé ``models.py``, ``views.py`` ap.) používáme spoustu
modelů. Někdy je praktičtější / někdo preferuje importovat celou aplikaci jako
jedno jméno a používat modely bez explicitních importů, tj::
# „hromadné“ importy:
import personalni.models as p
...
p.Organizator.objects.all()
# „explicitní“ importy:
from personalni.models import Organizator
...
Organizator.objects.all()
Na webschůzce 2024-11-05 jsme na toto téma otevřeli diskusi, tady je její závěr.
.. admonition:: Historické okénko
:class: note
Kdysi jsme měli (prakticky) všechny modely v jedné aplikaci, ``seminar``. Na
různých místech se pak ``seminar.models`` importovalo typicky jako ``s``
nebo jako ``m``.
Přirozeně, toto už nejde tak snadno, protože už neexistuje jedno místo, ze
kterého chceme tahat modely v kódu.
Konvence
========
Shodli jsme se, že nám rozhodně nevadí explicitní importy a z pohledu
čitelnosti je preferujeme. Nicméně při psaní kódu to některým webařům přijde
nepohodlné, takže očekáváme, že bude existovat spousta kódu, která bude chtít
importovat hromadně. Usnesli jsme se proto na následujících kanonických
zkratkách, aby se aplikace alespoň zkracovaly konzistentně.
V závorkách je uvedené případné jméno, ale nepředpokládáme, že někdo bude danou
aplikaci chtít importovat hromadně. Některé aplikace zkratku nemají, ty se
importují vždy pod plným jménem nebo explicitně.
.. list-table::
:header-rows: 1
* - Model
- Zkratka
* - ``aesop``
- ---
* - ``api``
- --- (``api``)
* - ``galerie``
- ``g``
* - ``header_fotky``
- --- (``hdr``)
* - ``korektury``
- ``kor``
* - ``novinky``
- ``nov``
* - ``odevzdavatko``
- ``odev``
* - ``personální``
- ``pers``/``p``
* - ``sifrovacka``
- (``sifr``)
* - ``soustredeni``
- ``sou``
* - ``treenode``
- ``tn``
* - ``tvorba``
- ``tv``
* - ``various``
- ``v``/``var``
* - ``vyroci``
- ---
* - ``vysledkovky``
- ``vysl``
.. admonition:: O všech modelech pod jedním jménem
:class: warning
Historické okénko výš zatajuje jeden detail: Při práci v shellu se hodí mít
modely k dispozici a nemuset přemýšlet nad dělením do aplikací, takže ve
skutečnosti existuje ``mamweb.vsechno``, jenž všechny modely obsahuje.
Z čitelnostních důvodů je ale *zakázáno* tento modul používat v kódu.

25
galerie/TODO Normal file
View file

@ -0,0 +1,25 @@
========
| TODO |
|======|
Aktualni
* co s titulni fotkou
* do CSS
* nahledy
* nastylovat tabulku s nahledy
* komentare uz na nahledy?
* detail
* nahledy pred a po
* opravit prechodove sipky
* vyrobit prechodove sipky ve M&M-stylu
Dlouhodobe
* sipky na prechazeni mezi fotkami
* hromadne PRIDANI fotek do jiz existujici galerie
Fylozoficke
* zvolit velikosti velke a male fotky
* je potreba i jine razeni nez automaticky podle casu nebo staci podgalerie?
* napr. dve hry na dvou ruznych mistech ve stejny cas
* fotky od ucastniku ze hry (skupinky se pohybuji ve stejny cas, ale maji sled fotek) -- nestaci to pripadne vrazit do podgalerii?

View file

@ -1,3 +1,5 @@
#coding: utf-8
from galerie.models import Obrazek, Galerie
from django.contrib import admin
from django.http import HttpResponseRedirect

View file

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from autocomplete_light import shortcuts as autocomplete_light
from .models import Obrazek, Galerie
from .views import cesta_od_korene
class ObrazekAutocomplete(autocomplete_light.AutocompleteModelBase):
model = Obrazek
search_fields = ['nazev', 'popis']
split_words = True
limit_choices = 15
attrs = {
# This will set the input placeholder attribute:
'placeholder': u'Obrázek',
# This will set the yourlabs.Autocomplete.minimumCharacters
# options, the naming conversion is handled by jQuery
'data-autocomplete-minimum-characters': 1,
}
choice_html_format = '''
<span class="block" data-value="{}">
<span class="block">
{}
<span class="block">{}</span>
</span>
</span>
'''
def choice_label(self, obrazek):
cesta = "/".join(g.nazev for g in cesta_od_korene(obrazek.galerie))
popis = "{}<br>".format(obrazek.popis) if obrazek.popis else ""
return '{}<br>{}{}'.format(obrazek.nazev, popis, cesta)
def choice_html(self, obrazek):
"""Vrátí kus html i s obrázkem, které se pak ukazuje v nabídce"""
return self.choice_html_format.format(self.choice_value(obrazek),
obrazek.obrazek_maly_tag(), self.choice_label(obrazek))
widget_attrs={
'data-widget-maximum-values': 15,
'class': 'modern-style',
}
autocomplete_light.register(ObrazekAutocomplete)

View file

@ -1,4 +1,7 @@
#coding: utf-8
from django import forms
from seminar.models import Soustredeni
class KomentarForm(forms.Form):
komentar = forms.CharField(label = "Komentář:", max_length = 300, required=False)

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-30 21:40
from __future__ import unicode_literals

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-10 21:58
from __future__ import unicode_literals

View file

@ -1,13 +0,0 @@
# Generated by Django 4.2.11 on 2024-04-30 21:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('galerie', '0010_auto_20200819_0947'),
]
operations = [
]

View file

@ -1,20 +0,0 @@
# Generated by Django 4.2.11 on 2024-05-01 13:07
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0001_split_from_seminar'),
('galerie', '0011_pre_split_soustredeni'),
]
operations = [
migrations.AlterField(
model_name='galerie',
name='soustredeni',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='soustredeni.soustredeni'),
),
]

View file

@ -1,14 +0,0 @@
# Generated by Django 4.2.11 on 2024-05-01 13:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('galerie', '0012_soustredeni_relink'),
('soustredeni', '0003_post_split_soustredeni'),
]
operations = [
]

View file

@ -1,11 +1,14 @@
# coding: utf-8
from django.db import models
#from django.db.models import Q
from django.utils.encoding import force_text
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFit, Transpose
import os
from soustredeni.models import Soustredeni
from seminar.models import Soustredeni
VZDY=0
ORG=1

View file

@ -1,186 +0,0 @@
@charset "utf-8"; /* vynuť utf-8 */
/* Galerie */
/* velká fotka */
/* zmenšování spolu s oknem prohlížeče */
.galerie .obrazek, .titulni_obrazek {
max-width: 100%;
height: auto;
width: auto\9; /* ie8 */
}
.predchozi_obrazek{
position: absolute;
z-index: 1;
width: 33%;
height: 100%;
left: 0;
top: 0;
}
.predchozi_obrazek:hover{
background-image: url("/static/galerie/prvky/predchozi.svg");
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
background-position: left center;
background-repeat: no-repeat;
}
.dalsi_obrazek{
position: absolute;
z-index: 1;
width: 33%;
height: 100%;
left: 67%;
top: 0;
}
.dalsi_obrazek:hover{
background-image: url("/static/galerie/prvky/dalsi.svg");
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
background-position: right center;
background-repeat: no-repeat;
}
.galerie {
position: relative;
text-align: center;
margin: 20px auto 0 auto;
}
.galerie h1 {
text-align: center;
}
.galerie_hlavicka {
margin: 30px auto 30px auto;
}
.popis {
margin: 10px 10px 30px 0px;
text-align: center;
}
#nahoru {
text-align: center;
}
/* titulní obrázek hlavní galerie soustředění */
.galerie_nahledy{
/*margin: 1em 0;*/
margin: auto;
padding: 10px;
text-align: center;
overflow: auto;
}
.galerie_nahledy img {
margin: 10px;
}
.galerie_nahledy div.navigace {
display: inline-block;
}
.galerie_nahled, .podgalerie_nahled { /* frame */
display: block;
position: relative;
float: left;
width: 200px;
height: 200px;
text-align: center;
border: solid;
border-width: 1px;
border-radius: 4px;
border-color: var(--svetla-oranzova);
background-color: var(--barva-pozadi);
white-space: nowrap;
margin: 10px;
font-weight: bold;
}
.galerie_nahled:hover, .podgalerie_nahled:hover {
background-color: var(--svetla-oranzova);
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
color: var(--tmava-oranzova);
}
.vystredeno{ /* helper */
display: inline-block;
height: 100%;
vertical-align: middle;
}
.galerie_nahled img {
vertical-align: middle;
max-height: 180px;
max-width: 180px;
}
.galerie_nahled div {
position: absolute;
bottom: 0px;
width: 100%;
text-align: center;
}
.podgalerie_nahled img {
margin-top: 20px;
margin-bottom: 15px;
max-height: 125px;
max-width: 167px;
}
.podgalerie_nahled .nazev_galerie {
position: absolute;
width: 100%;
top: 160px;
}
/* Odkazy na předchozí a následující podgalerii */
.galerie_predchozi_nasledujici {
overflow: auto;
margin: 10px auto 10px auto;
}
.galerie_predchozi_nasledujici .predchozi {
float: left;
}
.galerie_predchozi_nasledujici .nasledujici {
float: right;
}
/* 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 {
position: absolute;
width: 0;
height: 55px; /* viz #title */
margin-top: -55px; /* viz #title */
}
@media(max-width: 860px) {
.kotva_obrazku {
height: 3em; /* #FIXME nemám páru, jak zjistit výšku toho elementu */
margin-top: -3em; /* #FIXME */
}
}
/* plus a minus tlacitka */
.mam-org-only-galerie {
background: var(--orgovska-svetla-fialova);
padding: 10px;
margin: 10px 10px 10px -20px;
border: #333 2px dashed;
float: left;
}
.mam-org-only-galerie a{
padding: 3px 5px;
margin: 5px;
border-radius: 20px;
background-color: var(--tmava-oranzova);;
color: var(--barva-pozadi);
float: left;
}

View file

@ -1,4 +1,4 @@
{% extends "galerie/base.html" %}
{% extends "base.html" %}
{% block nadpis1a %}

View file

@ -1,4 +1,4 @@
{% extends "galerie/base.html" %}
{% extends "base.html" %}
{% block nadpis1a %}
Galerie {{galerie.nazev}}

View file

@ -1,4 +1,4 @@
{% extends "galerie/base.html" %}
{% extends "base.html" %}
{% block content %}

View file

@ -1,6 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% block custom_css %}
<link href="{% static 'css/galerie.css' %}?version=1" rel="stylesheet">
{% endblock %}

View file

@ -1,5 +1,7 @@
# coding: utf-8
from django.urls import path
from personalni.utils import org_required
from seminar.utils import org_required
from . import views
urlpatterns = [

View file

@ -1,3 +1,5 @@
# coding: utf-8
import random
from django.http import HttpResponse, Http404
@ -6,7 +8,7 @@ from django.template import RequestContext
from datetime import datetime
from galerie.models import Obrazek, Galerie
from soustredeni.models import Soustredeni
from seminar.models import Soustredeni
from galerie.forms import KomentarForm, NewGalerieForm
def zobrazit(galerie, request):

View file

@ -1,6 +0,0 @@
"""
Aplikace umožňující na uživatelské úrovni měnit obrázek v pozadí meníčka.
Umožňuje uložit obrázek a následně popsat, na kterých stránkách (podle url)
a v který čas (den/noc) se zobrazovat.
"""

View file

@ -4,11 +4,7 @@ import header_fotky.models as m
class FotkaPozadiAdmin(ModelAdmin):
"""
Nastaví čas vložení (:attr:`~header_fotky.models.FotkaHeader.cas`) jako
readonly = neměnitelný
"""
readonly_fields = ['cas']
admin.site.register(m.FotkaHeader, FotkaPozadiAdmin)
admin.site.register(m.FotkaUrlVazba)
admin.site.register(m.FotkaUrlVazba)

View file

@ -3,4 +3,3 @@ from django.apps import AppConfig
class HeaderFotkyConfig(AppConfig):
name = 'header_fotky'
verbose_name = 'Fotky v záhlaví'

View file

@ -1,8 +1,4 @@
"""
Context processory lze přidat do djanga v :mod:`~mamweb.settings` a dělají to,
že do contextu (tj. to, z čeho se např. berou proměnné v templatech) libovolné
stránky přidají další věci.
"""
from datetime import datetime, date
import random
@ -12,46 +8,38 @@ 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.
''' Podle casu prida do templatu, zdali je nebo neni noc '''
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
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 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
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()
# 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]
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"
if fotka is None:
fotka = settings.STATIC_URL + "images/header/vikendovka.jpg"
return {'noc': noc, 'fotka': fotka}
return {'noc': noc, 'fotka': fotka}

View file

@ -4,7 +4,6 @@ from django.utils import timezone
class FotkaHeader(models.Model):
""" fotka pro :mod:`fotka_header`, konkrétně :class:`~fotka_header.models.FotkaUrlVazba` """
class Meta:
ordering = ['-cas']
db_table = 'fotky_header'
@ -12,34 +11,27 @@ class FotkaHeader(models.Model):
verbose_name_plural = u'fotky do pozadí menu'
cas = models.DateTimeField(u'čas vložení fotky', default=timezone.now, help_text='Čas vložení fotky')
""" čas vložení fotky """
nazev = models.CharField(
u'název fotky', null=False, blank=False, unique=True, primary_key=True,
max_length=50, help_text='Název např. archiv_noc'
)
""" jméno fotky na webu """
fotka = models.ImageField(upload_to='header', null=False, blank=False)
""" soubor fotky (Nahrává se automaticky do složky ``media``.) """
def __str__(self):
return self.nazev
def clean(self):
""" kontroluje, zda sedí poměr stran """
if not self.fotka:
raise ValidationError("Chybí obrázek")
""" Kontroluje, zda sedí poměr stran """
if abs(self.fotka.width - (self.fotka.height * 970 / 350)) > 2:
raise ValidationError("Obrázek by měl mít rozměry 970w na 350h, nebo alespoň podobný poměr stran.")
super().clean()
class FotkaUrlVazba(models.Model):
"""
spojení :class:`~fotka_header.models.FotkaHeader` a url
(resp. prefixu url), na které být zobrazována
"""
class Meta:
ordering = ['url']
db_table = 'fotky_url_vazby'
@ -50,13 +42,11 @@ class FotkaUrlVazba(models.Model):
u'URL', blank=True, null=False, max_length=100,
help_text='url prefix stránek např: /archiv/ nebo /'
)
""" url prefix stránek, kde má být fotka zobrazena, např: /archiv/ nebo / """
fotka = models.ForeignKey(
FotkaHeader, blank=False, null=False, verbose_name='fotka',
on_delete=models.CASCADE
)
""" :class:`~fotka_header.models.FotkaHeader`, která má být zobrazována """
DOBA_DEN = 'den'
DOBA_NOC = 'noc'
@ -67,11 +57,9 @@ class FotkaUrlVazba(models.Model):
(DOBA_OBOJI, 'Zobrazovat pořád')]
denni_doba = models.CharField('denní doba', max_length=16, choices=DOBA_CHOICES, blank=False, default=DOBA_OBOJI)
""" dení doba, kdy tam má být zobrazována """
def __str__(self):
return self.url
def url_fotky(self):
""" vrací url fotky (tj. url, kde reálně získám soubor fotky) """
return self.fotka.fotka.url

View file

@ -1,4 +1,3 @@
FIXME přepsat do rst, přidat i další věci a případně přesunout na wiki
Přidání obrázků do odměn:
admin -> flatpage odměn -> ikona přidat obrázek
záložka odeslat, vybrat obrázek, odeslat

11
korektury/TODO Normal file
View file

@ -0,0 +1,11 @@
- korektura potrebuje reakci
+ komentáře fixně na username
- používat skutečné jméno?
- vyžádat pozornost autora obsahu
- zvednout upload limit na 5MB
- sbalit a rozbalit korekturu
- nahrávání jiných věcí než PDF - kontrolovat?
- stylování
- seznam PDF - co zobrazovat?

View file

@ -1,5 +0,0 @@
"""
Seznam pdf k opravě a `opraf`_ v djangu místo PHP.
.. _opraf: https://github.com/vitstradal/opraf
"""

View file

@ -1,23 +1,16 @@
from django.contrib import admin
from reversion.admin import VersionAdmin
from korektury.models import KorekturovanePDF, Oprava, KorekturaTag
from korektury.models import KorekturovanePDF
from django.core.mail import EmailMessage
from django.core.mail import send_mail
from django.urls import reverse
# Register your models here.
class KorekturovanePDFAdmin(VersionAdmin):
"""
nastaví čas vložení (:attr:`~koretkury.models.KorekturovanePDF.cas`) a počet
stran (:attr:`~koretkury.models.KorekturovanePDF.stran`) jako readonly =
neměnitelný
Při prvním uložení pošle e-mail.
"""
readonly_fields = ['cas', 'stran']
def get_readonly_fields(self, request, obj=None):
""" Když pdf existuje, tak nedovolím měnit ani pdf."""
if obj:
return self.readonly_fields + ['pdf']
return self.readonly_fields
@ -25,18 +18,14 @@ class KorekturovanePDFAdmin(VersionAdmin):
fieldsets = [
(None,
{'fields':
['pdf', 'cas', 'stran', 'nazev', 'orgove', 'komentar', 'poslat_mail']}),
['pdf', 'cas', 'org', 'stran', 'nazev', 'komentar', 'poslat_mail']}),
# (u'PDF', {'fields': ['pdf']}),
]
list_display = ['nazev', 'cas', 'stran']
list_display = ['nazev', 'cas', 'stran', 'org']
list_filter = []
search_fields = []
autocomplete_fields = ['orgove']
def save_model(self, request, obj, form, change):
"""
Pokud je soubor nový a se poslat e-mail, tak pošle e-mail o novém pdf.
"""
super().save_model(request, obj, form, change)
if not change and obj.poslat_mail: # Je nový a má se poslat mail
odkaz = request.build_absolute_uri(reverse('korektury', kwargs={'pdf': obj.id}))
@ -54,19 +43,6 @@ Popis souboru:
S pozdravem a korekturám zdar!
Korekturovátko
'''
EmailMessage(
subject=predmet,
body=text,
from_email=odesilatel,
to=[prijemce],
).send()
send_mail(predmet,text,odesilatel,[prijemce])
admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin)
class OpravaAdmin(admin.ModelAdmin):
model = Oprava
filter_horizontal = ("informovani_orgove", "tagy",)
admin.site.register(Oprava, OpravaAdmin)
admin.site.register(KorekturaTag)

View file

@ -1,7 +0,0 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'korektury.api'
label = 'korektury_api' # Protože jedno api už máme.

View file

@ -1,11 +0,0 @@
from django.urls import path
from personalni.utils import org_required
from . import views
urlpatterns = [
path('<int:pdf_id>/stav', org_required(views.korektury_stav_view), name='korektury_api_pdf_stav'),
path('oprava/stav', org_required(views.oprava_stav_view), name='korektury_api_oprava_stav'),
path('<int:pdf_id>/opravy_a_komentare', org_required(views.opravy_a_komentare_view), name='korektury_api_opravy_a_komentare'),
path('oprava/smaz', org_required(views.oprava_smaz_view), name='korektury_api_oprava_smaz'),
path('komentar/smaz', org_required(views.komentar_smaz_view), name='korektury_api_komentar_smaz'),
]

View file

@ -1,129 +0,0 @@
from http import HTTPStatus
from django.http import JsonResponse, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.html import linebreaks
from rest_framework import serializers
from korektury.utils import send_email_notification_komentar
from korektury.models import Oprava, KorekturovanePDF, Komentar, KorekturaTag
from personalni.models import Organizator
def korektury_stav_view(request, pdf_id: int, **kwargs):
q = request.POST
pdf = get_object_or_404(KorekturovanePDF, id=pdf_id)
status = q.get('state')
if status is not None:
assert status in KorekturovanePDF.STATUS.values
pdf.status = status
pdf.save()
return JsonResponse({'status': pdf.status})
def oprava_stav_view(request, **kwargs):
q = request.POST
op_id_str = q.get('id')
assert op_id_str is not None
op_id = int(op_id_str)
op = get_object_or_404(Oprava, id=op_id)
status = q.get('action')
if status is not None:
assert status in Oprava.STATUS.values
op.status = status
op.save()
return JsonResponse({'status': op.status})
def oprava_smaz_view(request, **kwargs):
q = request.POST
op_id_str = q.get('oprava_id')
assert op_id_str is not None
op_id = int(op_id_str)
oprava = get_object_or_404(Oprava, id=op_id)
oprava.delete()
return HttpResponse(status=HTTPStatus.NO_CONTENT)
def komentar_smaz_view(request, **kwargs):
q = request.POST
kom_id_str = q.get('komentar_id')
assert kom_id_str is not None
kom_id = int(kom_id_str)
komentar = get_object_or_404(Komentar, id=kom_id)
komentar.delete()
return HttpResponse(status=HTTPStatus.NO_CONTENT)
class KomentarSerializer(serializers.ModelSerializer):
class Meta:
model = Komentar
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
ret["autor"] = str(instance.autor)
ret["text"] = linebreaks(ret["text"], autoescape=True) # Autora není třeba escapovat, ten se vkládá jako text.
return ret
class KorekturaTagSerializer(serializers.ModelSerializer):
class Meta:
model = KorekturaTag
fields = '__all__'
class OpravaSerializer(serializers.ModelSerializer):
class Meta:
model = Oprava
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
ret["komentare"] = [KomentarSerializer(komentar).data for komentar in instance.komentar_set.all()]
ret["tagy"] = [KorekturaTagSerializer(tag).data for tag in instance.tagy.all()]
return ret
# komentar_set = serializers.ListField(child=KomentarSerializer())
def opravy_a_komentare_view(request, pdf_id: int, **kwargs):
if request.method == 'POST':
q = request.POST
x = int(q.get('x'))
y = int(q.get('y'))
img_id = int(q.get('img_id'))
oprava_id = int(q.get('oprava_id'))
komentar_id = int(q.get('komentar_id'))
text = q.get('text')
# prirazeni autora podle prihlaseni
autor_user = request.user
# pokud existuje ucet (user), ale neni to organizator = 403
autor = Organizator.objects.filter(osoba__user=autor_user).first()
if komentar_id != -1:
komentar = get_object_or_404(Komentar, id=komentar_id)
komentar.text = text
komentar.autor = autor
komentar.save()
else:
if oprava_id != -1:
oprava = get_object_or_404(Oprava, id=oprava_id)
else:
pdf = get_object_or_404(KorekturovanePDF, id=pdf_id)
oprava = Oprava.objects.create(
pdf=pdf,
strana=img_id,
x=x,
y=y,
)
tagy_raw = q.get('tagy')
if tagy_raw != "":
tagy = list(map(int, tagy_raw.split(",")))
oprava.tagy.add(*KorekturaTag.objects.filter(id__in=tagy))
Komentar.objects.create(oprava=oprava, autor=autor, text=text)
send_email_notification_komentar(oprava, autor, request)
opravy = Oprava.objects.filter(pdf=pdf_id).all()
# Serializovat list je prý security vulnerability, tedy je přidán slovník pro bezpečnost
return JsonResponse({"context": [OpravaSerializer(oprava).data for oprava in opravy]})

13
korektury/forms.py Normal file
View file

@ -0,0 +1,13 @@
from django import forms
class OpravaForm(forms.Form):
text = forms.CharField(max_length=256)
autor = forms.CharField(max_length=20)
x = forms.IntegerField()
y = forms.IntegerField()
scroll = forms.CharField(max_length=256)
pdf = forms.CharField(max_length=256)
img_id = forms.CharField(max_length=256)
id = forms.CharField(max_length=256)
action = forms.CharField(max_length=256)

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-30 21:40
from __future__ import unicode_literals

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-10 21:58
from __future__ import unicode_literals

View file

@ -1,18 +0,0 @@
# 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'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 2.2.28 on 2023-06-19 19:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('korektury', '0019_auto_20221205_2014'),
]
operations = [
migrations.AlterField(
model_name='korekturovanepdf',
name='nazev',
field=models.CharField(help_text='Název (např. `22.1 | analyza v4` nebo `propagace | letacek v0`) korekturovaného PDF', max_length=50, verbose_name='název PDF'),
),
]

View file

@ -1,13 +0,0 @@
# Generated by Django 4.2.8 on 2024-03-12 20:24
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('korektury', '0020_lepsi_popis_nazvu_PDF_v_adminu'),
]
operations = [
]

View file

@ -1,30 +0,0 @@
# Generated by Django 4.2.11 on 2024-03-19 21:35
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('personalni', '0003_initial'),
('korektury', '0021_auto_20240312_2124'),
]
operations = [
migrations.AlterField(
model_name='komentar',
name='autor',
field=models.ForeignKey(blank=True, help_text='Autor komentáře', null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator'),
),
migrations.AlterField(
model_name='korekturovanepdf',
name='org',
field=models.ForeignKey(blank=True, default=None, help_text='Zodpovědný organizátor za obsah', null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator'),
),
migrations.AlterField(
model_name='oprava',
name='autor',
field=models.ForeignKey(blank=True, help_text='Autor opravy', null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator'),
),
]

View file

@ -1,14 +0,0 @@
# Generated by Django 4.2.11 on 2024-03-26 21:25
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('korektury', '0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more'),
('personalni', '0005_personalni_post_migrate'),
]
operations = [
]

Some files were not shown because too many files have changed in this diff Show more