Compare commits
61 commits
master
...
cb924f846f
Author | SHA1 | Date | |
---|---|---|---|
cb924f846f | |||
206d37e21c | |||
f707cc02f7 | |||
e85c333bf8 | |||
5cccd40800 | |||
ff04978a95 | |||
c2613af9a6 | |||
bbaf109bbe | |||
56ac6f74b6 | |||
2319087bcb | |||
06d0a8cce3 | |||
afc238ba74 | |||
c6805ad7b5 | |||
6ff0438874 | |||
1f96234a0f | |||
cdd1759976 | |||
1790c0faf7 | |||
0c45d8051f | |||
8939a26ec8 | |||
d40265ba74 | |||
8301cbdb4d | |||
c00f60b38f | |||
5878d1bf7e | |||
c96ada8b37 | |||
f3c38f1f02 | |||
1f9966c51f | |||
a346c4d49a | |||
9412a52567 | |||
7a02a826cd | |||
9c68eac050 | |||
15b17023de | |||
c89a982440 | |||
39afe79da7 | |||
5fcf9bac15 | |||
7dc0e1d71b | |||
9ac0d06e1e | |||
6bb39fc0a0 | |||
5c9ed5e5ed | |||
9a20fc7c79 | |||
e569346274 | |||
78923f5237 | |||
0fd3526a87 | |||
d240774022 | |||
acea74bc6e | |||
9b3cbb512c | |||
1af4a13a62 | |||
115589e770 | |||
2b52ec028e | |||
5346da5107 | |||
75a7e607d5 | |||
ebf8165c53 | |||
8c881621b0 | |||
666b455bbd | |||
e45c819424 | |||
8319b28272 | |||
6d3a70165f | |||
59d9589162 | |||
44b10449af | |||
3631ec3c5b | |||
1868f96594 | |||
f8379b8b67 |
288 changed files with 5163 additions and 4915 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -25,9 +25,6 @@ TODO
|
|||
# .htpasswd kvůli přihlášení
|
||||
.htpasswd
|
||||
|
||||
# .htpasswd pro AESOPa
|
||||
/.htpasswd-aesop
|
||||
|
||||
# reversion kvůli historii objektů v reversion
|
||||
**/reversion
|
||||
|
||||
|
@ -36,4 +33,4 @@ TODO
|
|||
|
||||
# dokumentace
|
||||
docs/_build
|
||||
docs/modules
|
||||
docs/modules
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
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
|
|
@ -1,30 +0,0 @@
|
|||
#!/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
|
|
@ -1,61 +0,0 @@
|
|||
#!/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
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Obsahuje vše, co se týká aesopu (exportu, který po nás vyžaduje OPMK).
|
||||
"""
|
|
@ -1,8 +0,0 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AesopConfig(AppConfig):
|
||||
name = 'aesop'
|
|
@ -1,30 +0,0 @@
|
|||
from django.http import HttpResponse
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
|
||||
class OvvpFile:
|
||||
def __init__(self):
|
||||
# { header: value, ... }
|
||||
self.headers = {}
|
||||
# [ 'column-name', ... ]
|
||||
self.columns = []
|
||||
# [ { column: value, ...}, ...]
|
||||
self.rows = []
|
||||
|
||||
def to_lines(self):
|
||||
# header
|
||||
for hk in sorted(self.headers.keys()):
|
||||
yield f'{hk}\t{self.headers[hk]}\n'
|
||||
yield '\n'
|
||||
# columns
|
||||
yield '\t'.join(self.columns) + '\n'
|
||||
# rows
|
||||
for r in self.rows:
|
||||
yield '\t'.join([force_text(r[c]) for c in self.columns]) + '\n'
|
||||
|
||||
def to_string(self):
|
||||
return ''.join(self.to_lines())
|
||||
|
||||
# Pozn: tohle je ta jediná funkce, která se reálně používá…
|
||||
def to_HttpResponse(self):
|
||||
return HttpResponse(self.to_string(), content_type='text/plain; charset=utf-8')
|
|
@ -1,27 +0,0 @@
|
|||
"""
|
||||
Soubor sloužící jako „router“, tj. zde se definují url adresy a na co ukazují:
|
||||
|
||||
- ``aesop-export/mam-rocnik-<int:prvni_rok>.csv`` (seminar_export_rocnik) :class:`~aesop.views.ExportRocnikView`
|
||||
- ``aesop-export/mam-sous-<str:datum_zacatku>.csv`` (seminar_export_sous) :class:`~aesop.views.ExportSousView`
|
||||
- ``aesop-export/index.csv`` (seminar_export_index) :class:`~aesop.views.ExportIndexView`
|
||||
"""
|
||||
from django.urls import path
|
||||
from aesop import views
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
'aesop-export/mam-rocnik-<int:prvni_rok>.csv',
|
||||
views.ExportRocnikView.as_view(),
|
||||
name='seminar_export_rocnik'
|
||||
),
|
||||
path(
|
||||
'aesop-export/mam-sous-<str:datum_zacatku>.csv',
|
||||
views.ExportSousView.as_view(),
|
||||
name='seminar_export_sous'
|
||||
),
|
||||
path(
|
||||
'aesop-export/index.csv',
|
||||
views.ExportIndexView.as_view(),
|
||||
name='seminar_export_index'
|
||||
),
|
||||
]
|
|
@ -1,16 +0,0 @@
|
|||
import datetime
|
||||
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from aesop.ovvpfile import OvvpFile
|
||||
|
||||
|
||||
def default_ovvpfile(event, rocnik):
|
||||
of = OvvpFile()
|
||||
of.headers['version'] = '1'
|
||||
of.headers['event'] = event
|
||||
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'
|
||||
return of
|
101
aesop/views.py
101
aesop/views.py
|
@ -1,101 +0,0 @@
|
|||
"""
|
||||
Soubor sloužící k deklaraci jednotlivých „views“ (nejčastěji funkce beroucí request
|
||||
a vracející :func:`django.shortcuts.render` respektive nějakou response, nebo
|
||||
třídy většinou rozšiřující nějakou třídu z :mod:`django.views.generic`)
|
||||
"""
|
||||
import django
|
||||
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_text
|
||||
|
||||
from .utils import default_ovvpfile
|
||||
from seminar.models import Rocnik, Soustredeni
|
||||
from vysledkovky import utils
|
||||
from seminar.utils import aktivniResitele
|
||||
|
||||
class ExportIndexView(generic.View):
|
||||
def get(self, request):
|
||||
ls = []
|
||||
for r in Rocnik.objects.filter(exportovat = True):
|
||||
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('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')
|
||||
|
||||
|
||||
class ExportSousView(generic.View):
|
||||
|
||||
def get(self, request, datum_zacatku=None):
|
||||
try:
|
||||
dz = django.utils.dateparse.parse_date(datum_zacatku)
|
||||
except:
|
||||
dz = None
|
||||
if dz is None:
|
||||
raise django.http.Http404()
|
||||
|
||||
s = get_object_or_404(Soustredeni, datum_zacatku=dz, exportovat=True)
|
||||
|
||||
akce = {Soustredeni.TYP_JARNI: 'MaM.sous.jaro',
|
||||
Soustredeni.TYP_PODZIMNI: 'MaM.sous.podzim',
|
||||
Soustredeni.TYP_VIKEND: 'MaM.vikend',
|
||||
}[s.typ]
|
||||
|
||||
of = default_ovvpfile(akce, s.rocnik)
|
||||
of.headers['x-event-begin'] = s.datum_zacatku.isoformat()
|
||||
of.headers['x-event-end'] = s.datum_konce.isoformat()
|
||||
of.headers['x-event-location'] = s.misto
|
||||
of.headers['comment'] = u'MaM-Web export ucastniku soustredeni v {x-event-location} od {x-event-begin} do {x-event-end}'.format(**of.headers)
|
||||
of.columns = ['id', 'name', 'surname', 'gender', 'email', 'end-year', 'school', 'school-name']
|
||||
|
||||
for u in s.ucastnici.all():
|
||||
of.rows.append(u.export_row())
|
||||
|
||||
return of.to_HttpResponse()
|
||||
|
||||
# POZOR! Předělání na nový model neotestováno v reálu (ale zase jen drobné změny)
|
||||
class ExportRocnikView(generic.View):
|
||||
|
||||
def get(self, request, prvni_rok=None):
|
||||
try:
|
||||
pr = int(prvni_rok)
|
||||
except:
|
||||
pr = None
|
||||
if pr is None:
|
||||
raise django.http.Http404()
|
||||
|
||||
rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True)
|
||||
cislo = rocnik.posledni_zverejnena_vysledkovka_cislo()
|
||||
resitele = aktivniResitele(cislo, True)
|
||||
slovnik_body = utils.secti_body_za_rocnik(cislo, resitele, False)
|
||||
setrizeni_resitele, body = utils.setrid_resitele_a_body(slovnik_body)
|
||||
|
||||
of = default_ovvpfile('MaM.rocnik', rocnik)
|
||||
of.headers['comment'] = u'MaM-Web export aktivnich resitelu rocniku {rocnik} do cisla {cislo}'.format(rocnik=rocnik, cislo=cislo)
|
||||
of.columns = ['id', 'name', 'surname', 'gender', 'born', 'email', 'end-year',
|
||||
'street', 'town', 'postcode', 'country', 'spam-flag', 'spam-date',
|
||||
'school', 'school-name', 'points', 'rank',]
|
||||
|
||||
resitele_slovnik = {}
|
||||
for r in resitele:
|
||||
resitele_slovnik[r.id] = r
|
||||
|
||||
# počítání pořadí řešitelů
|
||||
posledni_body = 100000
|
||||
posledni_poradi = 0
|
||||
for i in range(len(setrizeni_resitele)):
|
||||
rd = resitele_slovnik[setrizeni_resitele[i]].export_row()
|
||||
|
||||
if posledni_body > body[i]:
|
||||
posledni_body = body[i]
|
||||
posledni_poradi = i + 1
|
||||
rd['rank'] = posledni_poradi
|
||||
rd['points'] = body[i]
|
||||
|
||||
of.rows.append(rd)
|
||||
|
||||
return of.to_HttpResponse()
|
|
@ -1,6 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
import seminar.models as m
|
||||
from personalni.models import Skola
|
||||
import seminar.views as v
|
||||
from seminar.utils import sync_skoly
|
||||
from personalni.utils import sync_skoly
|
||||
|
||||
class OrgSkolyAutocompleteTestCase(TestCase):
|
||||
@classmethod
|
||||
|
@ -48,7 +48,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 = m.Skola.objects.get(id=id)
|
||||
spravna_skola = 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']]
|
||||
|
|
26
api/urls.py
26
api/urls.py
|
@ -1,30 +1,18 @@
|
|||
"""
|
||||
Soubor sloužící jako „router“, tj. zde se definují url adresy a na co ukazují:
|
||||
|
||||
- ``api/expor/skoly/`` (export_skoly) :func:`~api.views.exports.exportSkolView`
|
||||
- ``api/autocomplete/skola/`` (autocomplete_skola) :class:`~api.views.autocomplete.SkolaAutocomplete`
|
||||
- ``api/autocomplete/resitel/`` (autocomplete_resitel) :class:`~api.views.autocomplete.ResitelAutocomplete`
|
||||
- ``api/autocomplete/problem/odevzdatelny`` (autocomplete_problem_odevzdatelny) :class:`~api.views.autocomplete.OdevzdatelnyProblemAutocomplete`
|
||||
|
||||
Na autocomplete v3 čeká:
|
||||
|
||||
- ``autocomplete/organizatori/`` (seminar_autocomplete_organizator) :class:`~api.views.autocomplete.OrganizatorAutocomplete`
|
||||
"""
|
||||
from django.urls import path
|
||||
from . import views
|
||||
from seminar.utils import org_required
|
||||
from personalni.utils import org_required
|
||||
|
||||
urlpatterns = [
|
||||
# Export škol
|
||||
path('api/export/skoly/', views.exportSkolView, name='export_skoly'),
|
||||
path('export/skoly/', views.exportSkolView, name='export_skoly'),
|
||||
|
||||
|
||||
# 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'),
|
||||
path('autocomplete/skola/', views.SkolaAutocomplete.as_view(), name='autocomplete_skola'),
|
||||
path('autocomplete/resitel/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'),
|
||||
path('autocomplete/resitel_public/', views.PublicResitelAutocomplete.as_view(), name='autocomplete_resitel_public'),
|
||||
path('autocomplete/problem/odevzdatelny', views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'),
|
||||
path('autocomplete/problem/vsechny', views.ProblemAutocomplete.as_view(), name='autocomplete_problem'),
|
||||
|
||||
# Ceka na autocomplete v3
|
||||
# path('autocomplete/organizatori/',
|
||||
|
|
|
@ -1,7 +1,2 @@
|
|||
"""
|
||||
Soubory sloužící k deklaraci jednotlivých „views“ (nejčastěji funkce beroucí request
|
||||
a vracející :func:`django.shortcuts.render` respektive nějakou response, nebo
|
||||
třídy většinou rozšiřující nějakou třídu z :mod:`django.views.generic`)
|
||||
"""
|
||||
from .autocomplete import *
|
||||
from .exports import *
|
||||
|
|
|
@ -5,7 +5,9 @@ from dal import autocomplete
|
|||
from django.shortcuts import get_object_or_404
|
||||
from django.db.models import Q
|
||||
|
||||
import seminar.models as m
|
||||
from personalni.models import Skola, Resitel
|
||||
from tvorba.models import Problem
|
||||
from seminar.models.nastaveni import Nastaveni
|
||||
from .helpers import LoginRequiredAjaxMixin
|
||||
|
||||
# TODO filosofie - zkratky, jak v databázi, tak ve vyhledávání (SPŠE, GASOŠ, Kpt., soukr)
|
||||
|
@ -13,7 +15,7 @@ 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 = m.Skola.objects.all()
|
||||
qs = Skola.objects.all()
|
||||
if self.q:
|
||||
words = self.q.split(' ') #TODO re split podle bileho znaku
|
||||
partq = Q()
|
||||
|
@ -31,7 +33,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView):
|
|||
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 = m.Resitel.objects.all()
|
||||
qs = Resitel.objects.all()
|
||||
if self.q:
|
||||
parts = self.q.split()
|
||||
query = Q()
|
||||
|
@ -51,8 +53,8 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer
|
|||
především v odevzdávátku.
|
||||
"""
|
||||
def get_queryset(self):
|
||||
letos = m.Nastaveni.get_solo().aktualni_rocnik
|
||||
qs = m.Resitel.objects.filter(
|
||||
letos = Nastaveni.get_solo().aktualni_rocnik
|
||||
qs = Resitel.objects.filter(
|
||||
rok_maturity__gte=letos.druhy_rok()
|
||||
).filter(
|
||||
prezdivka_resitele__isnull=False
|
||||
|
@ -70,7 +72,7 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer
|
|||
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 = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY)
|
||||
qs = Problem.objects.filter(stav=Problem.STAV_ZADANY)
|
||||
if self.q:
|
||||
qs = qs.filter(
|
||||
Q(nazev__icontains=self.q))
|
||||
|
@ -87,12 +89,12 @@ 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(m.Nastaveni)
|
||||
nastaveni = get_object_or_404(Nastaveni)
|
||||
rocnik = nastaveni.aktualni_rocnik
|
||||
temaQ = Q(Tema___rocnik = rocnik)
|
||||
ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik)
|
||||
clanekQ = Q(Clanek___cislo__rocnik=rocnik)
|
||||
qs = m.Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev")
|
||||
qs = Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev")
|
||||
if self.q:
|
||||
qs = qs.filter(
|
||||
Q(nazev__icontains=self.q))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import seminar.models as m
|
||||
from personalni.models import Skola
|
||||
from django.core import serializers as ser
|
||||
from django.http import HttpResponse
|
||||
def exportSkolView(request):
|
||||
|
@ -8,7 +8,7 @@ def exportSkolView(request):
|
|||
# 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
|
||||
skoly_json = ser.serialize("json", m.Skola.objects.all(), fields=fields)
|
||||
skoly_json = ser.serialize("json", Skola.objects.all(), fields=fields)
|
||||
response = HttpResponse(
|
||||
content = skoly_json,
|
||||
content_type = 'text/json',
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
if test "$#" -lt 1
|
||||
then
|
||||
echo "Usage: $0 file ..."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for file in "$@"
|
||||
do
|
||||
# Do the sed magic: keep replacing 4 spaces at the begining of line
|
||||
sed -i -re '
|
||||
: loop
|
||||
s/^( *) /\1 /
|
||||
t loop
|
||||
' "$file"
|
||||
done
|
600
data/prava_skupin.json
Normal file
600
data/prava_skupin.json
Normal file
|
@ -0,0 +1,600 @@
|
|||
[
|
||||
{
|
||||
"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"
|
||||
],
|
||||
[
|
||||
"delete_fotkaheader",
|
||||
"header_fotky",
|
||||
"fotkaheader"
|
||||
],
|
||||
[
|
||||
"view_fotkaheader",
|
||||
"header_fotky",
|
||||
"fotkaheader"
|
||||
],
|
||||
[
|
||||
"add_fotkaurlvazba",
|
||||
"header_fotky",
|
||||
"fotkaurlvazba"
|
||||
],
|
||||
[
|
||||
"change_fotkaurlvazba",
|
||||
"header_fotky",
|
||||
"fotkaurlvazba"
|
||||
],
|
||||
[
|
||||
"delete_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"
|
||||
],
|
||||
[
|
||||
"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"
|
||||
],
|
||||
[
|
||||
"change_skola",
|
||||
"personalni",
|
||||
"skola"
|
||||
],
|
||||
[
|
||||
"view_skola",
|
||||
"personalni",
|
||||
"skola"
|
||||
],
|
||||
[
|
||||
"add_hlasovani",
|
||||
"prednasky",
|
||||
"hlasovani"
|
||||
],
|
||||
[
|
||||
"change_hlasovani",
|
||||
"prednasky",
|
||||
"hlasovani"
|
||||
],
|
||||
[
|
||||
"delete_hlasovani",
|
||||
"prednasky",
|
||||
"hlasovani"
|
||||
],
|
||||
[
|
||||
"view_hlasovani",
|
||||
"prednasky",
|
||||
"hlasovani"
|
||||
],
|
||||
[
|
||||
"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"
|
||||
],
|
||||
[
|
||||
"change_nastaveni",
|
||||
"seminar",
|
||||
"nastaveni"
|
||||
],
|
||||
[
|
||||
"view_nastaveni",
|
||||
"seminar",
|
||||
"nastaveni"
|
||||
],
|
||||
[
|
||||
"add_novinky",
|
||||
"seminar",
|
||||
"novinky"
|
||||
],
|
||||
[
|
||||
"change_novinky",
|
||||
"seminar",
|
||||
"novinky"
|
||||
],
|
||||
[
|
||||
"delete_novinky",
|
||||
"seminar",
|
||||
"novinky"
|
||||
],
|
||||
[
|
||||
"view_novinky",
|
||||
"seminar",
|
||||
"novinky"
|
||||
],
|
||||
[
|
||||
"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_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"
|
||||
],
|
||||
[
|
||||
"delete_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"
|
||||
]
|
||||
]
|
||||
},
|
||||
"model": "auth.group",
|
||||
"pk": 1
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"name": "resitel",
|
||||
"permissions": [
|
||||
[
|
||||
"resitel",
|
||||
"auth",
|
||||
"user"
|
||||
]
|
||||
]
|
||||
},
|
||||
"model": "auth.group",
|
||||
"pk": 2
|
||||
}
|
||||
]
|
|
@ -1,3 +0,0 @@
|
|||
Tahle slozka obsahuje vsechny detaily a popisy, jak nasadit "druhou verzi" M&M webu.
|
||||
|
||||
TODO: chybi tu popis na zprovozneni flatpages, na loaddata &c.
|
Binary file not shown.
|
@ -1,652 +0,0 @@
|
|||
[
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"codename": "add_deadline",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "deadline"
|
||||
},
|
||||
{
|
||||
"codename": "change_deadline",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "deadline"
|
||||
},
|
||||
{
|
||||
"codename": "view_deadline",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "deadline"
|
||||
}
|
||||
]
|
|
@ -1,513 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
|
||||
OLD_DB = "mam_old"
|
||||
NEW_DB = "mamweb"
|
||||
|
||||
oldconn = psycopg2.connect(f"dbname={OLD_DB}")
|
||||
newconn = psycopg2.connect(f"dbname={NEW_DB}")
|
||||
|
||||
oldcur = oldconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
newcur = newconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
|
||||
|
||||
# Uses global variables oldcur, newcur!
|
||||
def execute_simple(old_query, new_query=None):
|
||||
if new_query is None:
|
||||
new_query = old_query
|
||||
|
||||
oldcur.execute(old_query)
|
||||
newcur.execute(new_query)
|
||||
|
||||
if oldcur.rowcount != newcur.rowcount:
|
||||
raise ValueError(f"Queries '{old_query}' and '{new_query}' returned different number of rows ({oldcur.rowcount} and {newcur.rowcount})")
|
||||
|
||||
return(oldcur.fetchall(), newcur.fetchall())
|
||||
|
||||
def check_same(old_row, new_row, old_fields, new_fields=None):
|
||||
if type(old_fields) != list:
|
||||
old_fields = [old_fields]
|
||||
|
||||
if new_fields is None:
|
||||
new_fields = old_fields
|
||||
|
||||
fields = zip(old_fields, new_fields)
|
||||
|
||||
for old_field, new_field in fields:
|
||||
if old_row[old_field] == new_row[new_field]:
|
||||
continue
|
||||
raise ValueError(f"Fields '{old_field}'({old_row[old_field]}) and '{new_field}'({new_row[new_field]}) differs for rows \n'{old_row}' and \n'{new_row}'")
|
||||
return True
|
||||
|
||||
def get_user_id_for_org_id(org_id):
|
||||
query = """SELECT auth_user.id FROM auth_user
|
||||
INNER JOIN seminar_osoby ON seminar_osoby.user_id = auth_user.id
|
||||
INNER JOIN seminar_organizator ON seminar_organizator.osoba_id = seminar_osoby.id
|
||||
WHERE seminar_organizator.id = %s """
|
||||
|
||||
newcur.execute(query,(org_id,))
|
||||
return newcur.fetchone()['id']
|
||||
|
||||
|
||||
|
||||
|
||||
def check_skola():
|
||||
old_query = "SELECT * FROM seminar_skoly ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','aesop_id','izo','nazev','kratky_nazev','ulice','mesto','psc','stat','je_zs','je_ss','poznamka'])
|
||||
|
||||
def check_resitel():
|
||||
old_query = 'SELECT * FROM seminar_resitele ORDER BY id'
|
||||
new_query = '''SELECT seminar_resitele.id, skola_id, rok_maturity, zasilat, seminar_resitele.poznamka,
|
||||
o.jmeno AS jmeno, o.prijmeni AS prijmeni, o.user_id AS user_id, o.pohlavi_muz AS pohlavi_muz, o.email AS email, o.telefon AS telefon, o.datum_narozeni AS datum_narozeni,
|
||||
o.datum_souhlasu_udaje AS datum_souhlasu_udaje, o.datum_souhlasu_zasilani AS datum_souhlasu_zasilani, o.datum_registrace AS datum_prihlaseni, o.ulice AS ulice, o.mesto AS mesto, o.psc AS psc, o.stat AS stat
|
||||
FROM seminar_resitele JOIN seminar_osoby AS o ON seminar_resitele.osoba_id = o.id ORDER BY seminar_resitele.id'''
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
fields_osoba = [
|
||||
'jmeno',
|
||||
'prijmeni',
|
||||
'user_id',
|
||||
'pohlavi_muz',
|
||||
#'email', #vyreseno separatne
|
||||
'telefon',
|
||||
'datum_narozeni',
|
||||
'datum_souhlasu_udaje',
|
||||
'datum_souhlasu_zasilani',
|
||||
'datum_prihlaseni',
|
||||
'ulice',
|
||||
'mesto',
|
||||
'psc',
|
||||
'stat',
|
||||
]
|
||||
fields_keep = [
|
||||
'id',
|
||||
'skola_id',
|
||||
'rok_maturity',
|
||||
'zasilat',
|
||||
'poznamka',
|
||||
]
|
||||
fields = fields_keep+fields_osoba
|
||||
for o,n in res:
|
||||
check_same(o,n,fields)
|
||||
if o['email'] != n['email'] and o['email'] != '':
|
||||
print(f"WARNING: Emails differ: old: {o['email']}, new: {n['email']}")
|
||||
|
||||
def check_reseni():
|
||||
# Migrace 0058 zamerne meni (zmensuje) pocet reseni, aby kazdy clanek mel
|
||||
# jen jedno reseni (s vice resiteli, coz postaru neslo)
|
||||
# Kvuli tomu je potreba kontrolovat dve veci:
|
||||
# 1) Ze kazdy resitel dostal za kazdy problem spravne bodu
|
||||
# 2) Ze detaily reseni zustaly zachovany
|
||||
|
||||
# Cast 1)
|
||||
old_query = 'SELECT * FROM seminar_reseni ORDER BY problem_id, resitel_id, body, timestamp'
|
||||
new_query = '''SELECT seminar_reseni.id, forma, seminar_reseni.poznamka, cas_doruceni, hodnoceni.problem_id AS problem_id, hodnoceni.body AS body, hodnoceni.cislo_body_id AS cislo_body_id, res.id AS resitel_id
|
||||
FROM seminar_reseni
|
||||
JOIN seminar_hodnoceni AS hodnoceni ON seminar_reseni.id = hodnoceni.reseni_id
|
||||
JOIN seminar_reseni_resitele AS rr ON seminar_reseni.id = rr.reseni_id
|
||||
JOIN seminar_resitele AS res ON res.id = rr.resitele_id
|
||||
ORDER BY problem_id, resitel_id, body, cas_doruceni'''
|
||||
|
||||
# Po spojeni nekterych problemu se lisi casy doruceni a poznamky, proto je nebudeme kontrolovat (jde v podstate o triviality, tak je to snad jedno)
|
||||
same_fields = ['forma', 'problem_id', 'body', 'cislo_body_id', 'resitel_id']
|
||||
renamed_fields = [
|
||||
#('timestamp', 'cas_doruceni'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,old_fields, new_fields)
|
||||
|
||||
# Cast 2)
|
||||
# Query se lisi tim, ze uz nejoinujeme resitele.
|
||||
old_query = 'SELECT * FROM seminar_reseni ORDER BY id'
|
||||
new_query = '''SELECT seminar_reseni.id, forma, poznamka, cas_doruceni AS timestamp, h.problem_id AS problem_id, h.body AS body, h.cislo_body_id AS cislo_body_id
|
||||
FROM seminar_reseni
|
||||
JOIN seminar_hodnoceni AS h ON h.reseni_id = seminar_reseni.id
|
||||
ORDER BY id'''
|
||||
|
||||
# execute_simple kontroluje stejnost poctu radku, to nechceme.
|
||||
oldcur.execute(old_query)
|
||||
newcur.execute(new_query)
|
||||
old_res, new_res = oldcur.fetchall(), newcur.fetchall()
|
||||
# Zkontrolujeme, ze pro kazde nove reseni ma stare reseni spravna data.
|
||||
new_ids = [n['id'] for n in new_res]
|
||||
spravna_old = list(filter(lambda o: o['id'] in new_ids, old_res))
|
||||
res = zip(spravna_old,new_res)
|
||||
for o,n in res:
|
||||
# Tady by se poznamky i timestampy mely zachovat
|
||||
# Z nejakeho duvodu se ale poznamky lisi ve whitespace, tak je zkontrolujeme separatne
|
||||
check_same(o,n,['id', 'forma', 'timestamp', 'problem_id', 'body', 'cislo_body_id'])
|
||||
old_pozn = o['poznamka'].strip()
|
||||
new_pozn = n['poznamka'].strip()
|
||||
if old_pozn != new_pozn:
|
||||
raise ValueError('Poznamky se lisi pro radky {dict(o)} a {dict(n)}')
|
||||
|
||||
|
||||
|
||||
def check_organizator():
|
||||
old_query = 'SELECT * FROM seminar_organizator ORDER BY id'
|
||||
new_query = '''SELECT seminar_organizator.id AS id, studuje, strucny_popis_organizatora, users.id AS uid, osoba.prezdivka AS o_prezdivka, osoba.foto AS o_foto, organizuje_od, organizuje_do
|
||||
FROM seminar_organizator
|
||||
JOIN seminar_osoby AS osoba ON osoba_id = osoba.id
|
||||
JOIN auth_user AS users ON osoba.user_id = users.id
|
||||
ORDER BY seminar_organizator.id'''
|
||||
|
||||
same_fields = ['studuje', 'strucny_popis_organizatora']
|
||||
renamed_fields = [
|
||||
('user_id', 'uid'),
|
||||
#('prezdivka', 'o_prezdivka'),
|
||||
('foto', 'o_foto'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
res = zip(old_res, new_res)
|
||||
for o,n in res:
|
||||
check_same(o,n,old_fields, new_fields)
|
||||
# organizuje od, do:
|
||||
# Migrace prirazuje aktualni casovou zonu, takze chceme tady rucne vynutit CET.
|
||||
from datetime import timedelta, timezone
|
||||
cet = timezone(timedelta(hours=1))
|
||||
if o['organizuje_od_roku'] is None and n['organizuje_od'] is None:
|
||||
pass
|
||||
elif o['organizuje_od_roku'] != n['organizuje_od'].astimezone(cet).year:
|
||||
raise ValueError(f'Not matching organizuje_od for org id={o["id"]}: old {o["organizuje_od_roku"]}, new {n["organizuje_od"]}')
|
||||
if o['organizuje_do_roku'] is None and n['organizuje_do'] is None:
|
||||
pass
|
||||
elif o['organizuje_do_roku'] != n['organizuje_do'].astimezone(cet).year:
|
||||
raise ValueError(f'Not matching organizuje_do for org id={o["id"]}: old {o["organizuje_do_roku"]}, new {n["organizuje_do"]}')
|
||||
if o['prezdivka'] == n['o_prezdivka']:
|
||||
continue
|
||||
if o['prezdivka'] is None and n['o_prezdivka'] == '':
|
||||
continue
|
||||
raise ValueError(f'Not matching prezdivka for org id={o["id"]}: old {o["prezdivka"]}, new {n["o_prezdivka"]}')
|
||||
|
||||
|
||||
def check_rocnik():
|
||||
old_query = "SELECT * FROM seminar_rocniky ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','prvni_rok', 'rocnik', 'exportovat'])
|
||||
|
||||
def check_cislo():
|
||||
old_query = "SELECT * FROM seminar_cisla ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, ['id','rocnik_id','cislo', 'datum_vydani','datum_deadline','verejne','poznamka','pdf'],
|
||||
['id','rocnik_id','poradi','datum_vydani','datum_deadline','verejne','poznamka','pdf'])
|
||||
|
||||
def check_priloha_reseni():
|
||||
old_query = "SELECT * FROM seminar_priloha_reseni"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, ['id','reseni_id', 'timestamp', 'soubor', 'poznamka'],
|
||||
['id','reseni_id', 'vytvoreno', 'soubor', 'poznamka'])
|
||||
|
||||
def check_soustredeni():
|
||||
old_query = "SELECT * FROM seminar_soustredeni ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','rocnik_id','datum_zacatku','datum_konce','verejne','misto','text','typ','exportovat'])
|
||||
#Kontrola ucasnici, organizatori v samostatnych funkcich
|
||||
|
||||
def check_soustredeni_ucastnici():
|
||||
old_query = "SELECT * FROM seminar_soustredeni_ucastnici ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','resitel_id','soustredeni_id','poznamka'])
|
||||
|
||||
def check_soustredeni_organizatori():
|
||||
old_query = "SELECT * FROM seminar_soustredeni_organizatori ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','organizator_id','soustredeni_id','poznamka'])
|
||||
|
||||
def check_nastaveni():
|
||||
old_query = "SELECT * FROM seminar_nastaveni ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','aktualni_cislo_id'])
|
||||
|
||||
def check_novinky():
|
||||
old_query = "SELECT * FROM seminar_novinky ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','datum','text','obrazek','zverejneno'])
|
||||
if get_user_id_for_org_id(n['autor_id']) != o['autor_id']:
|
||||
raise ValueError("Nesedi autori u novinek")
|
||||
|
||||
def check_pohadka():
|
||||
old_query = "SELECT * FROM seminar_pohadky ORDER BY id"
|
||||
new_query = """SELECT sp.id AS id, sp.autor_id AS autor_id, sp.vytvoreno AS vytvoreno, snp.treenode_ptr_id AS treenode_ptr_id, st.na_web AS text,
|
||||
zn_pred.uloha_id AS uloha_pred, zn_po.uloha_id AS uloha_po
|
||||
FROM seminar_pohadky AS sp
|
||||
-- Text pohádky
|
||||
INNER JOIN seminar_nodes_pohadka AS snp ON sp.id = snp.pohadka_id
|
||||
INNER JOIN seminar_nodes_treenode AS snt ON snt.id = snp.treenode_ptr_id
|
||||
INNER JOIN seminar_nodes_obsah AS sno ON sno.treenode_ptr_id = snt.first_child_id
|
||||
INNER JOIN seminar_texty AS st ON sno.text_id = st.id
|
||||
-- Predchozí úloha
|
||||
LEFT OUTER JOIN seminar_nodes_treenode AS ztn_pred ON ztn_pred.succ_id = snt.id
|
||||
LEFT OUTER JOIN seminar_nodes_uloha_zadani AS zn_pred ON zn_pred.treenode_ptr_id = ztn_pred.id
|
||||
-- Následující úloha
|
||||
LEFT OUTER JOIN seminar_nodes_uloha_zadani AS zn_po ON zn_po.treenode_ptr_id = snt.succ_id
|
||||
|
||||
ORDER BY sp.id"""
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','timestamp','text'],['id','vytvoreno','text'])
|
||||
if o['autor_id'] is not None:
|
||||
if get_user_id_for_org_id(n['autor_id']) != o['autor_id']:
|
||||
raise ValueError("Nesedi autori u pohadky")
|
||||
# Správné úlohy
|
||||
# NOTE: o['pred'] rika, zda je pohadka pred ulohou, nikoliv zda je relevantni uloha pred pohadkou!
|
||||
spravny_klic = 'uloha_po' if o['pred'] else 'uloha_pred'
|
||||
if o['uloha_id'] != n[spravny_klic]:
|
||||
raise ValueError(f"Pohádka přidružená ke špatné úloze! old: {o['uloha_id']}, new: {n[spravny_klic]}, pozice: {spravny_klic}")
|
||||
|
||||
|
||||
# Problémy jsou rozdělené podle typů:
|
||||
def check_problem_common():
|
||||
old_query = "SELECT id, nazev, stav, kod, autor_id, text_org, timestamp, typ FROM seminar_problemy ORDER BY id"
|
||||
new_query = """SELECT sp.id AS id, sp.nazev AS nazev, sp.stav AS stav, sp.kod AS kod, au.id AS autor_id, sp.poznamka AS poznamka, sp.vytvoreno AS vytvoreno
|
||||
FROM seminar_problemy AS sp
|
||||
LEFT OUTER JOIN seminar_organizator AS so ON sp.autor_id = so.id
|
||||
LEFT OUTER JOIN seminar_osoby AS sos ON so.osoba_id = sos.id
|
||||
LEFT OUTER JOIN auth_user AS au ON sos.user_id = au.id
|
||||
ORDER BY sp.id"""
|
||||
|
||||
same_fields = ['id', 'nazev', 'stav', 'autor_id', 'kod']
|
||||
renamed_fields = [
|
||||
('text_org', 'poznamka'),
|
||||
('timestamp', 'vytvoreno'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
|
||||
# Opravovatelé
|
||||
# Po staru byli opravovatele organizatori, takze je potreba je dohledat.
|
||||
old_query = """SELECT seminar_problemy.id, org.id AS opravovatel_id FROM seminar_problemy
|
||||
JOIN seminar_organizator AS org ON seminar_problemy.opravovatel_id = org.user_id;"""
|
||||
new_query = "SELECT problem_id, organizator_id FROM seminar_problemy_opravovatele"
|
||||
|
||||
# Simple cursors
|
||||
#oldcur = oldconn.cursor()
|
||||
oldcur.execute(old_query)
|
||||
old_results = oldcur.fetchall()
|
||||
#newcur = newconn.cursor()
|
||||
newcur.execute(new_query)
|
||||
new_results = newcur.fetchall()
|
||||
|
||||
for oldr in old_results:
|
||||
if oldr not in new_results:
|
||||
raise ValueError(f'Opravovatel pair {oldr} not found in new db.')
|
||||
|
||||
# Zaměření se vyřeší okometricky (#1186)
|
||||
|
||||
|
||||
def check_uloha():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'uloha' ORDER BY id"
|
||||
new_query = """SELECT cislo_zadani_id, cislo_reseni_id, problem_ptr_id, max_body, COALESCE(uzt.na_web, '') AS text_zadani, COALESCE(uvt.na_web, '') AS text_reseni, cislo_deadline_id
|
||||
FROM seminar_ulohy
|
||||
-- Problém:
|
||||
JOIN seminar_problemy AS problem ON problem_ptr_id = problem.id
|
||||
-- Text zadání:
|
||||
-- ZadaniNode a VzorakNode maji existovat vzdy, ale obsah nemusi (pokud ho nemaji)
|
||||
INNER JOIN seminar_nodes_uloha_zadani AS uzn ON problem.id = uzn.uloha_id
|
||||
INNER JOIN seminar_nodes_treenode AS uztn ON uztn.id = uzn.treenode_ptr_id
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS uzo ON uzo.treenode_ptr_id = uztn.first_child_id
|
||||
LEFT OUTER JOIN seminar_texty AS uzt ON uzo.text_id = uzt.id
|
||||
-- Text vzoráku:
|
||||
INNER JOIN seminar_nodes_uloha_vzorak AS uvn ON problem.id = uvn.uloha_id
|
||||
INNER JOIN seminar_nodes_treenode AS uvtn ON uvtn.id = uvn.treenode_ptr_id
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS uvo ON uvo.treenode_ptr_id = uvtn.first_child_id
|
||||
LEFT OUTER JOIN seminar_texty AS uvt ON uvo.text_id = uvt.id
|
||||
|
||||
ORDER BY problem_ptr_id"""
|
||||
|
||||
same_fields = ['cislo_zadani_id', 'cislo_reseni_id', 'text_zadani', 'text_reseni']
|
||||
renamed_fields = [
|
||||
('id', 'problem_ptr_id'),
|
||||
('body', 'max_body'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
# Datum deadline vypadá prázdně, tak to budeme předpokládat.
|
||||
if n['cislo_deadline_id'] is not None:
|
||||
raise ValueError("Úloha má deadline.")
|
||||
|
||||
def check_tema():
|
||||
old_query = """SELECT text_zadani, text_reseni, typ, c.rocnik_id AS rocnik_id
|
||||
FROM seminar_problemy
|
||||
LEFT OUTER JOIN seminar_cisla AS c ON c.id = cislo_zadani_id
|
||||
WHERE typ IN ('tema', 'serial')
|
||||
ORDER BY seminar_problemy.id"""
|
||||
new_query = """SELECT tema_typ, COALESCE(zad_text.na_web, '') AS text_zadani, COALESCE(res_text.na_web, '') AS text_reseni, rn.rocnik_id AS rocnik_id
|
||||
FROM seminar_temata
|
||||
-- Problém:
|
||||
JOIN seminar_problemy AS problem ON problem_ptr_id = problem.id
|
||||
-- Text:
|
||||
-- TvCNode má dva potomky, oba TextNode. První drží původní text zadání, druhý řešení.
|
||||
INNER JOIN seminar_nodes_temavcisle AS tvcn ON tvcn.tema_id = id
|
||||
INNER JOIN seminar_nodes_treenode AS ttn ON tvcn.treenode_ptr_id = ttn.id
|
||||
LEFT OUTER JOIN seminar_nodes_treenode AS zad_tn ON ttn.first_child_id = zad_tn.id -- jen 33 z nich ma zadani
|
||||
LEFT OUTER JOIN seminar_nodes_treenode AS res_tn ON zad_tn.succ_id = res_tn.id -- jen 4 z nich ma reseni
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS zad_on ON zad_on.treenode_ptr_id = zad_tn.id
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS res_on ON res_on.treenode_ptr_id = res_tn.id
|
||||
LEFT OUTER JOIN seminar_texty AS zad_text ON zad_on.text_id = zad_text.id
|
||||
LEFT OUTER JOIN seminar_texty AS res_text ON res_on.text_id = res_text.id -- vsechny 4
|
||||
-- Ročník tématu:
|
||||
-- Podle rootu TvCN
|
||||
LEFT OUTER JOIN seminar_nodes_rocnik AS rn ON ttn.root_id = rn.treenode_ptr_id
|
||||
|
||||
ORDER BY problem_ptr_id"""
|
||||
same_fields = ['text_zadani', 'text_reseni', 'rocnik_id']
|
||||
renamed_fields = [
|
||||
('typ', 'tema_typ'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
|
||||
def check_konfera():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'konfera'"
|
||||
new_query = "SELECT * FROM seminar_konfera"
|
||||
|
||||
oldcur.execute(old_query)
|
||||
newcur.execute(new_query)
|
||||
|
||||
if oldcur.rowcount != 0 or newcur.rowcount != 0:
|
||||
raise ValueError('There exists a Konfera!')
|
||||
|
||||
def check_org_clanek():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'org-clanek'"
|
||||
|
||||
oldcur.execute(old_query)
|
||||
|
||||
if oldcur.rowcount != 0:
|
||||
raise ValueError('There exists a Org-clanek!')
|
||||
|
||||
def check_res_clanek():
|
||||
# Dva(!) články mají text (zadání), který se má zachovat.
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'res-clanek' ORDER BY id"
|
||||
new_query = """SELECT cislo_id, text.na_web AS text_zadani
|
||||
FROM seminar_clanky
|
||||
JOIN seminar_problemy AS problem ON problem_ptr_id = problem.id
|
||||
INNER JOIN seminar_hodnoceni AS hodn ON problem.id = hodn.problem_id
|
||||
INNER JOIN seminar_reseni AS rese ON rese.id = hodn.reseni_id
|
||||
INNER JOIN seminar_nodes_otistene_reseni AS rn ON rese.text_cely_id = rn.treenode_ptr_id -- Tenhle radek neni potreba, ale ujistuje se mj. o spravnem typu TreeNode.
|
||||
INNER JOIN seminar_nodes_treenode AS tn ON rn.treenode_ptr_id = tn.id
|
||||
-- Nektere clanky vubec nemely text, tak jim migr 0058 nevyrobila dalsi treenody
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS son ON son.treenode_ptr_id = tn.first_child_id
|
||||
LEFT OUTER JOIN seminar_texty AS text ON text.id = son.text_id
|
||||
|
||||
ORDER BY problem_ptr_id"""
|
||||
same_fields = ['text_zadani']
|
||||
renamed_fields = [
|
||||
('cislo_zadani_id', 'cislo_id'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
# text_zadani po novu mohl byt None
|
||||
if n['text_zadani'] is None:
|
||||
n['text_zadani'] = ''
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
assert(o['text_reseni'] == '')
|
||||
|
||||
def check_untyped_problem():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ NOT IN ('uloha', 'tema', 'serial', 'konfera', 'org-clanek', 'res-clanek')"
|
||||
|
||||
oldcur.execute(old_query)
|
||||
|
||||
if oldcur.rowcount != 0:
|
||||
raise ValueError('There exists a Problem without type!')
|
||||
|
||||
|
||||
|
||||
check_skola()
|
||||
check_resitel()
|
||||
check_reseni()
|
||||
check_organizator()
|
||||
check_rocnik()
|
||||
check_cislo()
|
||||
check_priloha_reseni()
|
||||
check_soustredeni()
|
||||
check_soustredeni_ucastnici()
|
||||
check_soustredeni_organizatori()
|
||||
check_nastaveni()
|
||||
check_novinky()
|
||||
check_pohadka()
|
||||
|
||||
check_problem_common()
|
||||
check_uloha()
|
||||
check_tema()
|
||||
check_konfera()
|
||||
check_org_clanek()
|
||||
check_res_clanek()
|
||||
check_untyped_problem()
|
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -u
|
||||
|
||||
deactivate || true
|
||||
|
||||
cd /akce/mam/www/mamweb-test/
|
||||
make sync_test
|
||||
systemctl --user stop mamweb-test.service
|
||||
rm -rvf env
|
||||
make install_venv
|
||||
. env/bin/activate
|
||||
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
|
||||
|
||||
echo 'Et voilá!'
|
||||
echo 'Nezapomeň opravit práva pro sitetree!'
|
|
@ -1,14 +0,0 @@
|
|||
Milí řešitelé M&M,
|
||||
|
||||
web M&Mka dostal nový kabátek a zároveň se v něm objevilo odevzdávátko. Navíc,
|
||||
pokud se zaregistrujete pod e-mailem, na který jsme vám poslali tuhle zprávu,
|
||||
uvidíte rovnou i body za svá dosud odevzdaná řešení.
|
||||
|
||||
Web budeme i nadále vylepšovat, a zajímá nás i jak to vidíte vy. Pokud byste
|
||||
na webu našli nějaký nedostatek, nebo nám prostě jen chtěli napsat, nebojte se
|
||||
k tomu použít adresu mam@matfyz.cz.
|
||||
|
||||
Těšíme se na vaše řešení! (Připomínáme termín pro účast na soustředění 21. září.)
|
||||
|
||||
Za časopis M&M,
|
||||
Pavel Turinský
|
|
@ -1,3 +0,0 @@
|
|||
Jsou špatně práva k sitetree (protože se používají primární klíče, které jsou jiné :-()
|
||||
Špatné položky se dají najít pomocí následujícího příkazu:
|
||||
grep -E 'access_permissions": \[$' data/sitetree.json -A17 | grep -E 'acc|tit' -A2
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import django
|
||||
|
||||
#### Inicializace Djanga
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__))+'/..')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mamweb.settings')
|
||||
django.setup()
|
||||
|
||||
## Pozor, nejde pouzit ORM, protoze kod je na jine verzi nez databaze a nejde namigrovat.
|
||||
from django.db import connection
|
||||
|
||||
|
||||
def smaz_zle_clanky():
|
||||
# Tyhle clanky vubec nejsou clanky, bude potreba je udelat cele jinak a spravne.
|
||||
#m.Problem.objects.filter(id__in=[1981, 1970, 2222]).delete()
|
||||
## with connection.cursor() as cursor:
|
||||
## # Nejdriv musime smazat reseni:
|
||||
## cursor.execute('DELETE FROM seminar_reseni WHERE problem_id IN (1981, 1970, 2222);')
|
||||
## # Nakonec i ty clanky samotne
|
||||
## cursor.execute('DELETE FROM seminar_problemy WHERE id IN (1981, 1970, 2222);')
|
||||
|
||||
# Update: stejně je v DB bordel, tak z nich prostě jen udělám témata a všechno zhruba přežije…
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("UPDATE seminar_problemy SET typ = 'tema' WHERE id IN (1981, 1970, 2222);")
|
||||
|
||||
def smaz_divne_uzivatele():
|
||||
# U techto uzivatelu neexistuje Organizator s nimi spojeny
|
||||
# Takze pak delaji akorat neporadek
|
||||
with connection.cursor() as cursor:
|
||||
# Jeste je potreba zrusit vazby
|
||||
cursor.execute('UPDATE django_comments SET user_id = NULL WHERE user_id = 34;')
|
||||
cursor.execute('UPDATE seminar_problemy SET autor_id = NULL WHERE autor_id = 34;')
|
||||
cursor.execute('DELETE FROM django_admin_log WHERE user_id = 34;')
|
||||
cursor.execute('DELETE FROM auth_user_groups WHERE user_id = 34;')
|
||||
cursor.execute('DELETE FROM auth_user WHERE id IN (34, 40, 30, 50, 54, 58, 43);')
|
||||
|
||||
smaz_zle_clanky()
|
||||
smaz_divne_uzivatele()
|
|
@ -31,9 +31,10 @@ 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.
|
||||
nebo (v případě práv)::
|
||||
|
||||
./manage.py dumpdata auth.group --natural-foreign > data/prava_skupin_new.json
|
||||
./fix_json.py data/prava_skupin_new.json data/prava_skupin.json
|
||||
|
||||
docs
|
||||
----
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
TODO přepsat do rst 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
|
25
galerie/TODO
25
galerie/TODO
|
@ -1,25 +0,0 @@
|
|||
========
|
||||
| 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?
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
#coding: utf-8
|
||||
|
||||
from galerie.models import Obrazek, Galerie
|
||||
from django.contrib import admin
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
# -*- 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)
|
|
@ -1,7 +1,6 @@
|
|||
#coding: utf-8
|
||||
|
||||
from django import forms
|
||||
from seminar.models import Soustredeni
|
||||
from soustredeni.models import Soustredeni
|
||||
|
||||
class KomentarForm(forms.Form):
|
||||
komentar = forms.CharField(label = "Komentář:", max_length = 300, required=False)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-04-30 21:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.21 on 2019-06-10 21:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
29
galerie/migrations/0011_auto_20230809_2130.py
Normal file
29
galerie/migrations/0011_auto_20230809_2130.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.2.28 on 2023-08-09 19:30
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('galerie', '0010_auto_20200819_0947'),
|
||||
('soustredeni', '0001_initial'),
|
||||
]
|
||||
|
||||
run_before = [
|
||||
('seminar', '0121_smazani_soustredeni'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(
|
||||
state_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'),
|
||||
),
|
||||
],
|
||||
database_operations=[],
|
||||
),
|
||||
]
|
|
@ -1,4 +1,3 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.db import models
|
||||
#from django.db.models import Q
|
||||
|
@ -8,7 +7,7 @@ from imagekit.processors import ResizeToFit, Transpose
|
|||
|
||||
import os
|
||||
|
||||
from seminar.models import Soustredeni
|
||||
from soustredeni.models import Soustredeni
|
||||
|
||||
VZDY=0
|
||||
ORG=1
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
"""
|
||||
Soubor sloužící jako „router“, tj. zde se definují url adresy a na co ukazují:
|
||||
|
||||
- ``<int:pk>/`` :func:`~galerie.views.nahled`
|
||||
- ``<int:pk>/<int:fotka>/`` :func:`~galerie.views.detail`
|
||||
- ``<int:galerie>/new/`` :func:`~galerie.views.new_galerie`
|
||||
- ``<int:galerie>/plus/<int:subgalerie>/`` :func:`~galerie.views.plus_galerie`
|
||||
- ``<int:galerie>/minus/<int:subgalerie>/`` :func:`~galerie.views.minus_galerie`
|
||||
"""
|
||||
from django.urls import path
|
||||
from seminar.utils import org_required
|
||||
from personalni.utils import org_required
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# coding: utf-8
|
||||
|
||||
import random
|
||||
|
||||
|
@ -8,7 +7,7 @@ from django.template import RequestContext
|
|||
from datetime import datetime
|
||||
|
||||
from galerie.models import Obrazek, Galerie
|
||||
from seminar.models import Soustredeni
|
||||
from soustredeni.models import Soustredeni
|
||||
from galerie.forms import KomentarForm, NewGalerieForm
|
||||
|
||||
def zobrazit(galerie, request):
|
||||
|
|
|
@ -1,14 +1,3 @@
|
|||
"""
|
||||
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:
|
||||
|
||||
- :class:`~header_fotky.models.FotkaHeader`
|
||||
- :class:`~header_fotky.models.FotkaUrlVazba`
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import ModelAdmin
|
||||
import header_fotky.models as m
|
||||
|
@ -22,4 +11,4 @@ class FotkaPozadiAdmin(ModelAdmin):
|
|||
readonly_fields = ['cas']
|
||||
|
||||
admin.site.register(m.FotkaHeader, FotkaPozadiAdmin)
|
||||
admin.site.register(m.FotkaUrlVazba)
|
||||
admin.site.register(m.FotkaUrlVazba)
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
"""
|
||||
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
|
||||
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
"""
|
||||
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`
|
||||
"""
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
- 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?
|
||||
|
||||
|
|
@ -1,13 +1,4 @@
|
|||
"""
|
||||
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:
|
||||
|
||||
- :class:`korektury.models.KorekturovanePDF`
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from reversion.admin import VersionAdmin
|
||||
from korektury.models import KorekturovanePDF
|
||||
|
@ -15,7 +6,7 @@ from korektury.models import KorekturovanePDF
|
|||
from django.core.mail import EmailMessage
|
||||
from django.urls import reverse
|
||||
|
||||
# Register your models here.
|
||||
|
||||
class KorekturovanePDFAdmin(VersionAdmin):
|
||||
"""
|
||||
nastaví čas vložení (:attr:`~koretkury.models.KorekturovanePDF.cas`) a počet
|
||||
|
@ -50,8 +41,8 @@ class KorekturovanePDFAdmin(VersionAdmin):
|
|||
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}))
|
||||
odesilatel = 'korekturovatko-nove-pdf@mam.mff.cuni.cz'
|
||||
prijemce = 'org@mam.mff.cuni.cz'
|
||||
odesilatel = settings.KOREKTURY_NOVE_PDF_EMAIL
|
||||
prijemce = settings.KONFERA_ORGOVE_EMAIL
|
||||
predmet = f'Nové korektury: {obj.nazev}'
|
||||
text = f'''\
|
||||
V korekturovátku se objevil nový soubor: {obj.nazev}
|
||||
|
|
|
@ -1,13 +1,3 @@
|
|||
"""
|
||||
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
|
||||
"""
|
||||
from django import forms
|
||||
|
||||
class OpravaForm(forms.Form):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-04-30 21:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.21 on 2019-06-10 21:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
39
korektury/migrations/0021_auto_20230731_1954.py
Normal file
39
korektury/migrations/0021_auto_20230731_1954.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Generated by Django 2.2.28 on 2023-07-31 17:54
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('korektury', '0020_lepsi_popis_nazvu_PDF_v_adminu'),
|
||||
('personalni', '0002_initial'),
|
||||
]
|
||||
|
||||
run_before = [
|
||||
('seminar', '0116_smazani_personalniho'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(
|
||||
state_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'),
|
||||
),
|
||||
],
|
||||
database_operations=[],
|
||||
),
|
||||
]
|
|
@ -1,27 +1,12 @@
|
|||
"""
|
||||
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`
|
||||
"""
|
||||
import os
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_text
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.text import get_valid_filename
|
||||
|
||||
from seminar.models import Organizator
|
||||
from personalni.models import Organizator
|
||||
|
||||
import subprocess
|
||||
from reversion import revisions as reversion
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -1,6 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící ke generování testdat.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
from shutil import copyfile, rmtree
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
"""
|
||||
Soubor sloužící jako „router“, tj. zde se definují url adresy a na co ukazují:
|
||||
|
||||
- ``korektury/`` (korektury_list) :class:`~korektury.views.KorekturySeskupeneListView`
|
||||
- ``korektury/neseskupene/`` (korektury_neseskupene_list) :class:`~korektury.views.KorekturyAktualniListView`
|
||||
- ``korektury/zastarale/`` (korektury_stare_list) :class:`~korektury.views.KorekturyZastaraleListView`
|
||||
- ``korektury/<int:pdf>/`` (korektury) :class:`~korektury.views.KorekturyView`
|
||||
"""
|
||||
from django.urls import path
|
||||
from seminar.utils import org_required
|
||||
from personalni.utils import org_required
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('korektury/', org_required(views.KorekturySeskupeneListView.as_view()), name='korektury_list'),
|
||||
path('korektury/neseskupene/', org_required(views.KorekturyAktualniListView.as_view()), name='korektury_neseskupene_list'),
|
||||
path('korektury/zastarale/', org_required(views.KorekturyZastaraleListView.as_view()), name='korektury_stare_list'),
|
||||
path('korektury/<int:pdf>/', org_required(views.KorekturyView.as_view()), name='korektury'),
|
||||
path('', org_required(views.KorekturySeskupeneListView.as_view()), name='korektury_list'),
|
||||
path('neseskupene/', org_required(views.KorekturyAktualniListView.as_view()), name='korektury_neseskupene_list'),
|
||||
path('zastarale/', org_required(views.KorekturyZastaraleListView.as_view()), name='korektury_stare_list'),
|
||||
path('<int:pdf>/', org_required(views.KorekturyView.as_view()), name='korektury'),
|
||||
]
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící k deklaraci jednotlivých „views“ (nejčastěji funkce beroucí request
|
||||
a vracející :func:`django.shortcuts.render` respektive nějakou response, nebo
|
||||
třídy většinou rozšiřující nějakou třídu z :mod:`django.views.generic`)
|
||||
"""
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.views import generic
|
||||
from django.utils.translation import ugettext as _
|
||||
|
@ -166,7 +161,7 @@ class KorekturyView(generic.TemplateView):
|
|||
from django.urls import reverse
|
||||
odkaz = self.request.build_absolute_uri(reverse('korektury', kwargs={'pdf': oprava.pdf.pk}))
|
||||
odkaz = f"{odkaz}#op{oprava.id}-pointer"
|
||||
from_email = 'korekturovatko@mam.mff.cuni.cz'
|
||||
from_email = settings.KOREKTURY_EMAIL
|
||||
subject = 'Nová korektura od {} v {}'.format(autor, oprava.pdf.nazev)
|
||||
texty = [(oprava.autor.osoba.plne_jmeno(),oprava.text)]
|
||||
for kom in Komentar.objects.filter(oprava=oprava):
|
||||
|
@ -200,13 +195,6 @@ class KorekturyView(generic.TemplateView):
|
|||
if email:
|
||||
emails.discard(email)
|
||||
|
||||
if not settings.POSLI_MAILOVOU_NOTIFIKACI:
|
||||
print("Poslal bych upozornění na tyto adresy: ", " ".join(emails))
|
||||
print("---- Upozornění:")
|
||||
print(text)
|
||||
print("---- Konec upozornění")
|
||||
return
|
||||
|
||||
EmailMessage(
|
||||
subject=subject,
|
||||
body=text,
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
from datetime import datetime, date
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
|
||||
|
||||
|
||||
class LoggedInHintCookieMiddleware(object):
|
||||
"""Middleware to securely help with 'logged-in' detection for dual HTTP/HTTPS sites.
|
||||
|
||||
On insecure requests: Checks for a (non-secure) cookie settings.LOGGED_IN_HINT_COOKIE_NAME
|
||||
and if present, redirects to HTTPS (same adress).
|
||||
Note this usually breaks non-GET (POST) requests.
|
||||
|
||||
On secure requests: Updates cookie settings.LOGGED_IN_HINT_COOKIE_NAME to reflect
|
||||
whether an user is logged in in the current session (cookie set to 'True' or cleared).
|
||||
The cookie is set to expire at the same time as the sessionid cookie.
|
||||
|
||||
By default, LOGGED_IN_HINT_COOKIE_NAME = 'logged_in_hint'.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
if hasattr(settings, 'LOGGED_IN_HINT_COOKIE_NAME'):
|
||||
self.cookie_name = settings.LOGGED_IN_HINT_COOKIE_NAME
|
||||
else: self.cookie_name = 'logged_in_hint'
|
||||
self.cookie_value = 'True'
|
||||
|
||||
def cookie_correct(self, request):
|
||||
return self.cookie_name in request.COOKIES and request.COOKIES[self.cookie_name] == self.cookie_value
|
||||
|
||||
def process_request(self, request):
|
||||
if not request.is_secure():
|
||||
if self.cookie_correct(request):
|
||||
# redirect insecure (assuming http) requests with hint cookie to https
|
||||
url = request.build_absolute_uri()
|
||||
assert url[:5] == 'http:'
|
||||
return HttpResponseRedirect('https:' + url[5:])
|
||||
return None
|
||||
|
||||
def process_response(self, request, response):
|
||||
if request.is_secure():
|
||||
# assuming full session info (as the conn. is secure)
|
||||
try:
|
||||
user = request.user
|
||||
except AttributeError: # no user - ajax or other special request
|
||||
return response
|
||||
if user.is_authenticated():
|
||||
if not self.cookie_correct(request):
|
||||
expiry = None if request.session.get_expire_at_browser_close() else request.session.get_expiry_date()
|
||||
response.set_cookie(self.cookie_name, value=self.cookie_value, expires=expiry, secure=False)
|
||||
else:
|
||||
if self.cookie_name in request.COOKIES:
|
||||
response.delete_cookie(self.cookie_name)
|
||||
return response
|
||||
|
||||
|
||||
class vzhled:
|
||||
|
||||
def process_request(self, request):
|
||||
return None
|
||||
|
||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
||||
#print "====== process_request ======"
|
||||
#print view_func
|
||||
#print view_args
|
||||
#print view_kwargs
|
||||
#print "============================="
|
||||
return None
|
||||
|
||||
def process_template_response(self, request, response):
|
||||
hodin = datetime.now().hour
|
||||
if (hodin <= 6) or (hodin >= 14): # TODO 20
|
||||
response.context_data['noc'] = True
|
||||
else:
|
||||
response.context_data['noc'] = False
|
||||
return response
|
||||
|
||||
def process_response(self, request, response):
|
||||
#hodin = datetime.now().hour
|
||||
#if (hodin <= 6) or (hodin >= 14): # TODO 20
|
||||
#response.context_data['noc'] = True
|
||||
#else:
|
||||
#response.context_data['noc'] = False
|
||||
return response
|
||||
|
||||
|
||||
##def process_exception(request, exception):
|
||||
#pass
|
|
@ -1,80 +1,64 @@
|
|||
"""
|
||||
Django settings for mamweb project.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.7/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.7/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
import traceback
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# Application definition
|
||||
|
||||
SITE_ID = 1
|
||||
ROOT_URLCONF = 'mamweb.urls'
|
||||
WSGI_APPLICATION = 'mamweb.wsgi.application'
|
||||
|
||||
APPEND_SLASH = True
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.7/topics/i18n/
|
||||
|
||||
# Lokalizace
|
||||
LANGUAGE_CODE = 'cs'
|
||||
TIME_ZONE = 'Europe/Prague'
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.7/howto/static-files/
|
||||
USE_L10N = True # S přechodem k djangu>=4 lze smazat (localized formatting)
|
||||
USE_TZ = True # S přechodem k djangu>=5 lze smazat (timezone aware datetimes)
|
||||
|
||||
# Statické soubory (CSS, JavaScript, obrázky) a další média
|
||||
STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
||||
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
)
|
||||
|
||||
# Where redirect for login required services
|
||||
# URL pro přihlášení (default je account/login)
|
||||
LOGIN_URL = 'login'
|
||||
LOGIN_REDIRECT_URL = 'profil'
|
||||
|
||||
|
||||
# Odhlášení po zavření prohlížeče
|
||||
# (pozor nefunguje na firefox se znovuotevíráním oken po startu firefoxu)
|
||||
# default je False a SESSION_COOKIE_AGE = 3600*24*14 = 2 týdny
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
DOBA_ODHLASENI_PRI_ZASKRTNUTI_NEODHLASOVAT = 365 * 24 * 3600 # rok
|
||||
|
||||
# Modules configuration
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
# Create file 'django.secret' in every install (it is not kept in git)
|
||||
try:
|
||||
with open(os.path.join(os.path.dirname(__file__), '..', 'django.secret')) as f:
|
||||
SECRET_KEY = f.readline().strip()
|
||||
except:
|
||||
SECRET_KEY = '12345zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzdqwkdlkj'
|
||||
|
||||
|
||||
# Přidávání dalších součástí (do) djangovské mašinérie
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
|
||||
MIDDLEWARE = (
|
||||
# 'reversion.middleware.RevisionMiddleware',
|
||||
# 'reversion.middleware.RevisionMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
# FIXME: rozbilo se při přechodu na Django 2.0, nevím, jestli
|
||||
# se to dá zahodit bez náhrady
|
||||
# 'mamweb.middleware.LoggedInHintCookieMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
|
||||
)
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
|
@ -83,7 +67,7 @@ TEMPLATES = [
|
|||
'OPTIONS': {
|
||||
'context_processors': (
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.template.context_processors.request',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'sekizai.context_processors.sekizai',
|
||||
'header_fotky.context_processors.vzhled',
|
||||
|
@ -93,12 +77,7 @@ TEMPLATES = [
|
|||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
INSTALLED_APPS = (
|
||||
|
||||
|
||||
# Basic
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
|
@ -144,69 +123,71 @@ INSTALLED_APPS = (
|
|||
'various',
|
||||
'various.autentizace',
|
||||
'api',
|
||||
'aesop',
|
||||
'odevzdavatko',
|
||||
'vysledkovky',
|
||||
'personalni',
|
||||
'soustredeni',
|
||||
'tvorba',
|
||||
'treenode',
|
||||
'vyroci',
|
||||
|
||||
# Admin upravy:
|
||||
|
||||
# 'material',
|
||||
# 'material.admin',
|
||||
# 'admin_tools',
|
||||
# 'admin_tools.theming',
|
||||
# 'admin_tools.menu',
|
||||
# 'admin_tools.dashboard',
|
||||
# 'material',
|
||||
# 'material.admin',
|
||||
# 'admin_tools',
|
||||
# 'admin_tools.theming',
|
||||
# 'admin_tools.menu',
|
||||
# 'admin_tools.dashboard',
|
||||
'django.contrib.admin',
|
||||
|
||||
# Nechat na konci (INSTALLED_APPS je uspořádané):
|
||||
'django_cleanup.apps.CleanupConfig', # Uklízí media/
|
||||
)
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'SHOW_COLLAPSED': True,
|
||||
}
|
||||
|
||||
SUMMERNOTE_CONFIG = {
|
||||
'iframe': False,
|
||||
'airMode': False,
|
||||
'attachment_require_authentication': True,
|
||||
'width': '80%',
|
||||
# 'height': '30em',
|
||||
'toolbar': [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
['insert', ['link', 'picture', 'hr']],
|
||||
['view', ['fullscreen', 'codeview']],
|
||||
['help', ['help']],
|
||||
]
|
||||
}
|
||||
# MaM-specifické složky
|
||||
SEMINAR_RESENI_DIR = os.path.join('reseni')
|
||||
SEMINAR_KONFERY_DIR = os.path.join('konfery')
|
||||
KOREKTURY_PDF_DIR = os.path.join('korektury', 'pdf')
|
||||
KOREKTURY_IMG_DIR = os.path.join('korektury', 'img')
|
||||
CISLO_IMG_DIR = os.path.join('cislo', 'img')
|
||||
|
||||
|
||||
# MaM-specifické konstanty
|
||||
ROCNIK_ZRUSENI_TEMAT = 25
|
||||
ROCNIK_INFLACE_BODU = 25
|
||||
ROCNIK_INFLACE_TITULU = 26
|
||||
#
|
||||
NOVE_CISLO_EMAIL = 'zadani@mam.mff.cuni.cz'
|
||||
NOVE_RESENI_EMAIL = 'submitovatko@mam.mff.cuni.cz'
|
||||
KOREKTURY_NOVE_PDF_EMAIL = 'korekturovatko-nove-pdf@mam.mff.cuni.cz'
|
||||
KOREKTURY_EMAIL = 'korekturovatko@mam.mff.cuni.cz'
|
||||
REGISTRACE_EMAIL = 'registrace@mam.mff.cuni.cz'
|
||||
PASSWD_RESET_EMAIL = 'login@mam.mff.cuni.cz'
|
||||
#
|
||||
KONFERA_ORGOVE_EMAIL = 'org@mam.mff.cuni.cz'
|
||||
|
||||
|
||||
# CKEditor = WYSIWYG html editor
|
||||
CKEDITOR_UPLOAD_PATH = "uploads/"
|
||||
CKEDITOR_IMAGE_BACKEND = 'pillow'
|
||||
#CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'
|
||||
# CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'
|
||||
CKEDITOR_CONFIGS = {
|
||||
'default': {
|
||||
'entities': False,
|
||||
'toolbar': [
|
||||
['Source', 'ShowBlocks', '-', 'Maximize'],
|
||||
['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'],
|
||||
['NumberedList','BulletedList','-','Blockquote','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
|
||||
['NumberedList', 'BulletedList', '-', 'Blockquote', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
|
||||
['Link', 'Unlink', 'Anchor', '-', 'Image', 'Table', 'HorizontalRule'],
|
||||
['Format'],
|
||||
|
||||
],
|
||||
# 'toolbar': 'full',
|
||||
# 'toolbar': 'full',
|
||||
'height': '40em',
|
||||
'width': '100%',
|
||||
'toolbarStartupExpanded': False,
|
||||
'allowedContent' : True,
|
||||
'allowedContent': True,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -226,31 +207,21 @@ WEBPACK_LOADER = {
|
|||
|
||||
|
||||
# Dajngo REST Framework
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||
'PAGE_SIZE': 100
|
||||
}
|
||||
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
# Create file 'django.secret' in every install (it is not kept in git)
|
||||
|
||||
try:
|
||||
with open(os.path.join(os.path.dirname(__file__), '..', 'django.secret')) as f:
|
||||
SECRET_KEY = f.readline().strip()
|
||||
except:
|
||||
SECRET_KEY = '12345zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzdqwkdlkj'
|
||||
|
||||
# Logging
|
||||
|
||||
# Logování
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s (logger %(name)s): %(message)s'
|
||||
'format':
|
||||
'%(levelname)s %(asctime)s %(module)s (logger %(name)s): %(message)s'
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -281,18 +252,18 @@ LOGGING = {
|
|||
'filters': ['Http404AsInfo'],
|
||||
},
|
||||
|
||||
'seminar.prihlaska.form':{
|
||||
'handlers': ['console','registration_logfile'],
|
||||
'level': 'INFO'
|
||||
},
|
||||
'seminar.prihlaska.problem':{
|
||||
'handlers': ['console','mail_registration','registration_error_log'],
|
||||
'level': 'INFO'
|
||||
},
|
||||
'seminar.prihlaska.form': {
|
||||
'handlers': ['console', 'registration_logfile'],
|
||||
'level': 'INFO'
|
||||
},
|
||||
'seminar.prihlaska.problem': {
|
||||
'handlers': ['console', 'mail_registration', 'registration_error_log'],
|
||||
'level': 'INFO'
|
||||
},
|
||||
|
||||
# Catch-all logger
|
||||
'': {
|
||||
'handlers': ['console'], # Add 'mail_admins' in prod and test
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
'filters': ['StripSensitiveFormData'],
|
||||
},
|
||||
|
@ -302,7 +273,7 @@ LOGGING = {
|
|||
'handlers': {
|
||||
|
||||
'console': {
|
||||
'level': 'WARNING', ## Set to 'DEBUG' in local
|
||||
'level': 'WARNING',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
|
@ -318,40 +289,24 @@ LOGGING = {
|
|||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'registration_logfile':{
|
||||
'registration_logfile': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
# filename declared in specific configuration files
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'registration_error_log':{
|
||||
'class': 'logging.FileHandler',
|
||||
# filename declared in specific configuration files
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'registration_error_log': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
# filename declared in specific configuration files
|
||||
'class': 'logging.FileHandler',
|
||||
# filename declared in specific configuration files
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
# Permissions for uploads
|
||||
FILE_UPLOAD_PERMISSIONS = 0o0644
|
||||
|
||||
# MaM specific
|
||||
|
||||
SEMINAR_RESENI_DIR = os.path.join('reseni')
|
||||
SEMINAR_KONFERY_DIR = os.path.join('konfery')
|
||||
KOREKTURY_PDF_DIR = os.path.join('korektury', 'pdf')
|
||||
KOREKTURY_IMG_DIR = os.path.join('korektury', 'img')
|
||||
CISLO_IMG_DIR = os.path.join('cislo', 'img')
|
||||
|
||||
|
||||
# E-MAIL NOTIFICATIONS
|
||||
POSLI_MAILOVOU_NOTIFIKACI = False
|
||||
|
||||
|
||||
|
||||
# Logování chyb
|
||||
# Logování neexistujících proměnných v templatech
|
||||
class InvalidTemplateVariable(str):
|
||||
def __mod__(self, variable):
|
||||
import logging
|
||||
|
@ -363,5 +318,38 @@ class InvalidTemplateVariable(str):
|
|||
return ''
|
||||
TEMPLATES[0]['OPTIONS']['string_if_invalid'] = InvalidTemplateVariable('%s')
|
||||
|
||||
|
||||
# Django 3.2 vyžaduje explicitní nastavení autoklíče, zatím nechápu proč
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
|
||||
# TODO odstranit? (Je to default.)
|
||||
# Whether to append trailing slashes to URLs.
|
||||
APPEND_SLASH = True
|
||||
# TODO odstranit? (Je to default.)
|
||||
# Permissions for uploads
|
||||
FILE_UPLOAD_PERMISSIONS = 0o0644
|
||||
# TODO odstranit? (Je to default.)
|
||||
# Automatická lokalizace
|
||||
USE_I18N = True
|
||||
|
||||
|
||||
# TODO odstranit? (Nevím o tom, že bychom ho někde používali.)
|
||||
# Summernote = WYSIWYG editor (pro admin?)
|
||||
SUMMERNOTE_CONFIG = {
|
||||
'iframe': False,
|
||||
'airMode': False,
|
||||
'attachment_require_authentication': True,
|
||||
'width': '80%',
|
||||
# 'height': '30em',
|
||||
'toolbar': [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
['insert', ['link', 'picture', 'hr']],
|
||||
['view', ['fullscreen', 'codeview']],
|
||||
['help', ['help']],
|
||||
]
|
||||
}
|
||||
|
|
22
mamweb/settings_common_debug.py
Normal file
22
mamweb/settings_common_debug.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from .settings_common import *
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = True
|
||||
|
||||
MIDDLEWARE += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
)
|
||||
|
||||
INSTALLED_APPS += (
|
||||
'debug_toolbar', # Takovéto užitečné (pro debug) na html stránce vpravo
|
||||
'django_extensions', # Kolekce zajímavých ./manage.py commandů
|
||||
)
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'SHOW_COLLAPSED': True,
|
||||
}
|
||||
|
||||
# Nechceme nikomu omylem poslat e-mail
|
||||
# Když někdo spustí omylem tohle nastavení, prostě to při poslání mailu spadne
|
||||
# V settings_local a settings_test přepíšeme...
|
||||
EMAIL_BACKEND = None
|
|
@ -1,5 +1,3 @@
|
|||
import os.path
|
||||
|
||||
#
|
||||
# Lokalni / vyvojove nastaveni settings.py
|
||||
#
|
||||
|
@ -7,35 +5,20 @@ import os.path
|
|||
# DJANGO_SETTINGS_MODULE=mamweb.settings_local ./manage.py ...
|
||||
#
|
||||
|
||||
# Import common settings
|
||||
from .settings_common import *
|
||||
|
||||
MIDDLEWARE += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
)
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
|
||||
INSTALLED_APPS += (
|
||||
'debug_toolbar',
|
||||
'django_extensions',
|
||||
)
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
INTERNAL_IPS = ['127.0.0.1']
|
||||
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = True
|
||||
|
||||
import os.path
|
||||
from ipaddress import ip_network
|
||||
|
||||
from .settings_common_debug import *
|
||||
LOCAL_TEST_PROD = "local"
|
||||
|
||||
|
||||
INTERNAL_IPS = ['127.0.0.1']
|
||||
ALLOWED_HOSTS = [str(ip) for ip in ip_network('192.168.0.0/16')]
|
||||
ALLOWED_HOSTS.append('127.0.0.1')
|
||||
ALLOWED_HOSTS.append('localhost')
|
||||
ALLOWED_HOSTS += ['127.0.0.1', 'localhost']
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
|
||||
|
||||
# Databáze
|
||||
# SQLite
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
|
@ -45,15 +28,21 @@ DATABASES = {
|
|||
},
|
||||
},
|
||||
}
|
||||
#DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
# 'NAME': 'mam_local',
|
||||
# 'USER': 'mam',
|
||||
# },
|
||||
#}
|
||||
# # PostgreSQL
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
# 'NAME': 'mam_local',
|
||||
# 'USER': 'mam',
|
||||
# },
|
||||
# }
|
||||
|
||||
# LOGGING
|
||||
|
||||
# E-maily posílat chceme, ale do terminálu :-)
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
|
||||
# Logování
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
|
@ -78,12 +67,6 @@ LOGGING = {
|
|||
},
|
||||
},
|
||||
'loggers': {
|
||||
# Vypisovani databazovych dotazu do konzole
|
||||
#'django.db.backends': {
|
||||
# 'level': 'DEBUG',
|
||||
# 'handlers': ['console'],
|
||||
# 'propagate': False,
|
||||
#},
|
||||
'werkzeug': {
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
|
@ -96,11 +79,3 @@ LOGGING = {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
# set to 'DEBUG' for EXTRA verbose output
|
||||
# LOGGING['handlers']['console']['level'] = 'INFO'
|
||||
|
||||
# E-maily posílat chceme, ale do terminálu :-)
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
SEND_EMAIL_NOTIFICATIONS = True
|
||||
LOCAL_TEST_PROD = "local"
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os.path
|
||||
|
||||
#
|
||||
# Produkcni nastaveni settings.py
|
||||
#
|
||||
|
@ -11,28 +7,27 @@ import os.path
|
|||
|
||||
# Import common settings
|
||||
from .settings_common import *
|
||||
LOCAL_TEST_PROD = "prod"
|
||||
|
||||
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
|
||||
INSTALLED_APPS += (
|
||||
'django_extensions',
|
||||
)
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
assert not SECRET_KEY.startswith('12345')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
TEMPLATE_DEBUG = False
|
||||
# SECURITY: only send sensitive cookies via HTTPS
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
ALLOWED_HOSTS = ['mam.mff.cuni.cz', 'www.mam.mff.cuni.cz', 'atrey.karlin.mff.cuni.cz',
|
||||
'mamweb.bezva.org','gimli.ms.mff.cuni.cz']
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
|
||||
ALLOWED_HOSTS = ['mam.mff.cuni.cz', 'www.mam.mff.cuni.cz', 'gimli.ms.mff.cuni.cz']
|
||||
|
||||
# Přidání aplikací
|
||||
INSTALLED_APPS += (
|
||||
'django_extensions', # Kolekce zajímavých ./manage.py commandů
|
||||
)
|
||||
|
||||
|
||||
# Databáze
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
|
@ -44,28 +39,14 @@ DATABASES = {
|
|||
},
|
||||
}
|
||||
|
||||
import os
|
||||
|
||||
# Nastavení e-mailů (odkud a kam mají chodit)
|
||||
SERVER_EMAIL = 'mamweb-prod-errors@mam.mff.cuni.cz'
|
||||
ADMINS = [('M&M ERRORs', 'mam-errors@mam.mff.cuni.cz')]
|
||||
|
||||
|
||||
# SECURITY: only send sensitive cookies via HTTPS
|
||||
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
|
||||
|
||||
# LOGGING
|
||||
|
||||
# Logování
|
||||
LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['loggers']['django.security.csrf']['level'] = 'ERROR'
|
||||
LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/prod/registration.log'
|
||||
LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/prod/registration_errors.log'
|
||||
|
||||
|
||||
# E-MAIL NOTIFICATIONS
|
||||
POSLI_MAILOVOU_NOTIFIKACI = True
|
||||
LOCAL_TEST_PROD = "prod"
|
||||
|
|
|
@ -1,42 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os.path
|
||||
|
||||
#
|
||||
# Testovaci nastaveni settings.py (testovani na atreyi)
|
||||
# Testovaci nastaveni settings.py (testovani na mam-test.ks.marfyz.cz)
|
||||
#
|
||||
# Pro vyber tohoto nastaveni muzete pouzit tez:
|
||||
# DJANGO_SETTINGS_MODULE=mamweb.settings_test ./manage.py ...
|
||||
#
|
||||
|
||||
# Import common settings
|
||||
from .settings_common import * # zatim nutne, casem snad vyresime # noqa
|
||||
from .settings_common_debug import *
|
||||
LOCAL_TEST_PROD = "test"
|
||||
|
||||
MIDDLEWARE += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
)
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
|
||||
INSTALLED_APPS += (
|
||||
'debug_toolbar',
|
||||
'django_extensions',
|
||||
)
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = ')^u=i65*zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzv9l+zo)n'
|
||||
# SECURITY: only send sensitive cookies via HTTPS
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = True
|
||||
ALLOWED_HOSTS = ['mam-test.kam.mff.cuni.cz', 'gimli.ms.mff.cuni.cz', 'mam-test.ks.matfyz.cz']
|
||||
|
||||
ALLOWED_HOSTS = ['*.mam.mff.cuni.cz', 'atrey.karlin.mff.cuni.cz', 'mam.mff.cuni.cz', 'mam-test.kam.mff.cuni.cz', 'gimli.ms.mff.cuni.cz', 'mam-test.ks.matfyz.cz']
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
|
||||
|
||||
# Databáze
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
|
@ -48,32 +30,27 @@ DATABASES = {
|
|||
},
|
||||
}
|
||||
|
||||
import os
|
||||
|
||||
# Nastavení e-mailů (odkud a kam mají chodit)
|
||||
SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz'
|
||||
ADMINS = [
|
||||
('M&M ERRORs', 'mam-errors@mam.mff.cuni.cz'),
|
||||
]
|
||||
|
||||
|
||||
# SECURITY: only send sensitive cookies via HTTPS
|
||||
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
# LOGGING
|
||||
|
||||
LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/test/registration.log'
|
||||
LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/test/registration_errors.log'
|
||||
|
||||
FILE_UPLOAD_PERMISSIONS = 0o440
|
||||
|
||||
# Testování e-mailů
|
||||
POSLI_MAILOVOU_NOTIFIKACI = True
|
||||
EMAIL_BACKEND = 'various.mail_prefixer.PrefixingMailBackend'
|
||||
# TODO Pouze na otestování testu… Zvolit konferu!
|
||||
# XXX: Je to pole, protože implementační detail backendu.
|
||||
TESTOVACI_EMAILOVA_KONFERENCE = ['betatest@mam.mff.cuni.cz']
|
||||
LOCAL_TEST_PROD = "test"
|
||||
|
||||
|
||||
# Netuším proč (Jidáš)...
|
||||
FILE_UPLOAD_PERMISSIONS = 0o440
|
||||
|
||||
|
||||
# Logování
|
||||
LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/test/registration.log'
|
||||
LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/test/registration_errors.log'
|
||||
|
|
|
@ -1,20 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící jako základní „router“, tj. zde se includují veškeré ostatní urls:
|
||||
|
||||
- ``admin/`` :mod:`django.contrib.admin.site.urls`
|
||||
- ``ckeditor/`` :mod:`ckeditor_uploader.urls`
|
||||
- :mod:`seminar.urls`
|
||||
- :mod:`odevzdavatko.urls`
|
||||
- :mod:`korektury.urls`
|
||||
- :mod:`prednasky.urls`
|
||||
- :mod:`soustredeni.urls`
|
||||
- :mod:`personalni.urls`
|
||||
- :mod:`various.autentizace.urls`
|
||||
- :mod:`api.urls`
|
||||
- :mod:`treenode.urls`
|
||||
- :mod:`aesop.urls`
|
||||
- ``comments_dj/`` :mod:`django_comments.urls`
|
||||
"""
|
||||
from django.urls import path, include
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
from django.contrib import admin
|
||||
|
@ -34,17 +17,20 @@ urlpatterns = [
|
|||
# Seminarova aplikace (ma vlastni podadresare)
|
||||
path('', include('seminar.urls')),
|
||||
|
||||
# Tvorba, Čísla, Ročníky, atd. (ma vlastni podadresare)
|
||||
path('', include('tvorba.urls')),
|
||||
|
||||
# Odevzdavatko (ma vlastni podadresare)
|
||||
path('', include('odevzdavatko.urls')),
|
||||
|
||||
# Korekturovaci aplikace (ma vlastni podadresare)
|
||||
path('', include('korektury.urls')),
|
||||
path('korektury/', include('korektury.urls')),
|
||||
|
||||
# Prednaskova aplikace (ma vlastni podadresare)
|
||||
path('', include('prednasky.urls')),
|
||||
path('prednasky/', include('prednasky.urls')),
|
||||
|
||||
# Soustredkova aplikace (ma vlastni podadresare)
|
||||
path('', include('soustredeni.urls')),
|
||||
path('soustredeni/', include('soustredeni.urls')),
|
||||
|
||||
# Personalni aplikace (ma vlastni podadresare)
|
||||
# (profil, osobní údaje, ..., ne autentizace, viz dále)
|
||||
|
@ -54,14 +40,11 @@ urlpatterns = [
|
|||
path('', include('various.autentizace.urls')),
|
||||
|
||||
# Api (ma vlastni podadresare) (autocomplete apod.)
|
||||
path('', include('api.urls')),
|
||||
path('api/', include('api.urls')),
|
||||
|
||||
# treenode (ma vlastni podadresare)
|
||||
path('', include('treenode.urls')),
|
||||
|
||||
# Aesop (ma vlastni podadresare)
|
||||
path('', include('aesop.urls')),
|
||||
|
||||
# Comments (interni i verejne)
|
||||
path('comments_dj/', include('django_comments.urls')),
|
||||
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
"""
|
||||
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`.
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django_reverse_admin import ReverseModelAdmin
|
||||
import seminar.models as m
|
||||
from .models import *
|
||||
|
||||
|
||||
class PrilohaReseniInline(admin.TabularInline):
|
||||
model = m.PrilohaReseni
|
||||
model = PrilohaReseni
|
||||
extra = 1
|
||||
|
||||
|
||||
class Reseni_ResiteleInline(admin.TabularInline):
|
||||
model = m.Reseni_Resitele
|
||||
model = Reseni_Resitele
|
||||
|
||||
|
||||
@admin.register(m.Reseni)
|
||||
@admin.register(Reseni)
|
||||
class ReseniAdmin(ReverseModelAdmin):
|
||||
base_model = m.Reseni
|
||||
base_model = Reseni
|
||||
inline_type = 'tabular'
|
||||
# inline_reverse = ['text_cely','resitele'] TODO vrátit zpět a zrychlit dotaz
|
||||
inline_reverse = ['resitele']
|
||||
|
@ -30,5 +24,5 @@ class ReseniAdmin(ReverseModelAdmin):
|
|||
# inlines = [PrilohaReseniInline,Reseni_ResiteleInline]
|
||||
|
||||
|
||||
admin.site.register(m.PrilohaReseni)
|
||||
admin.site.register(m.Hodnoceni)
|
||||
admin.site.register(PrilohaReseni)
|
||||
admin.site.register(Hodnoceni)
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ from django.forms import formset_factory
|
|||
from django.forms.models import inlineformset_factory
|
||||
from django.utils import timezone
|
||||
|
||||
from seminar.models import Resitel
|
||||
import seminar.models as m
|
||||
from personalni.models import Resitel
|
||||
from tvorba.models import Problem, Deadline
|
||||
from seminar.models.nastaveni import Nastaveni
|
||||
from .models import *
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -22,7 +24,7 @@ class DateInput(forms.DateInput):
|
|||
|
||||
class PosliReseniForm(forms.Form):
|
||||
problem = forms.ModelMultipleChoiceField(
|
||||
queryset=m.Problem.objects.all(),
|
||||
queryset=Problem.objects.all(),
|
||||
label="Problémy",
|
||||
widget=autocomplete.ModelSelect2Multiple(
|
||||
url='autocomplete_problem',
|
||||
|
@ -58,7 +60,7 @@ class PosliReseniForm(forms.Form):
|
|||
|
||||
#cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True)
|
||||
|
||||
forma = forms.ChoiceField(label="Forma řešení",choices = m.Reseni.FORMA_CHOICES)
|
||||
forma = forms.ChoiceField(label="Forma řešení",choices = Reseni.FORMA_CHOICES)
|
||||
#forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
|
||||
# default=FORMA_EMAIL)
|
||||
|
||||
|
@ -69,7 +71,7 @@ class PosliReseniForm(forms.Form):
|
|||
|
||||
class NahrajReseniForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = m.Reseni
|
||||
model = Reseni
|
||||
fields = ('problem', 'resitele')
|
||||
help_texts = {'problem':''} # Nezobrazovat help text ve formuláři
|
||||
|
||||
|
@ -109,11 +111,11 @@ class NahrajReseniForm(forms.ModelForm):
|
|||
def clean_problem(self):
|
||||
problem = self.cleaned_data.get('problem')
|
||||
for p in problem:
|
||||
if p.stav != m.Problem.STAV_ZADANY:
|
||||
if p.stav != Problem.STAV_ZADANY:
|
||||
raise forms.ValidationError("Problém " + str(p) + " již nelze řešit!")
|
||||
return problem
|
||||
|
||||
ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
|
||||
ReseniSPrilohamiFormSet = inlineformset_factory(Reseni,PrilohaReseni,
|
||||
form = NahrajReseniForm,
|
||||
fields = ('soubor','res_poznamka'),
|
||||
widgets = {'res_poznamka':forms.TextInput()},
|
||||
|
@ -125,7 +127,7 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
|
|||
|
||||
class JednoHodnoceniForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = m.Hodnoceni
|
||||
model = Hodnoceni
|
||||
fields = ('problem', 'body', 'deadline_body', 'feedback',)
|
||||
widgets = {
|
||||
'problem': autocomplete.ModelSelect2(
|
||||
|
@ -158,7 +160,7 @@ OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm,
|
|||
|
||||
class PoznamkaReseniForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = m.Reseni
|
||||
model = Reseni
|
||||
fields = ('poznamka',)
|
||||
|
||||
# FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat
|
||||
|
@ -198,7 +200,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
|
|||
|
||||
from django.db.utils import OperationalError
|
||||
try:
|
||||
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik
|
||||
aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik
|
||||
except OperationalError:
|
||||
# django.db.utils.OperationalError: no such table: seminar_nastaveni
|
||||
# Nemáme databázi, takže to selhalo. Pro jistotu vrátíme aspoň dvě možnosti, ať to nepadá dál
|
||||
|
@ -214,7 +216,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
|
|||
|
||||
result.append(("0001-01-01", f"Odjakživa"))
|
||||
|
||||
for deadline in m.Deadline.objects.filter(
|
||||
for deadline in Deadline.objects.filter(
|
||||
deadline__lte=timezone.now(),
|
||||
cislo__rocnik=aktualni_rocnik
|
||||
).order_by("deadline"):
|
||||
|
|
108
odevzdavatko/migrations/0001_initial.py
Normal file
108
odevzdavatko/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Generated by Django 2.2.28 on 2023-08-09 17:37
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import odevzdavatko.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('tvorba', '0001_initial'),
|
||||
('personalni', '0002_initial'),
|
||||
('seminar', '0117_separace_tvorby'),
|
||||
]
|
||||
|
||||
run_before = [
|
||||
('seminar', '0120_smazani_odevzdavatka'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(
|
||||
state_operations=[
|
||||
migrations.CreateModel(
|
||||
name='Hodnoceni',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('body', models.DecimalField(blank=True, decimal_places=1, max_digits=8, null=True, verbose_name='body')),
|
||||
('feedback', models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba')),
|
||||
('cislo_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.Cislo', verbose_name='číslo pro body')),
|
||||
('deadline_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.Deadline', verbose_name='deadline pro body')),
|
||||
('problem', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.Problem', verbose_name='problém')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Hodnocení',
|
||||
'verbose_name_plural': 'Hodnocení',
|
||||
'db_table': 'mam_hodnoceni',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Reseni',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('cas_doruceni', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='čas_doručení')),
|
||||
('forma', models.CharField(choices=[('papir', 'Papírové řešení'), ('email', 'Emailem'), ('upload', 'Upload přes web')], default='email', max_length=16, verbose_name='forma řešení')),
|
||||
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešení (plain text)', verbose_name='neveřejná poznámka')),
|
||||
('zverejneno', models.BooleanField(default=False, help_text='Udává, zda je řešení zveřejněno', verbose_name='řešení zveřejněno')),
|
||||
('problem', models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='tvorba.Problem', verbose_name='problém')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Řešení',
|
||||
'verbose_name_plural': 'Řešení',
|
||||
'db_table': 'mam_reseni',
|
||||
'ordering': ['-cas_doruceni'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Reseni_Resitele',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.Reseni', verbose_name='řešení')),
|
||||
('resitele', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.Resitel', verbose_name='řešitel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Řešení řešitelů',
|
||||
'verbose_name_plural': 'Řešení řešitelů',
|
||||
'db_table': 'mam_reseni_resitele',
|
||||
'ordering': ['reseni', 'resitele'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='reseni',
|
||||
name='resitele',
|
||||
field=models.ManyToManyField(help_text='Seznam autorů řešení', through='odevzdavatko.Reseni_Resitele', to='personalni.Resitel', verbose_name='autoři řešení'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='reseni',
|
||||
name='text_cely',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.ReseniNode', verbose_name='Plná verze textu řešení'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PrilohaReseni',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='vytvořeno')),
|
||||
('soubor', models.FileField(upload_to=odevzdavatko.models.generate_filename, verbose_name='soubor')),
|
||||
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')),
|
||||
('res_poznamka', models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele')),
|
||||
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='odevzdavatko.Reseni', verbose_name='řešení')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Příloha řešení',
|
||||
'verbose_name_plural': 'Přílohy řešení',
|
||||
'db_table': 'mam_priloha_reseni',
|
||||
'ordering': ['reseni', 'vytvoreno'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='hodnoceni',
|
||||
name='reseni',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.Reseni', verbose_name='řešení'),
|
||||
),
|
||||
],
|
||||
database_operations=[],
|
||||
),
|
||||
]
|
25
odevzdavatko/migrations/0002_presun_treenode.py
Normal file
25
odevzdavatko/migrations/0002_presun_treenode.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Generated by Django 2.2.28 on 2023-08-10 07:14
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('odevzdavatko', '0001_initial'),
|
||||
('treenode', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(
|
||||
state_operations=[
|
||||
migrations.AlterField(
|
||||
model_name='reseni',
|
||||
name='text_cely',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='treenode.ReseniNode', verbose_name='Plná verze textu řešení'),
|
||||
),
|
||||
],
|
||||
database_operations=[],
|
||||
),
|
||||
]
|
|
@ -9,19 +9,18 @@ from django.urls import reverse_lazy
|
|||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
from seminar.models import tvorba as am
|
||||
from seminar.models import personalni as pm
|
||||
from seminar.models import treenode as tm
|
||||
from seminar.models import base as bm
|
||||
from tvorba.models import Cislo, Deadline, Problem, Uloha, aux_generate_filename
|
||||
from personalni.models import Resitel
|
||||
from seminar.models.base import SeminarModelBase
|
||||
|
||||
from seminar.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
|
||||
__all__ = ["Reseni", "Hodnoceni", "PrilohaReseni", "Reseni_Resitele"]
|
||||
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Reseni(bm.SeminarModelBase):
|
||||
class Reseni(SeminarModelBase):
|
||||
|
||||
class Meta:
|
||||
db_table = 'seminar_reseni'
|
||||
db_table = 'mam_reseni'
|
||||
verbose_name = 'Řešení'
|
||||
verbose_name_plural = 'Řešení'
|
||||
#ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to.
|
||||
|
@ -31,10 +30,10 @@ class Reseni(bm.SeminarModelBase):
|
|||
id = models.AutoField(primary_key = True)
|
||||
|
||||
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby.
|
||||
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
|
||||
problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém',
|
||||
through='Hodnoceni')
|
||||
|
||||
resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení',
|
||||
resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
|
||||
help_text='Seznam autorů řešení', through='Reseni_Resitele')
|
||||
|
||||
|
||||
|
@ -51,7 +50,7 @@ class Reseni(bm.SeminarModelBase):
|
|||
forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
|
||||
default=FORMA_EMAIL)
|
||||
|
||||
text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení',
|
||||
text_cely = models.OneToOneField("treenode.ReseniNode", verbose_name='Plná verze textu řešení',
|
||||
blank=True, null=True, related_name="reseni_cely_set",
|
||||
on_delete=models.PROTECT)
|
||||
|
||||
|
@ -81,7 +80,7 @@ class Reseni(bm.SeminarModelBase):
|
|||
# NOTE: Potenciální DB HOG (bez select_related)
|
||||
|
||||
def deadline_reseni(self):
|
||||
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
|
||||
return Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
|
||||
|
||||
## Pravdepodobne uz nebude potreba:
|
||||
# def save(self, *args, **kwargs):
|
||||
|
@ -90,9 +89,9 @@ class Reseni(bm.SeminarModelBase):
|
|||
# self.cislo_body = self.problem.cislo_reseni
|
||||
# super(Reseni, self).save(*args, **kwargs)
|
||||
|
||||
class Hodnoceni(bm.SeminarModelBase):
|
||||
class Hodnoceni(SeminarModelBase):
|
||||
class Meta:
|
||||
db_table = 'seminar_hodnoceni'
|
||||
db_table = 'mam_hodnoceni'
|
||||
verbose_name = 'Hodnocení'
|
||||
verbose_name_plural = 'Hodnocení'
|
||||
|
||||
|
@ -103,16 +102,16 @@ class Hodnoceni(bm.SeminarModelBase):
|
|||
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
|
||||
blank=True, null=True)
|
||||
|
||||
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
|
||||
cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',
|
||||
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
|
||||
|
||||
# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body
|
||||
deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body',
|
||||
deadline_body = models.ForeignKey(Deadline, verbose_name='deadline pro body',
|
||||
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
|
||||
|
||||
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||
|
||||
problem = models.ForeignKey(am.Problem, verbose_name='problém',
|
||||
problem = models.ForeignKey(Problem, verbose_name='problém',
|
||||
related_name='hodnoceni', on_delete=models.PROTECT)
|
||||
|
||||
feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
|
||||
|
@ -138,6 +137,7 @@ class Hodnoceni(bm.SeminarModelBase):
|
|||
def body_neprepocitane(self):
|
||||
if self.body is None:
|
||||
return None
|
||||
from odevzdavatko.utils import inverze_vzorecku_na_prepocet
|
||||
return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count())
|
||||
|
||||
@body_neprepocitane.setter
|
||||
|
@ -145,12 +145,14 @@ class Hodnoceni(bm.SeminarModelBase):
|
|||
if value is None:
|
||||
self.body = None
|
||||
else:
|
||||
from odevzdavatko.utils import vzorecek_na_prepocet
|
||||
self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count())
|
||||
|
||||
@property
|
||||
def body_neprepocitane_celkem(self):
|
||||
if self.body_celkem is None:
|
||||
return None
|
||||
from odevzdavatko.utils import inverze_vzorecku_na_prepocet
|
||||
return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count())
|
||||
|
||||
@body_neprepocitane_celkem.setter
|
||||
|
@ -158,17 +160,19 @@ class Hodnoceni(bm.SeminarModelBase):
|
|||
if value is None:
|
||||
self.body = None
|
||||
else:
|
||||
from odevzdavatko.utils import vzorecek_na_prepocet
|
||||
self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count())
|
||||
|
||||
@property
|
||||
def body_max(self):
|
||||
if self.body_neprepocitane_max is None:
|
||||
return None
|
||||
from odevzdavatko.utils import vzorecek_na_prepocet
|
||||
return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count())
|
||||
|
||||
@property
|
||||
def body_neprepocitane_max(self):
|
||||
if not isinstance(self.problem.get_real_instance(), am.Uloha):
|
||||
if not isinstance(self.problem.get_real_instance(), Uloha):
|
||||
return None
|
||||
return self.problem.uloha.max_body
|
||||
|
||||
|
@ -178,15 +182,15 @@ class Hodnoceni(bm.SeminarModelBase):
|
|||
def generate_filename(self, filename):
|
||||
return os.path.join(
|
||||
settings.SEMINAR_RESENI_DIR,
|
||||
am.aux_generate_filename(self, filename)
|
||||
aux_generate_filename(self, filename)
|
||||
)
|
||||
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class PrilohaReseni(bm.SeminarModelBase):
|
||||
class PrilohaReseni(SeminarModelBase):
|
||||
|
||||
class Meta:
|
||||
db_table = 'seminar_priloha_reseni'
|
||||
db_table = 'mam_priloha_reseni'
|
||||
verbose_name = 'Příloha řešení'
|
||||
verbose_name_plural = 'Přílohy řešení'
|
||||
ordering = ['reseni', 'vytvoreno']
|
||||
|
@ -221,7 +225,7 @@ class PrilohaReseni(bm.SeminarModelBase):
|
|||
class Reseni_Resitele(models.Model):
|
||||
|
||||
class Meta:
|
||||
db_table = 'seminar_reseni_resitele'
|
||||
db_table = 'mam_reseni_resitele'
|
||||
verbose_name = 'Řešení řešitelů'
|
||||
verbose_name_plural = 'Řešení řešitelů'
|
||||
ordering = ['reseni', 'resitele']
|
||||
|
@ -229,7 +233,7 @@ class Reseni_Resitele(models.Model):
|
|||
# Interní ID
|
||||
id = models.AutoField(primary_key = True)
|
||||
|
||||
resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
||||
resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
||||
|
||||
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||
|
||||
|
@ -239,19 +243,3 @@ class Reseni_Resitele(models.Model):
|
|||
def __str__(self):
|
||||
return '{} od {}'.format(self.reseni, self.resitel)
|
||||
# NOTE: Poteciální DB HOG bez select_related
|
||||
|
||||
class ReseniNode(tm.TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_otistene_reseni'
|
||||
verbose_name = 'Otištěné řešení (Node)'
|
||||
verbose_name_plural = 'Otištěná řešení (Node)'
|
||||
reseni = models.ForeignKey(Reseni,
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name = 'reseni')
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "ReseniNode: "+str(self.reseni)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return str(self.reseni)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load utils %} {# Možná by mohlo být někde výš v hierarchii templatů... #}
|
||||
{% load datumy %} {# Možná by mohlo být někde výš v hierarchii templatů... #}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ from django import template
|
|||
register = template.Library()
|
||||
|
||||
from personalni.utils import normalizuj_jmeno
|
||||
import seminar.models as m # jen kvůli typové anotaci…
|
||||
from personalni.models import Osoba # jen kvůli typové anotaci…
|
||||
|
||||
@register.filter
|
||||
def jmeno_jako_prefix(o: m.Osoba):
|
||||
def jmeno_jako_prefix(o: Osoba):
|
||||
return normalizuj_jmeno(o).replace(' ', '_')
|
||||
|
|
49
odevzdavatko/testutils.py
Normal file
49
odevzdavatko/testutils.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
import datetime
|
||||
import random
|
||||
|
||||
from .models import *
|
||||
|
||||
|
||||
def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele):
|
||||
pocet_reseni = rnd.randint(pocet_resitelu//4, pocet_resitelu * 4)
|
||||
# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla
|
||||
for _ in range(pocet_reseni):
|
||||
# print("Generuji {}-té řešení".format(reseni))
|
||||
if rnd.randint(1, 10) == 1:
|
||||
# cca desetina řešení od více řešitelů
|
||||
res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5))
|
||||
else:
|
||||
res_vyber = rnd.sample(resitele_cisla, 1)
|
||||
if resitele[0] in res_vyber: # speciální řešitel, který nemá žádné body
|
||||
res_vyber.remove(resitele[0])
|
||||
|
||||
# Vytvoření řešení.
|
||||
if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None:
|
||||
# combine, abychom dostali plný čas a ne jen datum
|
||||
cas_doruceni = (
|
||||
uloha.cislo_zadani.deadline_v_cisle.first().deadline -
|
||||
datetime.timedelta(days=random.randint(0, 40)) -
|
||||
datetime.timedelta(minutes=random.randint(0, 60*24))
|
||||
)
|
||||
# astimezone, protože jinak vyhazuje warning o nenastavené TZ
|
||||
res = Reseni.objects.create(
|
||||
forma=rnd.choice(Reseni.FORMA_CHOICES)[0],
|
||||
cas_doruceni=cas_doruceni.astimezone(datetime.timezone.utc),
|
||||
)
|
||||
else:
|
||||
res = Reseni.objects.create(
|
||||
forma=rnd.choice(Reseni.FORMA_CHOICES)[0],
|
||||
)
|
||||
# Problém a řešitele přiřadíme později, ManyToManyField
|
||||
# se nedá vyplnit v create().
|
||||
res.resitele.set(res_vyber)
|
||||
res.save()
|
||||
|
||||
# Vytvoření hodnocení.
|
||||
hod = Hodnoceni.objects.create(
|
||||
body=rnd.randint(0, uloha.max_body),
|
||||
cislo_body=cisla[poradi_cisla - 1],
|
||||
reseni=res,
|
||||
problem=uloha
|
||||
)
|
||||
return
|
|
@ -1,21 +1,8 @@
|
|||
"""
|
||||
Soubor sloužící jako „router“, tj. zde se definují url adresy a na co ukazují:
|
||||
|
||||
- ``org/add_solution`` (seminar_vloz_reseni) :class:`~odevzdavatko.views.PosliReseniView`
|
||||
- ``resitel/nahraj_reseni`` (seminar_nahraj_reseni) :class:`~odevzdavatko.views.NahrajReseniView`
|
||||
- ``resitel/odevzdana_reseni/`` (seminar_resitel_odevzdana_reseni) :class:`~odevzdavatko.views.PrehledOdevzdanychReseni`
|
||||
- ``org/reseni/`` (odevzdavatko_tabulka) :class:`~odevzdavatko.views.TabulkaOdevzdanychReseniView`
|
||||
- ``org/reseni/rocnik/<int:rocnik>/`` (odevzdavatko_tabulka) :class:`~odevzdavatko.views.TabulkaOdevzdanychReseniView`
|
||||
- ``org/reseni/<int:problem>/<int:resitel>/`` (odevzdavatko_reseni_resitele_k_problemu) :class:`~odevzdavatko.views.ReseniProblemuView`
|
||||
- ``org/reseni/<int:pk>/`` (odevzdavatko_detail_reseni) :func:`~seminar.utils.viewMethodSwitch` + :class:`~odevzdavatko.views.DetailReseniView` + :func:`~odevzdavatko.views.hodnoceniReseniView`
|
||||
- ``org/reseni/all`` :class:`~odevzdavatko.views.SeznamReseniView`
|
||||
- ``org/reseni/akt`` :class:`~odevzdavatko.views.TabulkaOdevzdanychReseniView`
|
||||
- ``resitel/reseni/<int:pk>`` (odevzdavatko_resitel_reseni) :class:`~odevzdavatko.views.ResitelReseniView`
|
||||
"""
|
||||
from django.urls import path
|
||||
|
||||
from seminar.utils import org_required, resitel_required, viewMethodSwitch, \
|
||||
from personalni.utils import org_required, resitel_required, \
|
||||
resitel_or_org_required
|
||||
from various.utils import viewMethodSwitch
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
|
|
11
odevzdavatko/utils.py
Normal file
11
odevzdavatko/utils.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
import decimal
|
||||
|
||||
|
||||
def vzorecek_na_prepocet(body, resitelu):
|
||||
""" Vzoreček na přepočet plných bodů na parciálni, když má řešení více řešitelů. """
|
||||
return body * 3 / (resitelu + 2)
|
||||
|
||||
|
||||
def inverze_vzorecku_na_prepocet(body: decimal.Decimal, resitelu) -> decimal.Decimal:
|
||||
""" Vzoreček na přepočet parciálních bodů na plné, když má řešení více řešitelů. """
|
||||
return round(body * (resitelu + 2) / 3, 1)
|
|
@ -10,17 +10,21 @@ from django.shortcuts import redirect, get_object_or_404, render
|
|||
from django.urls import reverse
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
from itertools import groupby
|
||||
import logging
|
||||
|
||||
import seminar.models as m
|
||||
from .models import *
|
||||
from tvorba.models import Problem, Rocnik, Deadline
|
||||
from seminar.models.nastaveni import Nastaveni
|
||||
from personalni.models import Resitel, Organizator, Osoba
|
||||
from . import forms as f
|
||||
from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
|
||||
from seminar.utils import resi_v_rocniku
|
||||
from seminar.views import formularOKView
|
||||
from personalni.utils import resi_v_rocniku
|
||||
from various.views import formularOKView
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -47,20 +51,20 @@ class SouhrnReseni:
|
|||
|
||||
class TabulkaOdevzdanychReseniView(ListView):
|
||||
template_name = 'odevzdavatko/tabulka.html'
|
||||
model = m.Hodnoceni
|
||||
model = Hodnoceni
|
||||
|
||||
def inicializuj_osy_tabulky(self):
|
||||
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů"""
|
||||
# FIXME: jméno metody není vypovídající...
|
||||
# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat
|
||||
# TODO: Prefetches, Select related, ...
|
||||
self.resitele = m.Resitel.objects.all()
|
||||
self.problemy = m.Problem.objects.all()
|
||||
self.reseni = m.Reseni.objects.all()
|
||||
self.resitele = Resitel.objects.all()
|
||||
self.problemy = Problem.objects.all()
|
||||
self.reseni = Reseni.objects.all()
|
||||
|
||||
self.aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
|
||||
self.aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
|
||||
if 'rocnik' in self.kwargs:
|
||||
self.aktualni_rocnik = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik'])
|
||||
self.aktualni_rocnik = get_object_or_404(Rocnik, rocnik=self.kwargs['rocnik'])
|
||||
|
||||
form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik)
|
||||
if form.is_valid():
|
||||
|
@ -91,14 +95,14 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok)
|
||||
|
||||
if problemy == FiltrForm.PROBLEMY_MOJE:
|
||||
org = m.Organizator.objects.get(osoba__user=self.request.user)
|
||||
org = Organizator.objects.get(osoba__user=self.request.user)
|
||||
self.problemy = self.problemy.filter(
|
||||
Q(autor=org)|Q(garant=org)|Q(opravovatele=org),
|
||||
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
|
||||
Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY),
|
||||
)
|
||||
elif problemy == FiltrForm.PROBLEMY_LETOSNI:
|
||||
self.problemy = self.problemy.filter(
|
||||
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
|
||||
Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY),
|
||||
)
|
||||
#self.problemy = list(filter(lambda problem: problem.rocnik() == self.aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník....
|
||||
# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy.
|
||||
|
@ -164,7 +168,7 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
# Pro použití hacku na automatické {{form.media}} v template:
|
||||
ctx['form'] = ctx['filtr']
|
||||
# Pro maximum v přesměrovátku ročníků
|
||||
ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik
|
||||
ctx['aktualni_rocnik'] = Nastaveni.get_solo().aktualni_rocnik
|
||||
if 'rocnik' in self.kwargs:
|
||||
ctx['rocnik'] = self.kwargs['rocnik']
|
||||
else:
|
||||
|
@ -174,7 +178,7 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
|
||||
# Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji?
|
||||
class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View):
|
||||
model = m.Reseni
|
||||
model = Reseni
|
||||
template_name = 'odevzdavatko/seznam.html'
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -186,8 +190,8 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
|
|||
if problem_id is None:
|
||||
raise ValueError("Nemám problém! (To je problém!)")
|
||||
|
||||
resitel = m.Resitel.objects.get(id=resitel_id)
|
||||
problem = m.Problem.objects.get(id=problem_id)
|
||||
resitel = Resitel.objects.get(id=resitel_id)
|
||||
problem = Problem.objects.get(id=problem_id)
|
||||
qs = qs.filter(
|
||||
problem__in=[problem],
|
||||
resitele__in=[resitel],
|
||||
|
@ -217,13 +221,13 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
|
|||
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
|
||||
class DetailReseniView(DetailView):
|
||||
""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """
|
||||
model = m.Reseni
|
||||
model = Reseni
|
||||
template_name = 'odevzdavatko/detail.html'
|
||||
|
||||
def aktualni_hodnoceni(self):
|
||||
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
|
||||
self.reseni = get_object_or_404(Reseni, id=self.kwargs['pk'])
|
||||
result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet
|
||||
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
|
||||
for hodn in Hodnoceni.objects.filter(reseni=self.reseni):
|
||||
seznam_atributu = [
|
||||
"problem",
|
||||
"body",
|
||||
|
@ -280,7 +284,7 @@ class EditReseniView(DetailReseniView):
|
|||
|
||||
|
||||
def hodnoceniReseniView(request, pk, *args, **kwargs):
|
||||
reseni = get_object_or_404(m.Reseni, pk=pk)
|
||||
reseni = get_object_or_404(Reseni, pk=pk)
|
||||
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk})
|
||||
|
||||
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově
|
||||
|
@ -296,7 +300,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
|
|||
poznamka_form.save()
|
||||
|
||||
# Smažeme všechna dosavadní hodnocení tohoto řešení
|
||||
qs = m.Hodnoceni.objects.filter(reseni=reseni)
|
||||
qs = Hodnoceni.objects.filter(reseni=reseni)
|
||||
logger.info(f"Will delete {qs.count()} objects: {qs}")
|
||||
qs.delete()
|
||||
|
||||
|
@ -307,7 +311,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
|
|||
del(data_for_hodnoceni["body_celkem"])
|
||||
del(data_for_hodnoceni["body_neprepocitane"])
|
||||
del(data_for_hodnoceni["body_neprepocitane_celkem"])
|
||||
hodnoceni = m.Hodnoceni(
|
||||
hodnoceni = Hodnoceni(
|
||||
reseni=reseni,
|
||||
**form.cleaned_data,
|
||||
)
|
||||
|
@ -327,14 +331,14 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
|
|||
|
||||
|
||||
class PrehledOdevzdanychReseni(ListView):
|
||||
model = m.Hodnoceni
|
||||
model = Hodnoceni
|
||||
template_name = 'odevzdavatko/prehled_reseni.html'
|
||||
|
||||
def get_queryset(self):
|
||||
if not self.request.user.is_authenticated:
|
||||
raise RuntimeError("Uživatel měl být přihlášený!")
|
||||
# get_or_none, aby neexistence řešitele (např. u orgů) neházela chybu
|
||||
resitel = m.Resitel.objects.filter(osoba__user=self.request.user).first()
|
||||
resitel = Resitel.objects.filter(osoba__user=self.request.user).first()
|
||||
qs = super().get_queryset()
|
||||
qs = qs.filter(reseni__resitele__in=[resitel])
|
||||
# Setřídíme podle času doručení řešení, aby se netřídily podle okamžiku vyrobení Hodnocení
|
||||
|
@ -355,13 +359,13 @@ class PrehledOdevzdanychReseni(ListView):
|
|||
# Přehled všech řešení kvůli debugování
|
||||
|
||||
class SeznamReseniView(ListView):
|
||||
model = m.Reseni
|
||||
model = Reseni
|
||||
template_name = 'odevzdavatko/seznam.html'
|
||||
|
||||
class SeznamAktualnichReseniView(SeznamReseniView):
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
|
||||
akt_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
|
||||
resitele = resi_v_rocniku(akt_rocnik)
|
||||
qs = qs.filter(resitele__in=resitele) # FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel
|
||||
return qs
|
||||
|
@ -373,7 +377,7 @@ class VlozReseniView(LoginRequiredMixin, FormView):
|
|||
|
||||
def form_valid(self, form):
|
||||
data = form.cleaned_data
|
||||
nove_reseni = m.Reseni.objects.create(
|
||||
nove_reseni = Reseni.objects.create(
|
||||
cas_doruceni=data['cas_doruceni'],
|
||||
forma=data['forma'],
|
||||
poznamka=data['poznamka'],
|
||||
|
@ -400,35 +404,35 @@ class VlozReseniView(LoginRequiredMixin, FormView):
|
|||
|
||||
|
||||
class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView):
|
||||
model = m.Problem
|
||||
model = Problem
|
||||
template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html'
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(stav=m.Problem.STAV_ZADANY, nadproblem__isnull=True)
|
||||
return super().get_queryset().filter(stav=Problem.STAV_ZADANY, nadproblem__isnull=True)
|
||||
|
||||
|
||||
class NahrajReseniView(LoginRequiredMixin, CreateView):
|
||||
model = m.Reseni
|
||||
model = Reseni
|
||||
template_name = 'odevzdavatko/nahraj_reseni.html'
|
||||
form_class = f.NahrajReseniForm
|
||||
nadproblem: m.Problem
|
||||
nadproblem: Problem
|
||||
|
||||
def setup(self, request, *args, **kwargs):
|
||||
super().setup(request, *args, **kwargs)
|
||||
nadproblem_id = self.kwargs["nadproblem_id"]
|
||||
self.nadproblem = get_object_or_404(m.Problem, id=nadproblem_id)
|
||||
self.nadproblem = get_object_or_404(Problem, id=nadproblem_id)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
# Zaříznutí nezadaných problémů
|
||||
if self.nadproblem.stav != m.Problem.STAV_ZADANY:
|
||||
if self.nadproblem.stav != Problem.STAV_ZADANY:
|
||||
raise PermissionDenied()
|
||||
|
||||
|
||||
# Zaříznutí starých řešitelů:
|
||||
# FIXME: Je to tady dost naprasené, mělo by to asi být jinde…
|
||||
osoba = m.Osoba.objects.get(user=self.request.user)
|
||||
osoba = Osoba.objects.get(user=self.request.user)
|
||||
resitel = osoba.resitel
|
||||
if resitel.rok_maturity <= m.Nastaveni.get_solo().aktualni_rocnik.prvni_rok:
|
||||
if resitel.rok_maturity <= Nastaveni.get_solo().aktualni_rocnik.prvni_rok:
|
||||
return render(request, 'universal.html', {
|
||||
'title': 'Nelze odevzdat',
|
||||
'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.',
|
||||
|
@ -440,7 +444,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
|||
nadproblem_id = self.nadproblem.id
|
||||
return {
|
||||
"nadproblem_id": nadproblem_id,
|
||||
"problem": [] if self.nadproblem.podproblem.filter(stav=m.Problem.STAV_ZADANY).exists() else nadproblem_id
|
||||
"problem": [] if self.nadproblem.podproblem.filter(stav=Problem.STAV_ZADANY).exists() else nadproblem_id
|
||||
|
||||
}
|
||||
|
||||
|
@ -452,7 +456,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
|||
data['prilohy'] = f.ReseniSPrilohamiFormSet()
|
||||
|
||||
data["nadproblem_id"] = self.nadproblem.id
|
||||
data["nadproblem"] = get_object_or_404(m.Problem, id=self.nadproblem.id)
|
||||
data["nadproblem"] = get_object_or_404(Problem, id=self.nadproblem.id)
|
||||
return data
|
||||
|
||||
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
|
||||
|
@ -464,17 +468,17 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
|||
return super().form_invalid(form)
|
||||
with transaction.atomic():
|
||||
self.object = form.save()
|
||||
self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user))
|
||||
self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user))
|
||||
self.object.resitele.add(*form.cleaned_data["resitele"])
|
||||
self.object.cas_doruceni = timezone.now()
|
||||
self.object.forma = m.Reseni.FORMA_UPLOAD
|
||||
self.object.forma = Reseni.FORMA_UPLOAD
|
||||
self.object.save()
|
||||
|
||||
prilohy.instance = self.object
|
||||
prilohy.save()
|
||||
|
||||
for hodnoceni in self.object.hodnoceni_set.all():
|
||||
hodnoceni.deadline_body = m.Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
|
||||
hodnoceni.deadline_body = Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
|
||||
hodnoceni.save()
|
||||
|
||||
# Pošleme mail opravovatelům a garantovi
|
||||
|
@ -492,7 +496,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
|||
# FIXME: Víc informativní obsah mailů, možná vč. příloh?
|
||||
prijemci = map(lambda it: it.osoba.email, prijemci)
|
||||
|
||||
resitel = m.Osoba.objects.get(user = self.request.user)
|
||||
resitel = Osoba.objects.get(user = self.request.user)
|
||||
|
||||
seznam = "problému " + str(problemy[0]) if len(problemy) == 1 else 'následujícím problémům:\n' + ', \n'.join(map(str, problemy))
|
||||
seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })")
|
||||
|
@ -500,7 +504,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
|||
EmailMessage(
|
||||
subject="Nové řešení k " + seznam_do_subjectu,
|
||||
body=f"Řešitel{ '' if resitel.pohlavi_muz else 'ka' } { resitel } právě nahrál{'' if resitel.pohlavi_muz else 'a' } nové řešení k { seznam }.\n\nHurá do opravování: { self.object.absolute_url() }",
|
||||
from_email="submitovatko@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení?
|
||||
from_email=settings.NOVE_RESENI_EMAIL,
|
||||
to=list(prijemci),
|
||||
).send()
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group
|
||||
from django_reverse_admin import ReverseModelAdmin
|
||||
import seminar.models as m
|
||||
from .models import *
|
||||
|
||||
|
||||
@admin.register(m.Osoba)
|
||||
@admin.register(Osoba)
|
||||
class OsobaAdmin(admin.ModelAdmin):
|
||||
actions = ['synchronizuj_maily', 'udelej_orgem']
|
||||
search_fields = ['jmeno', 'prijmeni', 'prezdivka']
|
||||
|
@ -27,25 +27,25 @@ class OsobaAdmin(admin.ModelAdmin):
|
|||
user.groups.add(org_group)
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
org = m.Organizator.objects.create(osoba=o)
|
||||
org = Organizator.objects.create(osoba=o)
|
||||
org.save()
|
||||
udelej_orgem.short_description = "Udělej vybraných osob organizátory"
|
||||
|
||||
class OsobaInline(admin.TabularInline):
|
||||
model = m.Osoba
|
||||
model = Osoba
|
||||
|
||||
@admin.register(m.Organizator)
|
||||
@admin.register(Organizator)
|
||||
class OrganizatorAdmin(ReverseModelAdmin):
|
||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
||||
inline_type = 'stacked'
|
||||
inline_reverse = ['osoba']
|
||||
|
||||
@admin.register(m.Resitel)
|
||||
@admin.register(Resitel)
|
||||
class ResitelAdmin(ReverseModelAdmin):
|
||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
||||
ordering = ('osoba__prijmeni', 'osoba__jmeno')
|
||||
inline_type = 'stacked'
|
||||
inline_reverse = ['osoba']
|
||||
|
||||
admin.site.register(m.Skola)
|
||||
admin.site.register(m.Prijemce)
|
||||
admin.site.register(Skola)
|
||||
admin.site.register(Prijemce)
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.contrib.auth.forms import PasswordResetForm
|
|||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from seminar.models import Skola, Resitel, Osoba
|
||||
from .models import *
|
||||
|
||||
from datetime import date
|
||||
import logging
|
||||
|
|
134
personalni/migrations/0002_initial.py
Normal file
134
personalni/migrations/0002_initial.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
# Generated by Django 2.2.28 on 2023-07-31 17:54
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
import django_countries.fields
|
||||
import imagekit.models.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('personalni', '0001_skupiny'),
|
||||
('seminar', '0114_prejmenovani_tabulek'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(
|
||||
state_operations=[
|
||||
migrations.CreateModel(
|
||||
name='Osoba',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('jmeno', models.CharField(max_length=256, verbose_name='jméno')),
|
||||
('prijmeni', models.CharField(max_length=256, verbose_name='příjmení')),
|
||||
('prezdivka', models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka')),
|
||||
('pohlavi_muz', models.BooleanField(default=False, verbose_name='pohlaví (muž)')),
|
||||
('email', models.EmailField(blank=True, default='', max_length=256, verbose_name='e-mail')),
|
||||
('telefon', models.CharField(blank=True, default='', max_length=256, verbose_name='telefon')),
|
||||
('datum_narozeni', models.DateField(blank=True, null=True, verbose_name='datum narození')),
|
||||
('datum_souhlasu_udaje', models.DateField(blank=True, help_text='Datum souhlasu se zpracováním osobních údajů', null=True, verbose_name='datum souhlasu (údaje)')),
|
||||
('datum_souhlasu_zasilani', models.DateField(blank=True, help_text='Datum souhlasu se zasíláním MFF materiálů', null=True, verbose_name='datum souhlasu (spam)')),
|
||||
('datum_registrace', models.DateField(default=django.utils.timezone.now, verbose_name='datum registrace do semináře')),
|
||||
('ulice', models.CharField(blank=True, default='', max_length=256, verbose_name='ulice')),
|
||||
('mesto', models.CharField(blank=True, default='', max_length=256, verbose_name='město')),
|
||||
('psc', models.CharField(blank=True, default='', max_length=32, verbose_name='PSČ')),
|
||||
('stat', django_countries.fields.CountryField(default='CZ', help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)', max_length=2, verbose_name='stát')),
|
||||
('jak_se_dozvedeli', models.TextField(blank=True, verbose_name='Jak se dozvěděli')),
|
||||
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k osobě (plain text)', verbose_name='neveřejná poznámka')),
|
||||
('foto', imagekit.models.fields.ProcessedImageField(blank=True, help_text='Vlož fotografii osoby o libovolné velikosti', null=True, upload_to='image_osoby/velke/%Y/', verbose_name='Fotografie osoby')),
|
||||
('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL, verbose_name='uživatel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Osoba',
|
||||
'verbose_name_plural': 'Osoby',
|
||||
'db_table': 'mam_osoby',
|
||||
'ordering': ['prijmeni', 'jmeno'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Skola',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('aesop_id', models.CharField(blank=True, default='', help_text='Aesopi ID typu "izo:..." nebo "aesop:..."', max_length=32, verbose_name='Aesop ID')),
|
||||
('izo', models.CharField(blank=True, help_text='IZO školy (jen české školy)', max_length=32, verbose_name='IZO')),
|
||||
('nazev', models.CharField(help_text='Celý název školy', max_length=256, verbose_name='název')),
|
||||
('kratky_nazev', models.CharField(blank=True, help_text='Zkrácený název pro zobrazení ve výsledkovce', max_length=256, verbose_name='zkrácený název')),
|
||||
('ulice', models.CharField(max_length=256, verbose_name='ulice')),
|
||||
('mesto', models.CharField(max_length=256, verbose_name='město')),
|
||||
('psc', models.CharField(max_length=32, verbose_name='PSČ')),
|
||||
('stat', django_countries.fields.CountryField(default='CZ', help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)', max_length=2, verbose_name='stát')),
|
||||
('je_zs', models.BooleanField(default=True, verbose_name='základní stupeň')),
|
||||
('je_ss', models.BooleanField(default=True, verbose_name='střední stupeň')),
|
||||
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke škole (plain text)', verbose_name='neveřejná poznámka')),
|
||||
('kontaktni_osoba', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.Osoba', verbose_name='Kontaktní osoba')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Škola',
|
||||
'verbose_name_plural': 'Školy',
|
||||
'db_table': 'mam_skoly',
|
||||
'ordering': ['mesto', 'nazev'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Resitel',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('prezdivka_resitele', models.CharField(blank=True, max_length=256, null=True, unique=True, verbose_name='přezdívka řešitele')),
|
||||
('rok_maturity', models.IntegerField(blank=True, null=True, verbose_name='rok maturity')),
|
||||
('zasilat', models.CharField(choices=[('domu', 'Domů'), ('do_skoly', 'Do školy'), ('nikam', 'Nezasílat papírově')], default='domu', max_length=32, verbose_name='kam zasílat')),
|
||||
('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce řešitel dostávat číslo emailem', verbose_name='zasílat číslo emailem')),
|
||||
('zasilat_cislo_papirove', models.BooleanField(default=True, help_text='True pokud chce řešitel dostávat číslo papírově', verbose_name='zasílat číslo papírově')),
|
||||
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)', verbose_name='neveřejná poznámka')),
|
||||
('osoba', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='personalni.Osoba', verbose_name='osoba')),
|
||||
('skola', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.Skola', verbose_name='škola')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Řešitel',
|
||||
'verbose_name_plural': 'Řešitelé',
|
||||
'db_table': 'mam_resitele',
|
||||
'ordering': ['osoba'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Prijemce',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příemci čísel (plain text)', verbose_name='neveřejná poznámka')),
|
||||
('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce příjemce dostávat číslo emailem', verbose_name='zasílat číslo emailem')),
|
||||
('osoba', models.OneToOneField(help_text='Které osobě či na jakou adresu se mají zasílat čísla', on_delete=django.db.models.deletion.CASCADE, to='personalni.Osoba', verbose_name='komu')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'příjemce',
|
||||
'verbose_name_plural': 'příjemce',
|
||||
'db_table': 'mam_prijemce',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Organizator',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='Vytvořeno')),
|
||||
('organizuje_od', models.DateTimeField(blank=True, null=True, verbose_name='Organizuje od')),
|
||||
('organizuje_do', models.DateTimeField(blank=True, null=True, verbose_name='Organizuje do')),
|
||||
('studuje', models.CharField(blank=True, help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', 'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo 'Přednáší na MFF'", max_length=256, null=True, verbose_name='Studium aj.')),
|
||||
('strucny_popis_organizatora', models.TextField(blank=True, null=True, verbose_name='Stručný popis organizátora')),
|
||||
('skola', models.CharField(blank=True, help_text='Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuješkolu, ale jen obor, možnost zobrazit zvlášť', max_length=256, null=True, verbose_name='Škola, kterou studuje')),
|
||||
('osoba', models.OneToOneField(help_text='osobní údaje organizátora', on_delete=django.db.models.deletion.PROTECT, related_name='org', to='personalni.Osoba', verbose_name='osoba')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organizátor',
|
||||
'verbose_name_plural': 'Organizátoři',
|
||||
'db_table': 'mam_organizatori',
|
||||
'ordering': ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'],
|
||||
},
|
||||
),
|
||||
],
|
||||
database_operations=[],
|
||||
),
|
||||
]
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
|
@ -12,7 +11,9 @@ from django_countries.fields import CountryField
|
|||
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from .base import SeminarModelBase
|
||||
from seminar.models.base import SeminarModelBase
|
||||
|
||||
__all__ = ["Osoba", "Skola", "Prijemce", "Organizator", "Resitel"]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -21,7 +22,7 @@ logger = logging.getLogger(__name__)
|
|||
class Osoba(SeminarModelBase):
|
||||
|
||||
class Meta:
|
||||
db_table = 'seminar_osoby'
|
||||
db_table = 'mam_osoby'
|
||||
verbose_name = 'Osoba'
|
||||
verbose_name_plural = 'Osoby'
|
||||
ordering = ['prijmeni','jmeno']
|
||||
|
@ -115,16 +116,12 @@ class Osoba(SeminarModelBase):
|
|||
u.save()
|
||||
super().save()
|
||||
|
||||
#
|
||||
# Mělo by být částečně vytaženo z Aesopa
|
||||
# viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol.
|
||||
#
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Skola(SeminarModelBase):
|
||||
|
||||
# Mělo by být částečně vytaženo z Aesopa
|
||||
class Meta:
|
||||
db_table = 'seminar_skoly'
|
||||
db_table = 'mam_skoly'
|
||||
verbose_name = 'Škola'
|
||||
verbose_name_plural = 'Školy'
|
||||
ordering = ['mesto', 'nazev']
|
||||
|
@ -177,7 +174,7 @@ class Skola(SeminarModelBase):
|
|||
|
||||
class Prijemce(SeminarModelBase):
|
||||
class Meta:
|
||||
db_table = 'seminar_prijemce'
|
||||
db_table = 'mam_prijemce'
|
||||
verbose_name = 'příjemce'
|
||||
verbose_name_plural = 'příjemce'
|
||||
|
||||
|
@ -205,7 +202,7 @@ class Prijemce(SeminarModelBase):
|
|||
class Resitel(SeminarModelBase):
|
||||
|
||||
class Meta:
|
||||
db_table = 'seminar_resitele'
|
||||
db_table = 'mam_resitele'
|
||||
verbose_name = 'Řešitel'
|
||||
verbose_name_plural = 'Řešitelé'
|
||||
ordering = ['osoba']
|
||||
|
@ -243,30 +240,6 @@ class Resitel(SeminarModelBase):
|
|||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||
help_text='Neveřejná poznámka k řešiteli (plain text)')
|
||||
|
||||
|
||||
def export_row(self):
|
||||
"Slovnik pro pouziti v AESOP exportu"
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.osoba.jmeno,
|
||||
'surname': self.osoba.prijmeni,
|
||||
'gender': 'M' if self.osoba.pohlavi_muz else 'F',
|
||||
'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '',
|
||||
'email': self.osoba.email,
|
||||
'end-year': self.rok_maturity or '',
|
||||
|
||||
'street': self.osoba.ulice,
|
||||
'town': self.osoba.mesto,
|
||||
'postcode': self.osoba.psc,
|
||||
'country': self.osoba.stat,
|
||||
|
||||
'spam-flag': 'Y' if self.osoba.datum_souhlasu_zasilani else '',
|
||||
'spam-date': self.osoba.datum_souhlasu_zasilani.isoformat() if self.osoba.datum_souhlasu_zasilani else '',
|
||||
|
||||
'school': self.skola.aesop_id if self.skola else '',
|
||||
'school-name': str(self.skola) if self.skola else 'Skola neni znama',
|
||||
}
|
||||
|
||||
def rocnik(self, rocnik):
|
||||
"""Vrati skolni rocnik resitele pro zadany Rocnik.
|
||||
Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ZŠ."""
|
||||
|
@ -281,7 +254,7 @@ class Resitel(SeminarModelBase):
|
|||
def vsechny_body(self):
|
||||
"Spočítá body odjakživa."
|
||||
vsechna_reseni = self.reseni_set.all()
|
||||
from .odevzdavatko import Hodnoceni
|
||||
from odevzdavatko.models import Hodnoceni
|
||||
vsechna_hodnoceni = Hodnoceni.objects.filter(
|
||||
reseni__in=vsechna_reseni)
|
||||
return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
|
||||
|
@ -328,8 +301,8 @@ class Resitel(SeminarModelBase):
|
|||
# - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
|
||||
# - proto se započítávají dvojnásobně a byly posunuté hranice titulů
|
||||
# - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
|
||||
from .odevzdavatko import Hodnoceni
|
||||
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
|
||||
from odevzdavatko.models import Hodnoceni
|
||||
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=settings.ROCNIK_INFLACE_BODU,reseni__in=self.reseni_set.all())
|
||||
novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
|
||||
|
||||
def body_z_hodnoceni(hh : list):
|
||||
|
@ -366,8 +339,8 @@ class Resitel(SeminarModelBase):
|
|||
else:
|
||||
return Titul.akad
|
||||
|
||||
from .odevzdavatko import Hodnoceni
|
||||
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
|
||||
from odevzdavatko.models import Hodnoceni
|
||||
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=settings.ROCNIK_INFLACE_TITULU,reseni__in=self.reseni_set.all())
|
||||
novejsi_body = body_z_hodnoceni(
|
||||
Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
|
||||
.difference(hodnoceni_do_26_rocniku)
|
||||
|
@ -438,6 +411,7 @@ class Organizator(SeminarModelBase):
|
|||
return "{} {}".format(self.osoba.jmeno, self.osoba.prijmeni)
|
||||
|
||||
class Meta:
|
||||
db_table = 'mam_organizatori'
|
||||
verbose_name = 'Organizátor'
|
||||
verbose_name_plural = 'Organizátoři'
|
||||
# Řadí aktivní orgy na začátek, pod tím v pořadí od nejstarších neaktivní orgy.
|
223
personalni/testutils.py
Normal file
223
personalni/testutils.py
Normal file
|
@ -0,0 +1,223 @@
|
|||
import unidecode
|
||||
import logging
|
||||
import datetime
|
||||
from pytz import timezone
|
||||
|
||||
from django.contrib.auth.models import Permission, Group
|
||||
import django.contrib.auth
|
||||
|
||||
from .models import *
|
||||
|
||||
User = django.contrib.auth.get_user_model()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# testuje unikátnost vygenerovaného jména
|
||||
def __unikatni_jmeno(osoby, jmeno, prijmeni):
|
||||
for os in osoby:
|
||||
if os.jmeno == jmeno and os.prijmeni == prijmeni:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
def gen_osoby(rnd, size):
|
||||
logger.info('Generuji osoby (size={})...'.format(size))
|
||||
|
||||
jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel']
|
||||
jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', 'Marta Iva', 'Shu Shan']
|
||||
prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', 'Pokora', 'Koch', 'Szegedy', 'Rudý', "von Neumann", "d'Este"]
|
||||
prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', 'Mrkvová', 'Suchá', 'Lovelace', 'Holcová', 'Rui', "Nováčková Tydlitátová"]
|
||||
prezdivky = ['Kaki', 'Hurdur', 'Maracuja', 'Bobbo', "", "", "", "", "", "", "", 'Riki', 'Sapa', "", '', '---', 'Koko']
|
||||
domain = ['example.com', 'dolujeme.eu', 'mff.cuni.cz', 'strcprstskrzkrk.cz', 'british.co.uk', 'splachni.to', 'haha.org']
|
||||
seznam_ulic = ['Krátká', 'Vlhká', 'Jungmanova', '17. listopadu', '4. října', 'Roztocká', 'Forstova', 'Generála Františka Janouška', 'Náměstí Války', 'Svratecké náměstí', 'Zelená lhota', 'Z Plynu', 'K Jezeru', 'U Kocourkova', 'Uštěpačná', 'Ostrorepská', 'Zubří']
|
||||
seznam_mest = ['Praha', 'Brno', 'Ostrava', 'Horní Jelení', 'Dolní Zábrdovice', 'Prdelkov', 'Stará myslivna', 'Kocourkov', 'Šalingrad', 'Medvědí hora', 'Basilej', 'Unterschiedlich', 'Old York', 'Lancastershire', 'Vóloďháza']
|
||||
|
||||
osoby = []
|
||||
# 30 je náhodná konstanta, size je použité na víc místech a
|
||||
# říká, jak velká asi chceme testovací data
|
||||
for i in range(30 * size):
|
||||
pohlavi = rnd.randint(0, 1)
|
||||
jmeno = rnd.choice([jmena_m, jmena_f][pohlavi])
|
||||
prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi])
|
||||
pokusy = 0
|
||||
max_pokusy = 120*size
|
||||
while not __unikatni_jmeno and pokusy < max_pokusy:
|
||||
# pokud jméno a příjmení není unikátní, zkoušíme generovat nová
|
||||
# do daného limitu (abychom se nezacyklili do nekonečna při málo jménech a příjmeních
|
||||
# ze kterých se generuje)
|
||||
jmeno = rnd.choice([jmena_m, jmena_f][pohlavi])
|
||||
prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi])
|
||||
pokusy += 1
|
||||
if pokusy >= max_pokusy:
|
||||
print("Chyba, na danou velikost testovacích dat příliš málo možných jmen a příjmení")
|
||||
exit()
|
||||
prezdivka = rnd.choice(prezdivky)
|
||||
email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)])
|
||||
telefon = "".join([str(rnd.choice([k for k in range(10)])) for _ in range(9)])
|
||||
narozeni = datetime.date(
|
||||
rnd.randint(1980, datetime.datetime.now().year),
|
||||
rnd.randint(1, 12), rnd.randint(1, 28),
|
||||
)
|
||||
ulic = rnd.choice(seznam_ulic)
|
||||
cp = rnd.randint(1, 99)
|
||||
ulice = " ".join([ulic, str(cp)])
|
||||
mesto = rnd.choice(seznam_mest)
|
||||
psc = "".join([str(rnd.choice([k for k in range(10)])) for _ in range(5)])
|
||||
|
||||
osoby.append(Osoba.objects.create(
|
||||
jmeno=jmeno, prijmeni=prijmeni,
|
||||
prezdivka=prezdivka, pohlavi_muz=pohlavi, email=email,
|
||||
telefon=telefon, datum_narozeni=narozeni, ulice=ulice,
|
||||
mesto=mesto, psc=psc,
|
||||
datum_registrace=datetime.date(
|
||||
rnd.randint(2019, 2029),
|
||||
rnd.randint(1, 12), rnd.randint(1, 28)
|
||||
),
|
||||
))
|
||||
# TODO pridat foto male a velke. Jak?
|
||||
# Pavel tvrdí, že to necháme a přidáme až do adminu
|
||||
|
||||
return osoby
|
||||
|
||||
|
||||
def gen_skoly(): # TODO někdy to přepsat, aby jich bylo více
|
||||
logger.info('Generuji školy...')
|
||||
|
||||
skoly = []
|
||||
prvnizs = Skola.objects.create(
|
||||
mesto='Praha', stat='CZ', psc='101 00',
|
||||
ulice='Krátká 5', nazev='První ZŠ', je_zs=True, je_ss=False,
|
||||
)
|
||||
skoly.append(prvnizs)
|
||||
skoly.append(Skola.objects.create(
|
||||
mesto='Praha', stat='CZ', psc='101 00',
|
||||
ulice='Krátká 5', nazev='První SŠ', je_zs=False, je_ss=True,
|
||||
))
|
||||
skoly.append(Skola.objects.create(
|
||||
mesto='Praha', stat='CZ', psc='102 00',
|
||||
ulice='Dlouhá 5', nazev='Druhá SŠ', je_zs=False, je_ss=True,
|
||||
))
|
||||
skoly.append(Skola.objects.create(
|
||||
mesto='Praha', stat='CZ', psc='103 00',
|
||||
ulice='Široká 3', nazev='Třetí SŠ a ZŠ', je_zs=True, je_ss=True,
|
||||
))
|
||||
skoly.append(Skola.objects.create(
|
||||
mesto='Ostrava', stat='CZ', psc='700 00',
|
||||
ulice='Hluboká 42', nazev='Hutní gympl', je_zs=False, je_ss=True,
|
||||
))
|
||||
skoly.append(Skola.objects.create(
|
||||
mesto='Humenné', stat='SK', psc='012 34',
|
||||
ulice='Pltká 1', nazev='Sredná škuola', je_zs=False, je_ss=True,
|
||||
))
|
||||
|
||||
# tohle bude speciální škola, které později dodáme kontaktní osobu
|
||||
zlinska = None
|
||||
zlinska = Skola.objects.create(
|
||||
mesto='Zlín', stat='CZ', psc='76001',
|
||||
ulice='náměstí T.G. Masaryka 2734-9',
|
||||
nazev='Gymnázium a Střední jazyková škola s právem SJZ',
|
||||
kratky_nazev="GaSJŠspSJZ", je_zs=True, je_ss=True,
|
||||
)
|
||||
skoly.append(zlinska)
|
||||
return skoly, zlinska
|
||||
|
||||
|
||||
def gen_resitele(rnd, osoby, skoly):
|
||||
logger.info('Generuji řešitele...')
|
||||
|
||||
resitele = []
|
||||
x = 0
|
||||
resitel_perm = Permission.objects.filter(codename__exact='resitel').first()
|
||||
resitel_group = Group.objects.filter(name__exact='resitel').first()
|
||||
for os in osoby:
|
||||
rand = rnd.randint(0, 8)
|
||||
if not (rand % 8 == 0):
|
||||
if not os.user:
|
||||
if x:
|
||||
user = User.objects.create_user(
|
||||
username='r'+str(x), email=os.email, password='r',
|
||||
)
|
||||
else:
|
||||
user = User.objects.create_user(
|
||||
username='r', email=os.email, password='r',
|
||||
)
|
||||
x += 1
|
||||
os.user = user
|
||||
os.save()
|
||||
os.user.user_permissions.add(resitel_perm)
|
||||
os.user.groups.add(resitel_group)
|
||||
resitele.append(Resitel.objects.create(
|
||||
osoba=os, skola=rnd.choice(skoly),
|
||||
rok_maturity=os.datum_narozeni.year + rnd.randint(18, 21),
|
||||
zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0]
|
||||
))
|
||||
return resitele
|
||||
|
||||
|
||||
def gen_prijemci(rnd, osoby, kolik=10):
|
||||
logger.info('Generuji příjemce (kolik={})...'.format(kolik))
|
||||
prijemci = []
|
||||
for i in rnd.sample(osoby, kolik):
|
||||
prijemci.append(Prijemce.objects.create(osoba=i))
|
||||
return prijemci
|
||||
|
||||
|
||||
def gen_organizatori(rnd, osoby, last_rocnik):
|
||||
logger.info('Generuji organizátory...')
|
||||
organizatori = []
|
||||
|
||||
seznam_konicku = ["vařím", "jezdím na kole", "řeším diferenciální rovnice", "koukám z okna", "tancuji", "programuji", "jezdím vlakem", "nedělám nic"]
|
||||
seznam_oboru = ["matematiku", "matematiku", "matematiku", "fyziku", "literaturu", "informatiku", "informatiku", "běhání dokolečka"]
|
||||
|
||||
x = 0
|
||||
org_perm = Permission.objects.filter(codename__exact='org').first()
|
||||
org_group = Group.objects.filter(name__exact='org').first()
|
||||
for os in osoby:
|
||||
rand = rnd.randint(0, 8)
|
||||
if rand % 8 == 0:
|
||||
pusobnost = rnd.randint(1, last_rocnik)
|
||||
od = datetime.datetime(
|
||||
year=1993 + pusobnost,
|
||||
month=rnd.randint(1, 12),
|
||||
day=rnd.randint(1, 28),
|
||||
tzinfo=timezone('CET'),
|
||||
)
|
||||
do = datetime.datetime(
|
||||
year=od.year + rnd.randint(1, 6),
|
||||
month=rnd.randint(1, 12),
|
||||
day=rnd.randint(1, 28),
|
||||
tzinfo=timezone('CET'),
|
||||
)
|
||||
# aktualni organizatori jeste nemaji vyplnene organizuje_do
|
||||
|
||||
# popis orga
|
||||
konicek1 = rnd.choice(seznam_konicku)
|
||||
konicek2 = rnd.choice(seznam_konicku)
|
||||
obor = rnd.choice(seznam_oboru)
|
||||
popis_orga = "Ve volném čase " + konicek1 + " a také " + konicek2 + ". Studuji " + obor + " a moc mě to baví."
|
||||
|
||||
if do.year > datetime.datetime.now().year:
|
||||
do = None
|
||||
if not os.user:
|
||||
if x:
|
||||
user = User.objects.create_user(
|
||||
username='o'+str(x), email=os.email, password='o',
|
||||
)
|
||||
else:
|
||||
user = User.objects.create_user(
|
||||
username='o', email=os.email, password='o',
|
||||
)
|
||||
x += 1
|
||||
os.user = user
|
||||
os.save()
|
||||
os.user.user_permissions.add(org_perm)
|
||||
os.user.groups.add(org_group)
|
||||
os.user.is_staff = True
|
||||
os.user.save()
|
||||
organizatori.append(Organizator.objects.create(
|
||||
osoba=os,
|
||||
organizuje_od=od, organizuje_do=do,
|
||||
strucny_popis_organizatora=popis_orga
|
||||
))
|
||||
return organizatori
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue