Compare commits
No commits in common. "05e6e5fb59d7d2a8305297053cad2d2aad233247" and "fbd5087c022fa3468ab0a1c3c03fd7a969f99be8" have entirely different histories.
05e6e5fb59
...
fbd5087c02
219 changed files with 809 additions and 499 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -31,15 +31,9 @@ TODO
|
|||
# reversion kvůli historii objektů v reversion
|
||||
**/reversion
|
||||
|
||||
# dokumentace
|
||||
docs/_build
|
||||
docs/modules
|
||||
|
||||
# logy týracího skriptu (./checklinks.sh)
|
||||
/wget.log.*
|
||||
|
||||
# pro lidi, co programují v nástrojích od JetBrains
|
||||
.idea
|
||||
|
||||
# Mac users
|
||||
.DS_Store
|
||||
# dokumentace
|
||||
docs/_build
|
||||
docs/modules
|
16
_git_hooks/README.md
Normal file
16
_git_hooks/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
git hooks
|
||||
=========
|
||||
|
||||
Kontrola stylu pythoních zdrojáků pomocí flake8. Kontrolujeme jen změny,
|
||||
abychom nenutili lidi dělat nesouvisející úpravy, které by rozbíjely historii
|
||||
(git blame).
|
||||
|
||||
pre-commit
|
||||
----------
|
||||
* kontrola změn před commitnutím
|
||||
* instalace: lokálně zkopírovat do .git/hooks (musí být spustitelný)
|
||||
|
||||
update
|
||||
------
|
||||
* kontrola změn přicházejících s pushem
|
||||
* instalace: na atreyi zkopírovat do /akce/MaM/MaMweb/mamweb.git/hooks
|
30
_git_hooks/pre-commit
Executable file
30
_git_hooks/pre-commit
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Git hook script to verify what is about to be committed.
|
||||
# Checks that the changes don't introduce new flake8 errors.
|
||||
|
||||
TMPDIFF=`tempfile`
|
||||
FLAKE8="`git rev-parse --show-toplevel`/bin/flake8"
|
||||
|
||||
status=0
|
||||
|
||||
# select only changed python files which are not migrations
|
||||
changed=`git diff --cached --name-only | grep 'py$' | grep -v 'migrations/[0-9]'`
|
||||
if [ -z $changed ] ; then
|
||||
# Nothing to check. Note the exit is necessary -- we would not pass any
|
||||
# paths to git diff below and it would output the diff unfiltered.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git diff --unified=1 --cached HEAD -- $changed > $TMPDIFF
|
||||
|
||||
# only do the check when there are some changes to be commited
|
||||
# otherwise flake8 would hang waiting for input
|
||||
if [ -s $TMPDIFF ] ; then
|
||||
cat $TMPDIFF | $FLAKE8 --diff
|
||||
status=$?
|
||||
fi
|
||||
|
||||
rm -f $TMPDIFF
|
||||
|
||||
exit $status
|
61
_git_hooks/update
Executable file
61
_git_hooks/update
Executable file
|
@ -0,0 +1,61 @@
|
|||
#!/bin/sh
|
||||
|
||||
# git update hook to check that pushed changes don't introduce new flake8
|
||||
# errors
|
||||
|
||||
# --- Command line
|
||||
refname="$1"
|
||||
oldrev="$2"
|
||||
newrev="$3"
|
||||
|
||||
# --- Safety check
|
||||
if [ -z "$GIT_DIR" ]; then
|
||||
echo "Don't run this script from the command line." >&2
|
||||
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
TMPDIR=`mktemp -d`
|
||||
TMPDIFF=`tempfile`
|
||||
|
||||
[ $refname != "refs/heads/master" -a $refname != "refs/heads/stable" ] && exit 0
|
||||
|
||||
# select only changed python files which are not migrations
|
||||
changed=`git diff --name-only $oldrev $newrev | grep 'py$' | grep -v 'migrations/[0-9]'`
|
||||
if [ -z $changed ] ; then
|
||||
# Nothing to check. Note the exit is necessary -- we would not pass any
|
||||
# paths to git diff below and it would output the diff unfiltered.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git diff --unified=1 $oldrev $newrev -- $changed >${TMPDIFF}
|
||||
|
||||
# there is no working tree in bare git repository, so we recreate it for flake8
|
||||
git archive $newrev | tar -x -C ${TMPDIR}
|
||||
|
||||
cd ${TMPDIR}
|
||||
# report only errors on lines in diff
|
||||
# (if threre was flake8 installed on atrey, we could just call flake8)
|
||||
/akce/MaM/WWW/mamweb-test/bin/flake8 --diff <${TMPDIFF}
|
||||
status=$?
|
||||
if [ $status != 0 ] ; then
|
||||
echo
|
||||
echo -n "Změny, které se snažíte pushnout, obsahují kód v pythonu "
|
||||
echo -n "nevyhovující flake8 (viz výše). Opravte je a zkuste to znovu. "
|
||||
echo -n "Nezapomeňte, že můžete editovat historii (git commit --amend, "
|
||||
echo -n "git rebase -i). Pokud byste chybu příště raději odhalili už při "
|
||||
echo "commitu, zkopírujte si pre-commit hook z _git_hooks do .git/hooks."
|
||||
echo
|
||||
fi
|
||||
|
||||
rm -rf ${TMPDIR}
|
||||
rm -f ${TMPDIFF}
|
||||
|
||||
exit $status
|
|
@ -1,5 +1,8 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AesopConfig(AppConfig):
|
||||
name = 'aesop'
|
||||
verbose_name = 'Export do AESOPa'
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
"""
|
||||
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
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
name = 'api'
|
||||
verbose_name = 'Různá webová API'
|
||||
|
|
12
api/urls.py
12
api/urls.py
|
@ -1,3 +1,15 @@
|
|||
"""
|
||||
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
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
"""
|
||||
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 *
|
||||
|
|
|
@ -1079,29 +1079,5 @@
|
|||
},
|
||||
"model": "sitetree.treeitem",
|
||||
"pk": 53
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"access_guest": false,
|
||||
"access_loggedin": false,
|
||||
"access_perm_type": 1,
|
||||
"access_permissions": [],
|
||||
"access_restricted": true,
|
||||
"alias": null,
|
||||
"description": "",
|
||||
"hidden": false,
|
||||
"hint": "",
|
||||
"inbreadcrumbs": true,
|
||||
"inmenu": true,
|
||||
"insitetree": true,
|
||||
"parent": 20,
|
||||
"sort_order": 54,
|
||||
"title": "Export do abstraktů sousu {{ soustredeni.id }}",
|
||||
"tree": 1,
|
||||
"url": "seminar_soustredeni_abstrakty soustredeni.id",
|
||||
"urlaspattern": true
|
||||
},
|
||||
"model": "sitetree.treeitem",
|
||||
"pk": 54
|
||||
}
|
||||
]
|
||||
|
|
|
@ -331,22 +331,22 @@
|
|||
},
|
||||
{
|
||||
"codename": "add_novinky",
|
||||
"ct_app_label": "novinky",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "novinky"
|
||||
},
|
||||
{
|
||||
"codename": "change_novinky",
|
||||
"ct_app_label": "novinky",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "novinky"
|
||||
},
|
||||
{
|
||||
"codename": "delete_novinky",
|
||||
"ct_app_label": "novinky",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "novinky"
|
||||
},
|
||||
{
|
||||
"codename": "view_novinky",
|
||||
"ct_app_label": "novinky",
|
||||
"ct_app_label": "seminar",
|
||||
"ct_model": "novinky"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -9,6 +9,12 @@ static
|
|||
------
|
||||
Složka, kam django nakopíruje všechno ze složek static a pak na to z templatů / kódu jde ukazovat pomocí ``static``.
|
||||
|
||||
_git_hooks
|
||||
----------
|
||||
Hooky do gitu pro kontrolu Pythoního stylu. Především ``flake8``.
|
||||
|
||||
Zbylo tu z minulosti mamwebu.
|
||||
|
||||
data
|
||||
----
|
||||
Obsahuje data, která patří do databáze, ale jsou přímo součástí webu jako
|
||||
|
|
25
galerie/TODO
Normal file
25
galerie/TODO
Normal file
|
@ -0,0 +1,25 @@
|
|||
========
|
||||
| TODO |
|
||||
|======|
|
||||
|
||||
Aktualni
|
||||
* co s titulni fotkou
|
||||
* do CSS
|
||||
* nahledy
|
||||
* nastylovat tabulku s nahledy
|
||||
* komentare uz na nahledy?
|
||||
* detail
|
||||
* nahledy pred a po
|
||||
* opravit prechodove sipky
|
||||
* vyrobit prechodove sipky ve M&M-stylu
|
||||
|
||||
Dlouhodobe
|
||||
* sipky na prechazeni mezi fotkami
|
||||
* hromadne PRIDANI fotek do jiz existujici galerie
|
||||
|
||||
Fylozoficke
|
||||
* zvolit velikosti velke a male fotky
|
||||
* je potreba i jine razeni nez automaticky podle casu nebo staci podgalerie?
|
||||
* napr. dve hry na dvou ruznych mistech ve stejny cas
|
||||
* fotky od ucastniku ze hry (skupinky se pohybuji ve stejny cas, ale maji sled fotek) -- nestaci to pripadne vrazit do podgalerii?
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
#coding: utf-8
|
||||
|
||||
from galerie.models import Obrazek, Galerie
|
||||
from django.contrib import admin
|
||||
from django.http import HttpResponseRedirect
|
||||
|
|
47
galerie/autocomplete_light_registry.py.old
Normal file
47
galerie/autocomplete_light_registry.py.old
Normal file
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from autocomplete_light import shortcuts as autocomplete_light
|
||||
|
||||
from .models import Obrazek, Galerie
|
||||
from .views import cesta_od_korene
|
||||
|
||||
|
||||
class ObrazekAutocomplete(autocomplete_light.AutocompleteModelBase):
|
||||
|
||||
model = Obrazek
|
||||
search_fields = ['nazev', 'popis']
|
||||
split_words = True
|
||||
limit_choices = 15
|
||||
attrs = {
|
||||
# This will set the input placeholder attribute:
|
||||
'placeholder': u'Obrázek',
|
||||
# This will set the yourlabs.Autocomplete.minimumCharacters
|
||||
# options, the naming conversion is handled by jQuery
|
||||
'data-autocomplete-minimum-characters': 1,
|
||||
}
|
||||
|
||||
choice_html_format = '''
|
||||
<span class="block" data-value="{}">
|
||||
<span class="block">
|
||||
{}
|
||||
<span class="block">{}</span>
|
||||
</span>
|
||||
</span>
|
||||
'''
|
||||
|
||||
def choice_label(self, obrazek):
|
||||
cesta = "/".join(g.nazev for g in cesta_od_korene(obrazek.galerie))
|
||||
popis = "{}<br>".format(obrazek.popis) if obrazek.popis else ""
|
||||
return '{}<br>{}{}'.format(obrazek.nazev, popis, cesta)
|
||||
|
||||
def choice_html(self, obrazek):
|
||||
"""Vrátí kus html i s obrázkem, které se pak ukazuje v nabídce"""
|
||||
return self.choice_html_format.format(self.choice_value(obrazek),
|
||||
obrazek.obrazek_maly_tag(), self.choice_label(obrazek))
|
||||
|
||||
widget_attrs={
|
||||
'data-widget-maximum-values': 15,
|
||||
'class': 'modern-style',
|
||||
}
|
||||
|
||||
autocomplete_light.register(ObrazekAutocomplete)
|
|
@ -1,3 +1,5 @@
|
|||
#coding: utf-8
|
||||
|
||||
from django import forms
|
||||
from seminar.models import Soustredeni
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-04-30 21:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.21 on 2019-06-10 21:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.db import models
|
||||
#from django.db.models import Q
|
||||
from imagekit.models import ImageSpecField
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
"""
|
||||
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 . import views
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# coding: utf-8
|
||||
|
||||
import random
|
||||
|
||||
from django.http import HttpResponse, Http404
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
"""
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HeaderFotkyConfig(AppConfig):
|
||||
name = 'header_fotky'
|
||||
verbose_name = 'Fotky v záhlaví'
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
"""
|
||||
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,4 +1,3 @@
|
|||
FIXME přepsat do rst, přidat i další věci a případně přesunout na wiki
|
||||
Přidání obrázků do odměn:
|
||||
admin -> flatpage odměn -> ikona přidat obrázek
|
||||
záložka odeslat, vybrat obrázek, odeslat
|
11
korektury/TODO
Normal file
11
korektury/TODO
Normal file
|
@ -0,0 +1,11 @@
|
|||
- korektura potrebuje reakci
|
||||
+ komentáře fixně na username
|
||||
- používat skutečné jméno?
|
||||
- vyžádat pozornost autora obsahu
|
||||
- zvednout upload limit na 5MB
|
||||
- sbalit a rozbalit korekturu
|
||||
- nahrávání jiných věcí než PDF - kontrolovat?
|
||||
- stylování
|
||||
- seznam PDF - co zobrazovat?
|
||||
|
||||
|
|
@ -1,3 +1,13 @@
|
|||
"""
|
||||
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.contrib import admin
|
||||
from reversion.admin import VersionAdmin
|
||||
from korektury.models import KorekturovanePDF
|
||||
|
@ -5,6 +15,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
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
"""
|
||||
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,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-04-30 21:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.21 on 2019-06-10 21:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
"""
|
||||
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
|
||||
|
|
3
korektury/tests.py
Normal file
3
korektury/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -1,3 +1,11 @@
|
|||
"""
|
||||
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 . import views
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
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.conf import settings
|
||||
|
@ -194,6 +199,13 @@ 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,
|
||||
|
|
|
@ -7,5 +7,5 @@ make/install_web
|
|||
ensure_venv
|
||||
./manage.py testdata
|
||||
./manage.py loaddata data/*
|
||||
#make/sync_prod_flatpages
|
||||
make/sync_prod_flatpages
|
||||
./manage.py load_org_permissions deploy_v2/admin_org_prava.json
|
||||
|
|
|
@ -42,18 +42,8 @@ def get_app_list(self, request, app_label=None):
|
|||
"""
|
||||
|
||||
app_dict = self._build_app_dict(request, label=app_label)
|
||||
aplikace_nahore = [
|
||||
'seminar',
|
||||
'personalni',
|
||||
'novinky',
|
||||
'korektury',
|
||||
'various',
|
||||
'prednasky',
|
||||
'soustredeni',
|
||||
]
|
||||
# Odhlášený admin má prázdný app_dict :-/
|
||||
app_list = [app_dict[label] for label in aplikace_nahore if label in app_dict] + [app_dict[label] for label in app_dict if label not in aplikace_nahore]
|
||||
|
||||
# Sort the apps alphabetically.
|
||||
app_list = sorted(app_dict.values(), key=lambda x: locale.strxfrm('!') if (x['name'] == "Seminar") else locale.strxfrm(x['name'].lower()))
|
||||
|
||||
# Sort the models alphabetically within each app.
|
||||
for app in app_list:
|
||||
|
|
88
mamweb/middleware.py
Normal file
88
mamweb/middleware.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
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
|
|
@ -68,6 +68,9 @@ MIDDLEWARE = (
|
|||
'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',
|
||||
|
@ -146,7 +149,6 @@ INSTALLED_APPS = (
|
|||
'treenode',
|
||||
'vyroci',
|
||||
'sifrovacka',
|
||||
'novinky',
|
||||
|
||||
# Admin upravy:
|
||||
|
||||
|
@ -342,6 +344,10 @@ 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
|
||||
class InvalidTemplateVariable(str):
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os.path
|
||||
|
||||
#
|
||||
|
@ -68,4 +70,5 @@ LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/
|
|||
|
||||
|
||||
# E-MAIL NOTIFICATIONS
|
||||
POSLI_MAILOVOU_NOTIFIKACI = True
|
||||
LOCAL_TEST_PROD = "prod"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os.path
|
||||
|
||||
#
|
||||
|
@ -72,6 +74,7 @@ LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/
|
|||
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.
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
"""
|
||||
Soubor sloužící jako základní „router“, tj. zde se includují veškeré ostatní urls.
|
||||
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`
|
||||
"""
|
||||
from django.urls import path, include
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Novinky
|
||||
|
||||
admin.site.register(Novinky)
|
|
@ -1,5 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
class NovinkyConfig(AppConfig):
|
||||
name = 'novinky'
|
||||
verbose_name = 'Novinky'
|
|
@ -1,45 +0,0 @@
|
|||
# Generated by Django 4.2.13 on 2024-05-13 20:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
def nastav_nove_contenttypes(apps, schema_editor):
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
for m in ('novinka'):
|
||||
oct = ContentType.objects.filter(app_label='seminar', model=m)
|
||||
oct.update(app_label='novinky')
|
||||
|
||||
def nastav_stare_contenttypes(apps, schema_editor):
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
for m in ('novinka'):
|
||||
nct = ContentType.objects.filter(app_label='novinky', model=m)
|
||||
nct.update(app_label='seminar')
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0127_unmanage_novinky'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
|
||||
migrations.CreateModel(
|
||||
name='Novinky',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('datum', models.DateField(auto_now_add=True)),
|
||||
('text', models.TextField(blank=True, null=True, verbose_name='Text novinky')),
|
||||
('obrazek', models.ImageField(blank=True, null=True, upload_to='image_novinky/%Y/%m/%d/', verbose_name='Obrázek')),
|
||||
('autor', models.ForeignKey(to='personalni.organizator', verbose_name='Autor novinky', null=True, on_delete=models.SET_NULL)),
|
||||
('zverejneno', models.BooleanField(default=False, verbose_name='Zveřejněno')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Novinka',
|
||||
'verbose_name_plural': 'Novinky',
|
||||
'db_table': 'seminar_novinky',
|
||||
'ordering': ['-datum'],
|
||||
'managed': False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 4.2.13 on 2024-05-13 20:58
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('novinky', '0001_initial'),
|
||||
('seminar', '0128_delete_novinky'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='novinky',
|
||||
options={'ordering': ['-datum'], 'verbose_name': 'Novinka', 'verbose_name_plural': 'Novinky'},
|
||||
),
|
||||
]
|
|
@ -1,13 +0,0 @@
|
|||
# Generated by Django 4.2.13 on 2024-05-13 21:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('novinky', '0002_manage_novinky'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
|
@ -1,3 +1,9 @@
|
|||
"""
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class OdevzdavatkoConfig(AppConfig):
|
||||
name = 'odevzdavatko'
|
||||
verbose_name = 'Odevzdávátko'
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
"""
|
||||
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, \
|
||||
|
|
|
@ -504,7 +504,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
|||
|
||||
EmailMessage(
|
||||
subject="Nové řešení k " + seznam_do_subjectu,
|
||||
body=f"{resitel} posílá nové řešení k { seznam }.\n\nHurá do opravování: { self.object.absolute_url() }",
|
||||
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í?
|
||||
to=list(prijemci),
|
||||
).send()
|
||||
|
|
|
@ -5,52 +5,10 @@ from django.contrib.messages import WARNING, ERROR, SUCCESS
|
|||
import seminar.models as m
|
||||
from datetime import datetime
|
||||
|
||||
@admin.action(description="Sjednoť telefony")
|
||||
def sjednot_telefony(admin, request, queryset):
|
||||
for o in queryset:
|
||||
if o.telefon == '':
|
||||
continue
|
||||
try:
|
||||
telefon = int(o.telefon.replace(" ", "").replace("+", ""))
|
||||
# 6 míst
|
||||
if len(str(telefon)) == 9:
|
||||
o.telefon = "+420 " + str(telefon)[0:3] + " " + str(telefon)[3:6] + " " + str(telefon)[6:9]
|
||||
o.save()
|
||||
# 12 míst
|
||||
elif len(str(telefon)) == 12:
|
||||
o.telefon = "+" + str(telefon)[0:3] + " " + str(telefon)[3:6] + " " + str(telefon)[6:9] + " " + str(telefon)[9:12]
|
||||
o.save()
|
||||
else:
|
||||
raise ValueError
|
||||
except:
|
||||
admin.message_user(request, f"{o.jmeno} {o.prijmeni} (id: {o.id}) má divný telefon: {o.telefon}", level=ERROR)
|
||||
admin.message_user(request, "Telefony sjednoceny.", level=SUCCESS)
|
||||
|
||||
# Tohle chceme umět použít i z ResitelAdmin
|
||||
@admin.action(description="Udělej z vybraných osob organizátory")
|
||||
def udelej_orgem(admin, request, queryset):
|
||||
org_group = Group.objects.get(name='org')
|
||||
uspesne_vytvoreni_orgove = 0
|
||||
for o in queryset:
|
||||
if m.Organizator.objects.filter(osoba=o).exists():
|
||||
# Ref: https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.message_user
|
||||
admin.message_user(request, f"Osoba {o} už je org, přeskakuji.", level=WARNING)
|
||||
continue
|
||||
user = o.user
|
||||
if user is None:
|
||||
admin.message_user(request, f"Osoba {o} nemá uživatele! Přeskakuji.", level=ERROR)
|
||||
continue
|
||||
user.groups.add(org_group)
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
org = m.Organizator.objects.create(osoba=o, organizuje_od=datetime.now())
|
||||
org.save()
|
||||
uspesne_vytvoreni_orgove += 1
|
||||
admin.message_user(request, f'Úspěšně vytvořeno {uspesne_vytvoreni_orgove} orgů.', level=SUCCESS)
|
||||
|
||||
@admin.register(m.Osoba)
|
||||
class OsobaAdmin(admin.ModelAdmin):
|
||||
actions = ['synchronizuj_maily', udelej_orgem, sjednot_telefony]
|
||||
actions = ['synchronizuj_maily', 'udelej_orgem']
|
||||
search_fields = ['jmeno', 'prijmeni', 'prezdivka']
|
||||
|
||||
def synchronizuj_maily(self, request, queryset):
|
||||
|
@ -62,6 +20,27 @@ class OsobaAdmin(admin.ModelAdmin):
|
|||
self.message_user(request, "E-maily synchronizovány.")
|
||||
synchronizuj_maily.short_description = "Synchronizuj vybraným osobám e-maily do uživatelů"
|
||||
|
||||
def udelej_orgem(self,request,queryset):
|
||||
org_group = Group.objects.get(name='org')
|
||||
uspesne_vytvoreni_orgove = 0
|
||||
for o in queryset:
|
||||
if m.Organizator.objects.filter(osoba=o).exists():
|
||||
# Ref: https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.message_user
|
||||
self.message_user(request, f"Osoba {o} už je org, přeskakuji.", level=WARNING)
|
||||
continue
|
||||
user = o.user
|
||||
if user is None:
|
||||
self.message_user(request, f"Osoba {o} nemá uživatele! Přeskakuji.", level=ERROR)
|
||||
continue
|
||||
user.groups.add(org_group)
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
org = m.Organizator.objects.create(osoba=o, organizuje_od=datetime.now())
|
||||
org.save()
|
||||
uspesne_vytvoreni_orgove += 1
|
||||
self.message_user(request, f'Úspěšně vytvořeno {uspesne_vytvoreni_orgove} orgů.', level=SUCCESS)
|
||||
udelej_orgem.short_description = "Udělej z vybraných osob organizátory"
|
||||
|
||||
class OsobaInline(admin.TabularInline):
|
||||
model = m.Osoba
|
||||
|
||||
|
@ -74,16 +53,9 @@ class OrganizatorAdmin(ReverseModelAdmin):
|
|||
@admin.register(m.Resitel)
|
||||
class ResitelAdmin(ReverseModelAdmin):
|
||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
||||
list_filter = ['zasilat', 'zasilat_cislo_papirove', 'zasilat_cislo_emailem', 'rok_maturity']
|
||||
ordering = ('osoba__prijmeni', 'osoba__jmeno')
|
||||
inline_type = 'stacked'
|
||||
inline_reverse = ['osoba']
|
||||
|
||||
actions = ['udelej_resitele_orgem']
|
||||
@admin.action(description="Udělej z řešitelů organizátory")
|
||||
def udelej_resitele_orgem(self, req, qs):
|
||||
osoby = m.Osoba.objects.filter(resitel__in=qs)
|
||||
udelej_orgem(self, req, osoby)
|
||||
|
||||
admin.site.register(m.Skola)
|
||||
admin.site.register(m.Prijemce)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Soubor sloužící k pojmenování a jiným nastavením djangovské aplikace.
|
||||
"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PersonalniConfig(AppConfig):
|
||||
name = 'personalni'
|
||||
verbose_name = 'Personální' # Má to nějaký použitelnější název?
|
||||
|
|
|
@ -32,7 +32,7 @@ class UdajeForm(forms.Form):
|
|||
jmeno = forms.CharField(label='Jméno', max_length=256, required=True)
|
||||
prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False)
|
||||
prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True)
|
||||
osloveni = forms.ChoiceField(label='Oslovení', choices=Osoba.OSLOVENI_CHOICES, required=False)
|
||||
pohlavi_muz = forms.ChoiceField(label='Pohlaví', choices=((True, 'muž'), (False, 'žena')), required=True)
|
||||
email = forms.EmailField(label='E-mail', max_length=256, required=True)
|
||||
telefon = forms.CharField(widget=TelInput(), label='Telefon', max_length=256, required=False)
|
||||
datum_narozeni = forms.DateField(widget=DateInput(), label='Datum narození', required=False)
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
# Generated by Django 4.2.11 on 2024-04-30 21:53
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('personalni', '0006_pre_split_soustredeni'),
|
||||
('soustredeni', '0003_post_split_soustredeni'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
|
@ -1,40 +0,0 @@
|
|||
# Generated by Django 4.2.11 on 2024-04-12 14:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
# V migracích nemáme Osoba.OSLOVENI_*, tak si to sem nakopíruji.
|
||||
OSLOVENI_MUZSKE = 'resitel'
|
||||
OSLOVENI_ZENSKE = 'resitelka'
|
||||
OSLOVENI_ZADNE = ''
|
||||
|
||||
def pohlavi_to_osloveni(apps, schema_editor):
|
||||
Osoba = apps.get_model('personalni', 'Osoba')
|
||||
Osoba.objects.filter(pohlavi_muz=True).update(osloveni=OSLOVENI_MUZSKE)
|
||||
Osoba.objects.filter(pohlavi_muz=False).update(osloveni=OSLOVENI_ZENSKE)
|
||||
|
||||
def osloveni_to_pohlavi(apps, schema_editor):
|
||||
Osoba = apps.get_model('personalni', 'Osoba')
|
||||
nebinarni = Osoba.objects.filter(osloveni=OSLOVENI_ZADNE)
|
||||
if nebinarni.count() > 0:
|
||||
raise Exception("Nelze odmigrovat: v databázi jsou nebinární osoby, které starý model nereprezentuje správně.")
|
||||
Osoba.objects.filter(osloveni=OSLOVENI_MUZSKE).update(pohlavi_muz=True)
|
||||
Osoba.objects.filter(osloveni=OSLOVENI_MUZSKE).update(pohlavi_muz=False)
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('personalni', '0007_post_split_soustredeni'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='osoba',
|
||||
name='osloveni',
|
||||
field=models.CharField(blank=True, choices=[('resitel', 'Řešitel'), ('resitelka', 'Řešitelka')], max_length=32, verbose_name='Oslovení'),
|
||||
),
|
||||
migrations.RunPython(pohlavi_to_osloveni, osloveni_to_pohlavi),
|
||||
migrations.RemoveField(
|
||||
model_name='osoba',
|
||||
name='pohlavi_muz',
|
||||
),
|
||||
]
|
|
@ -1,13 +0,0 @@
|
|||
# Generated by Django 4.2.13 on 2024-05-13 20:35
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('personalni', '0008_reforma_pohlavi'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
|
@ -1,14 +0,0 @@
|
|||
# Generated by Django 4.2.13 on 2024-05-13 20:59
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('personalni', '0009_novinky_pre'),
|
||||
('novinky', '0003_novinky_post'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 4.2.13 on 2024-06-03 14:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('personalni', '0010_novinky_post'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='osoba',
|
||||
name='osloveni',
|
||||
field=models.CharField(blank=True, choices=[('resitel', 'Řešitel'), ('resitelka', 'Řešitelka'), ('', 'Cokoliv jiného')], max_length=32, verbose_name='Oslovení'),
|
||||
),
|
||||
]
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
|
@ -37,16 +38,8 @@ class Osoba(SeminarModelBase):
|
|||
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,
|
||||
verbose_name='uživatel', on_delete=models.DO_NOTHING)
|
||||
|
||||
# Pohlaví nás prakticky nezajímá, reálně.
|
||||
OSLOVENI_MUZSKE = 'resitel'
|
||||
OSLOVENI_ZENSKE = 'resitelka'
|
||||
OSLOVENI_ZADNE = ''
|
||||
OSLOVENI_CHOICES = [
|
||||
(OSLOVENI_MUZSKE, 'Řešitel'),
|
||||
(OSLOVENI_ZENSKE, 'Řešitelka'),
|
||||
(OSLOVENI_ZADNE, 'Cokoliv jiného'), # Reálně nás u nikoho jiného oslovení nezajímá? (A pohlaví už vůbec)
|
||||
]
|
||||
osloveni = models.CharField('Oslovení', choices=OSLOVENI_CHOICES, max_length=32, blank=True)
|
||||
# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování)
|
||||
pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
|
||||
|
||||
email = models.EmailField('e-mail', max_length=256, blank=True, default='')
|
||||
|
||||
|
@ -253,19 +246,11 @@ class Resitel(SeminarModelBase):
|
|||
|
||||
def export_row(self):
|
||||
"Slovnik pro pouziti v AESOP exportu"
|
||||
# Ref: https://opmk.mff.cuni.cz/wiki/aesop/import#telo
|
||||
|
||||
# FUJ: Oslovení nemusí souviset s genderem.
|
||||
gender = {
|
||||
Osoba.OSLOVENI_MUZSKE: 'M',
|
||||
Osoba.OSLOVENI_ZENSKE: 'F',
|
||||
Osoba.OSLOVENI_ZADNE: '',
|
||||
}[self.osoba.osloveni]
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.osoba.jmeno,
|
||||
'surname': self.osoba.prijmeni,
|
||||
'gender': gender,
|
||||
'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 '',
|
||||
|
|
|
@ -13,18 +13,18 @@
|
|||
<li>soustředění</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="{% url 'admin:novinky_novinky_add' %}"><strong>přidat novinku</strong></a> na web</li>
|
||||
<li><a href="/admin/seminar/novinky/add/"><strong>přidat novinku</strong></a> na web</li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h2><strong>Tvorba čísla</strong></h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="{% url 'admin:seminar_problem_add' %}"><strong>přidat téma</strong></a></li>
|
||||
<li><a href="/admin/seminar/problem/add/"><strong>přidat téma</strong></a></li>
|
||||
<li><strong>korektury</strong>
|
||||
<ul>
|
||||
<li><a href="{% url 'korektury_list' %}">korekturování</a></li>
|
||||
<li><a href="{% url 'admin:korektury_korekturovanepdf_add' %}">přidat pdf k opravám</a></li>
|
||||
<li><a href="/korektury/">korekturování</a></li>
|
||||
<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -70,15 +70,15 @@
|
|||
<h2><strong>Soustředění</strong></h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="{% url 'admin:soustredeni_soustredeni_add' %}">přidat soustředění</a></li>
|
||||
<li><a href="/admin/seminar/soustredeni/add/">přidat soustředění</a></li>
|
||||
<li><strong>přednášky</strong>
|
||||
|
||||
<ul>
|
||||
<li><a href="{% url 'admin:prednasky_prednaska_add' %}">vypisování přednášek</a></li>
|
||||
<li><a href="/admin/prednasky/prednaska/">vypisování přednášek</a></li>
|
||||
<li>hlasování o přednáškách</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="{% url 'seminar_seznam_soustredeni' %}">proběhlá soustředění</a>
|
||||
<li><a href="/soustredeni/probehlo/">proběhlá soustředění</a>
|
||||
<ul>
|
||||
<li>vytvoření galerie</li>
|
||||
<li>stažení seznamu účastníků</li>
|
||||
|
@ -91,7 +91,7 @@
|
|||
<h2><strong>Můj profil</strong></h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="{% url 'admin:personalni_organizator_change' organizator.id %}"><strong>upravit </strong></a></li>
|
||||
<li><a href="/admin/seminar/organizator/{{ organizator.id }}/change/"><strong>upravit </strong></a></li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
|
@ -108,6 +108,6 @@
|
|||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>Nemůžeš najít, co hledáš? Může to být v <a href="{% url 'admin:index' %}">administračním rozhraní webu</a>.</p>
|
||||
<p>Nemůžeš najít, co hledáš? Může to být v <a href="/admin/">administračním rozhraní webu</a>.</p>
|
||||
{% endblock content %}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
{% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.osloveni%}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.email %}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %}
|
||||
{% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
"""
|
||||
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 django.contrib.auth.decorators import login_required
|
||||
from . import views
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.views import generic
|
|||
from django.db.models import Q, Count, Min
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.contrib.auth.models import User, Permission, Group, AnonymousUser
|
||||
from django.contrib.auth.models import User, Permission, Group
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponse
|
||||
|
@ -139,7 +139,7 @@ def resitelEditView(request):
|
|||
form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak
|
||||
osoba_edit.jmeno = fcd['jmeno']
|
||||
osoba_edit.prijmeni = fcd['prijmeni']
|
||||
osoba_edit.osloveni = fcd['osloveni']
|
||||
osoba_edit.pohlavi_muz = fcd['pohlavi_muz']
|
||||
osoba_edit.email = fcd['email']
|
||||
osoba_edit.telefon = fcd['telefon']
|
||||
osoba_edit.ulice = fcd['ulice']
|
||||
|
@ -209,7 +209,7 @@ def prihlaskaView(request):
|
|||
o = s.Osoba(
|
||||
jmeno = fcd['jmeno'],
|
||||
prijmeni = fcd['prijmeni'],
|
||||
osloveni = fcd['osloveni'],
|
||||
pohlavi_muz = fcd['pohlavi_muz'],
|
||||
email = fcd['email'],
|
||||
telefon = fcd.get('telefon',''),
|
||||
datum_narozeni = fcd.get('datum_narozeni',None),
|
||||
|
@ -242,7 +242,7 @@ def prihlaskaView(request):
|
|||
|
||||
# Porovnání údajů
|
||||
assert orig_osoba.user is None, "Právě-registrující-se osoba už má Uživatele!"
|
||||
osoba_attrs = ['jmeno', 'prijmeni', 'osloveni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'stat', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_registrace']
|
||||
osoba_attrs = ['jmeno', 'prijmeni', 'pohlavi_muz', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'stat', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_registrace']
|
||||
diffattrs = []
|
||||
for attr in osoba_attrs:
|
||||
new = getattr(o, attr)
|
||||
|
@ -318,12 +318,6 @@ def prihlaskaView(request):
|
|||
# Jen hloupé rozhazovátko
|
||||
def profilView(request):
|
||||
user = request.user
|
||||
if not isinstance(user, AnonymousUser) and m.Osoba.objects.filter(user=user).count() != 1:
|
||||
# m.Osoba.objects.get() v ostatních views selže
|
||||
return render(request, "universal.html", {
|
||||
'title': 'Krize identity.',
|
||||
'raw_html': r'<blockquote>Zvláštní pocit, že jo?<br>[…]<br>Co to znamená?<br>— Že ti MaMweb neumí říct, kdo jsi.<br>A <a href="/admin">Admin</a> ano?<br>— V tom je rozdíl.</blockquote> — Matrix (1999), parafrázováno',
|
||||
})
|
||||
if user.has_perm('auth.org'):
|
||||
return OrgoRozcestnikView.as_view()(request)
|
||||
if user.has_perm('auth.resitel'):
|
||||
|
@ -345,7 +339,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True):
|
|||
'osoba__telefon',
|
||||
'osoba__user__username',
|
||||
'osoba__datum_narozeni',
|
||||
'osoba__osloveni',
|
||||
'osoba__pohlavi_muz',
|
||||
'osoba__ulice',
|
||||
'osoba__mesto',
|
||||
'osoba__psc',
|
||||
|
@ -373,7 +367,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True):
|
|||
'osoba__telefon': 'telefon',
|
||||
'osoba__user__username': 'user',
|
||||
'osoba__datum_narozeni': 'datum_narozeni',
|
||||
'osoba__osloveni': 'osloveni',
|
||||
'osoba__pohlavi_muz': 'pohlavi_muz',
|
||||
'osoba__ulice': 'ulice',
|
||||
'osoba__mesto': 'mesto',
|
||||
'osoba__psc': 'psc',
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from django.contrib import messages
|
||||
from reversion.admin import VersionAdmin
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding: utf-8
|
||||
from django import forms
|
||||
|
||||
class NewPrednaskyForm(forms.Form):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue