Browse Source

Merge branch 'tabs'

pull/13/head
Jonas Havelka 2 years ago
parent
commit
2393c76d63
  1. 2
      api/views/autocomplete.py
  2. 84
      header_fotky/context_processors.py
  3. 34
      korektury/views.py
  4. 32
      mamweb/admin.py
  5. 152
      mamweb/middleware.py
  6. 386
      mamweb/settings_common.py
  7. 96
      mamweb/settings_local.py
  8. 20
      mamweb/settings_prod.py
  9. 28
      mamweb/settings_test.py
  10. 18
      seminar/models/base.py
  11. 42
      seminar/models/novinky.py
  12. 234
      seminar/models/odevzdavatko.py
  13. 18
      seminar/testutils.py
  14. 2
      seminar/views/views_all.py
  15. 50
      soustredeni/admin.py
  16. 90
      treenode/admin.py
  17. 4
      treenode/permissions.py
  18. 24
      various/autentizace/utils.py
  19. 8
      various/log_filters.py

2
api/views/autocomplete.py

@ -12,7 +12,7 @@ from .helpers import LoginRequiredAjaxMixin
class SkolaAutocomplete(autocomplete.Select2QuerySetView): class SkolaAutocomplete(autocomplete.Select2QuerySetView):
""" View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """ """ View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """
def get_queryset(self): def get_queryset(self):
# Don't forget to filter out results depending on the visitor ! # Don't forget to filter out results depending on the visitor !
qs = m.Skola.objects.all() qs = m.Skola.objects.all()
if self.q: if self.q:
words = self.q.split(' ') #TODO re split podle bileho znaku words = self.q.split(' ') #TODO re split podle bileho znaku

84
header_fotky/context_processors.py

@ -12,46 +12,46 @@ from header_fotky.models import FotkaUrlVazba
def vzhled(request): def vzhled(request):
""" """
Podle času přidá do contextu, zdali je nebo není noc. Dále podle dení Podle času přidá do contextu, zdali je nebo není noc. Dále podle dení
doby a url přidá do contextu správnou fotku. doby a url přidá do contextu správnou fotku.
url adresu nejprve vyzkouší celou, pak postupně odřezává věci za url adresu nejprve vyzkouší celou, pak postupně odřezává věci za
lomítkem, dokud nenajde url, pro kterou existuje alespoň jedna fotka. lomítkem, dokud nenajde url, pro kterou existuje alespoň jedna fotka.
Z fotek pro toto url zkusí vybrat tu ve správné denní době a poté Z fotek pro toto url zkusí vybrat tu ve správné denní době a poté
libovolnou. (Z více možných fotek pro 1 url a 1 dobu vybírá náhodně.) libovolnou. (Z více možných fotek pro 1 url a 1 dobu vybírá náhodně.)
""" """
hodin = datetime.now().hour hodin = datetime.now().hour
if (hodin <= 6) or (hodin >= 20): if (hodin <= 6) or (hodin >= 20):
noc = True noc = True
nedoba = 'den' nedoba = 'den'
doba = 'noc' doba = 'noc'
else: else:
noc = False noc = False
nedoba = 'noc' nedoba = 'noc'
doba = 'den' doba = 'den'
url = request.path url = request.path
fotky = FotkaUrlVazba.objects.exclude(denni_doba=nedoba) fotky = FotkaUrlVazba.objects.exclude(denni_doba=nedoba)
fotka = None fotka = None
# TODO rychlejší patternmatch? # TODO rychlejší patternmatch?
while (fotka is None) and (url != ''): while (fotka is None) and (url != ''):
presne = fotky.filter(url__exact=url) presne = fotky.filter(url__exact=url)
if presne.count() > 0: if presne.count() > 0:
presne_doba = presne.filter(denni_doba=doba) presne_doba = presne.filter(denni_doba=doba)
if presne_doba.count() > 0: if presne_doba.count() > 0:
fotka = random.choice(presne_doba).url_fotky() fotka = random.choice(presne_doba).url_fotky()
else: else:
fotka = random.choice(presne).url_fotky() fotka = random.choice(presne).url_fotky()
url = url[:-1] url = url[:-1]
index = url.rfind('/') index = url.rfind('/')
if index != -1: if index != -1:
url = url[:index+1] url = url[:index+1]
if fotka is None: if fotka is None:
fotka = settings.STATIC_URL + "images/header/vikendovka.jpg" fotka = settings.STATIC_URL + "images/header/vikendovka.jpg"
return {'noc': noc, 'fotka': fotka} return {'noc': noc, 'fotka': fotka}

34
korektury/views.py

@ -30,28 +30,28 @@ class KorekturyListView(generic.ListView):
template_name = 'korektury/seznam.html' template_name = 'korektury/seznam.html'
class KorekturyAktualniListView(KorekturyListView): class KorekturyAktualniListView(KorekturyListView):
def get_queryset(self, *args, **kwargs): def get_queryset(self, *args, **kwargs):
queryset=super().get_queryset() queryset=super().get_queryset()
queryset=queryset.exclude(status="zastarale") queryset=queryset.exclude(status="zastarale")
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['selected'] = 'aktualni' context['selected'] = 'aktualni'
return context return context
class KorekturyZastaraleListView(KorekturyListView): class KorekturyZastaraleListView(KorekturyListView):
def get_queryset(self, *args, **kwargs): def get_queryset(self, *args, **kwargs):
queryset=super().get_queryset() queryset=super().get_queryset()
queryset=queryset.filter(status="zastarale").order_by("-cas") queryset=queryset.filter(status="zastarale").order_by("-cas")
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['selected'] = 'zastarale' context['selected'] = 'zastarale'
return context return context
class KorekturySeskupeneListView(KorekturyAktualniListView): class KorekturySeskupeneListView(KorekturyAktualniListView):
template_name = 'korektury/seskupeny_seznam.html' template_name = 'korektury/seskupeny_seznam.html'

32
mamweb/admin.py

@ -17,14 +17,14 @@ from ckeditor_uploader.widgets import CKEditorUploadingWidget
class FlatpageForm(FlatpageFormOld): class FlatpageForm(FlatpageFormOld):
content = forms.CharField(widget=CKEditorUploadingWidget()) content = forms.CharField(widget=CKEditorUploadingWidget())
class Meta: class Meta:
model = FlatPage # this is not automatically inherited from FlatpageFormOld model = FlatPage # this is not automatically inherited from FlatpageFormOld
exclude = [] exclude = []
class FlatPageAdmin(FlatPageAdminOld): class FlatPageAdmin(FlatPageAdminOld):
form = FlatpageForm form = FlatpageForm
# We have to unregister the normal admin, and then reregister ours # We have to unregister the normal admin, and then reregister ours
@ -36,19 +36,19 @@ locale.setlocale(locale.LC_COLLATE, 'cs_CZ.UTF-8')
# https://books.agiliq.com/projects/django-admin-cookbook/en/latest/set_ordering.html # https://books.agiliq.com/projects/django-admin-cookbook/en/latest/set_ordering.html
# FIXME zpraseno pomocí toho, že Python umí bez problému přepisovat funkce # FIXME zpraseno pomocí toho, že Python umí bez problému přepisovat funkce
def get_app_list(self, request): def get_app_list(self, request):
""" """
Return a sorted list of all the installed apps that have been Return a sorted list of all the installed apps that have been
registered in this site. registered in this site.
""" """
app_dict = self._build_app_dict(request) app_dict = self._build_app_dict(request)
# Sort the apps alphabetically. # Sort the apps alphabetically.
app_list = sorted(app_dict.values(), key=lambda x: locale.strxfrm('!') if (x['name'] == "Seminar") else locale.strxfrm(x['name'].lower())) app_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. # Sort the models alphabetically within each app.
for app in app_list: for app in app_list:
app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower())) app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower()))
return app_list return app_list
AdminSite.get_app_list = get_app_list AdminSite.get_app_list = get_app_list

152
mamweb/middleware.py

@ -6,83 +6,83 @@ from django.http import HttpResponse, HttpResponseRedirect
class LoggedInHintCookieMiddleware(object): class LoggedInHintCookieMiddleware(object):
"""Middleware to securely help with 'logged-in' detection for dual HTTP/HTTPS sites. """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 On insecure requests: Checks for a (non-secure) cookie settings.LOGGED_IN_HINT_COOKIE_NAME
and if present, redirects to HTTPS (same adress). and if present, redirects to HTTPS (same adress).
Note this usually breaks non-GET (POST) requests. Note this usually breaks non-GET (POST) requests.
On secure requests: Updates cookie settings.LOGGED_IN_HINT_COOKIE_NAME to reflect 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). 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. The cookie is set to expire at the same time as the sessionid cookie.
By default, LOGGED_IN_HINT_COOKIE_NAME = 'logged_in_hint'. By default, LOGGED_IN_HINT_COOKIE_NAME = 'logged_in_hint'.
""" """
def __init__(self): def __init__(self):
if hasattr(settings, 'LOGGED_IN_HINT_COOKIE_NAME'): if hasattr(settings, 'LOGGED_IN_HINT_COOKIE_NAME'):
self.cookie_name = settings.LOGGED_IN_HINT_COOKIE_NAME self.cookie_name = settings.LOGGED_IN_HINT_COOKIE_NAME
else: self.cookie_name = 'logged_in_hint' else: self.cookie_name = 'logged_in_hint'
self.cookie_value = 'True' self.cookie_value = 'True'
def cookie_correct(self, request): def cookie_correct(self, request):
return self.cookie_name in request.COOKIES and request.COOKIES[self.cookie_name] == self.cookie_value return self.cookie_name in request.COOKIES and request.COOKIES[self.cookie_name] == self.cookie_value
def process_request(self, request): def process_request(self, request):
if not request.is_secure(): if not request.is_secure():
if self.cookie_correct(request): if self.cookie_correct(request):
# redirect insecure (assuming http) requests with hint cookie to https # redirect insecure (assuming http) requests with hint cookie to https
url = request.build_absolute_uri() url = request.build_absolute_uri()
assert url[:5] == 'http:' assert url[:5] == 'http:'
return HttpResponseRedirect('https:' + url[5:]) return HttpResponseRedirect('https:' + url[5:])
return None return None
def process_response(self, request, response): def process_response(self, request, response):
if request.is_secure(): if request.is_secure():
# assuming full session info (as the conn. is secure) # assuming full session info (as the conn. is secure)
try: try:
user = request.user user = request.user
except AttributeError: # no user - ajax or other special request except AttributeError: # no user - ajax or other special request
return response return response
if user.is_authenticated(): if user.is_authenticated():
if not self.cookie_correct(request): if not self.cookie_correct(request):
expiry = None if request.session.get_expire_at_browser_close() else request.session.get_expiry_date() 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) response.set_cookie(self.cookie_name, value=self.cookie_value, expires=expiry, secure=False)
else: else:
if self.cookie_name in request.COOKIES: if self.cookie_name in request.COOKIES:
response.delete_cookie(self.cookie_name) response.delete_cookie(self.cookie_name)
return response return response
class vzhled: class vzhled:
def process_request(self, request): def process_request(self, request):
return None return None
def process_view(self, request, view_func, view_args, view_kwargs): def process_view(self, request, view_func, view_args, view_kwargs):
#print "====== process_request ======" #print "====== process_request ======"
#print view_func #print view_func
#print view_args #print view_args
#print view_kwargs #print view_kwargs
#print "=============================" #print "============================="
return None return None
def process_template_response(self, request, response): def process_template_response(self, request, response):
hodin = datetime.now().hour hodin = datetime.now().hour
if (hodin <= 6) or (hodin >= 14): # TODO 20 if (hodin <= 6) or (hodin >= 14): # TODO 20
response.context_data['noc'] = True response.context_data['noc'] = True
else: else:
response.context_data['noc'] = False response.context_data['noc'] = False
return response return response
def process_response(self, request, response): def process_response(self, request, response):
#hodin = datetime.now().hour #hodin = datetime.now().hour
#if (hodin <= 6) or (hodin >= 14): # TODO 20 #if (hodin <= 6) or (hodin >= 14): # TODO 20
#response.context_data['noc'] = True #response.context_data['noc'] = True
#else: #else:
#response.context_data['noc'] = False #response.context_data['noc'] = False
return response return response
##def process_exception(request, exception): ##def process_exception(request, exception):
#pass #pass

386
mamweb/settings_common.py

@ -40,8 +40,8 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.FileSystemFinder',
) )
# Where redirect for login required services # Where redirect for login required services
@ -57,41 +57,41 @@ DOBA_ODHLASENI_PRI_ZASKRTNUTI_NEODHLASOVAT = 365 * 24 * 3600 # rok
# Modules configuration # Modules configuration
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
) )
MIDDLEWARE = ( MIDDLEWARE = (
# 'reversion.middleware.RevisionMiddleware', # 'reversion.middleware.RevisionMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
# FIXME: rozbilo se při přechodu na Django 2.0, nevím, jestli # FIXME: rozbilo se při přechodu na Django 2.0, nevím, jestli
# se to dá zahodit bez náhrady # se to dá zahodit bez náhrady
# 'mamweb.middleware.LoggedInHintCookieMiddleware', # 'mamweb.middleware.LoggedInHintCookieMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
) )
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], 'DIRS': [],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': ( 'context_processors': (
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'sekizai.context_processors.sekizai', 'sekizai.context_processors.sekizai',
'header_fotky.context_processors.vzhled', 'header_fotky.context_processors.vzhled',
'various.context_processors.rozliseni', 'various.context_processors.rozliseni',
'various.context_processors.april', 'various.context_processors.april',
) )
}, },
}, },
] ]
@ -99,59 +99,59 @@ TEMPLATES = [
INSTALLED_APPS = ( INSTALLED_APPS = (
# Basic # Basic
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.sites', 'django.contrib.sites',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.auth', 'django.contrib.auth',
# Utilities # Utilities
'sekizai', 'sekizai',
'reversion', 'reversion',
'django_countries', 'django_countries',
'solo', 'solo',
'ckeditor', 'ckeditor',
'ckeditor_uploader', 'ckeditor_uploader',
'taggit', 'taggit',
'dal', 'dal',
'dal_select2', 'dal_select2',
'crispy_forms', 'crispy_forms',
'django_comments', 'django_comments',
'django.contrib.flatpages', 'django.contrib.flatpages',
'django.contrib.humanize', 'django.contrib.humanize',
'sitetree', 'sitetree',
'imagekit', 'imagekit',
'polymorphic', 'polymorphic',
'webpack_loader', 'webpack_loader',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
# MaMweb # MaMweb
'mamweb', 'mamweb',
'seminar', 'seminar',
'galerie', 'galerie',
'korektury', 'korektury',
'prednasky', 'prednasky',
'header_fotky', 'header_fotky',
'various', 'various',
'various.autentizace', 'various.autentizace',
'api', 'api',
'aesop', 'aesop',
'odevzdavatko', 'odevzdavatko',
'vysledkovky', 'vysledkovky',
'personalni', 'personalni',
'soustredeni', 'soustredeni',
'treenode', 'treenode',
# Admin upravy: # Admin upravy:
# 'material', # 'material',
# 'material.admin', # 'material.admin',
@ -159,76 +159,76 @@ INSTALLED_APPS = (
# 'admin_tools.theming', # 'admin_tools.theming',
# 'admin_tools.menu', # 'admin_tools.menu',
# 'admin_tools.dashboard', # 'admin_tools.dashboard',
'django.contrib.admin', 'django.contrib.admin',
# Nechat na konci (INSTALLED_APPS je uspořádané): # Nechat na konci (INSTALLED_APPS je uspořádané):
'django_cleanup.apps.CleanupConfig', # Uklízí media/ 'django_cleanup.apps.CleanupConfig', # Uklízí media/
) )
DEBUG_TOOLBAR_CONFIG = { DEBUG_TOOLBAR_CONFIG = {
'SHOW_COLLAPSED': True, 'SHOW_COLLAPSED': True,
} }
SUMMERNOTE_CONFIG = { SUMMERNOTE_CONFIG = {
'iframe': False, 'iframe': False,
'airMode': False, 'airMode': False,
'attachment_require_authentication': True, 'attachment_require_authentication': True,
'width': '80%', 'width': '80%',
# 'height': '30em', # 'height': '30em',
'toolbar': [ 'toolbar': [
['style', ['style']], ['style', ['style']],
['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']], ['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']],
['color', ['color']], ['color', ['color']],
['para', ['ul', 'ol', 'paragraph']], ['para', ['ul', 'ol', 'paragraph']],
['table', ['table']], ['table', ['table']],
['insert', ['link', 'picture', 'hr']], ['insert', ['link', 'picture', 'hr']],
['view', ['fullscreen', 'codeview']], ['view', ['fullscreen', 'codeview']],
['help', ['help']], ['help', ['help']],
] ]
} }
CKEDITOR_UPLOAD_PATH = "uploads/" CKEDITOR_UPLOAD_PATH = "uploads/"
CKEDITOR_IMAGE_BACKEND = 'pillow' 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 = { CKEDITOR_CONFIGS = {
'default': { 'default': {
'entities': False, 'entities': False,
'toolbar': [ 'toolbar': [
['Source', 'ShowBlocks', '-', 'Maximize'], ['Source', 'ShowBlocks', '-', 'Maximize'],
['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'], ['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'], ['Link', 'Unlink', 'Anchor', '-', 'Image', 'Table', 'HorizontalRule'],
['Format'], ['Format'],
], ],
# 'toolbar': 'full', # 'toolbar': 'full',
'height': '40em', 'height': '40em',
'width': '100%', 'width': '100%',
'toolbarStartupExpanded': False, 'toolbarStartupExpanded': False,
'allowedContent' : True, 'allowedContent' : True,
}, },
} }
# Webpack loader # Webpack loader
VUE_FRONTEND_DIR = os.path.join(BASE_DIR, 'vue_frontend') VUE_FRONTEND_DIR = os.path.join(BASE_DIR, 'vue_frontend')
WEBPACK_LOADER = { WEBPACK_LOADER = {
'DEFAULT': { 'DEFAULT': {
'CACHE': False, 'CACHE': False,
'BUNDLE_DIR_NAME': 'vue/', # must end with slash 'BUNDLE_DIR_NAME': 'vue/', # must end with slash
'STATS_FILE': os.path.join(VUE_FRONTEND_DIR, 'webpack-stats.json'), 'STATS_FILE': os.path.join(VUE_FRONTEND_DIR, 'webpack-stats.json'),
'POLL_INTERVAL': 0.1, 'POLL_INTERVAL': 0.1,
'TIMEOUT': None, 'TIMEOUT': None,
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'] 'IGNORE': [r'.+\.hot-update.js', r'.+\.map']
} }
} }
# Dajngo REST Framework # Dajngo REST Framework
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100 'PAGE_SIZE': 100
} }
@ -236,22 +236,22 @@ REST_FRAMEWORK = {
# Create file 'django.secret' in every install (it is not kept in git) # Create file 'django.secret' in every install (it is not kept in git)
try: try:
with open(os.path.join(os.path.dirname(__file__), '..', 'django.secret')) as f: with open(os.path.join(os.path.dirname(__file__), '..', 'django.secret')) as f:
SECRET_KEY = f.readline().strip() SECRET_KEY = f.readline().strip()
except: except:
SECRET_KEY = '12345zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzdqwkdlkj' SECRET_KEY = '12345zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzdqwkdlkj'
# Logging # Logging
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': False, 'disable_existing_loggers': False,
'formatters': { 'formatters': {
'verbose': { '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'
}, },
}, },
'filters': { 'filters': {
'Http404AsInfo': { 'Http404AsInfo': {
@ -262,76 +262,76 @@ LOGGING = {
}, },
}, },
'loggers': { 'loggers': {
'django': { 'django': {
'handlers': ['console'], 'handlers': ['console'],
'level': 'DEBUG', 'level': 'DEBUG',
'filters': ['StripSensitiveFormData'], 'filters': ['StripSensitiveFormData'],
}, },
'django.security.csrf': { 'django.security.csrf': {
'handlers': ['console'], 'handlers': ['console'],
'level': 'DEBUG', 'level': 'DEBUG',
'filters': ['StripSensitiveFormData'], 'filters': ['StripSensitiveFormData'],
}, },
'django.request': { 'django.request': {
'handlers': ['console'], 'handlers': ['console'],
'level': 'DEBUG', 'level': 'DEBUG',
'filters': ['Http404AsInfo'], 'filters': ['Http404AsInfo'],
}, },
'seminar.prihlaska.form':{ 'seminar.prihlaska.form':{
'handlers': ['console','registration_logfile'], 'handlers': ['console','registration_logfile'],
'level': 'INFO' 'level': 'INFO'
}, },
'seminar.prihlaska.problem':{ 'seminar.prihlaska.problem':{
'handlers': ['console','mail_registration','registration_error_log'], 'handlers': ['console','mail_registration','registration_error_log'],
'level': 'INFO' 'level': 'INFO'
}, },
# Catch-all logger # Catch-all logger
'': { '': {
'handlers': ['console'], # Add 'mail_admins' in prod and test 'handlers': ['console'], # Add 'mail_admins' in prod and test
'level': 'DEBUG', 'level': 'DEBUG',
'filters': ['StripSensitiveFormData'], 'filters': ['StripSensitiveFormData'],
}, },
}, },
'handlers': { 'handlers': {
'console': { 'console': {
'level': 'WARNING', ## Set to 'DEBUG' in local 'level': 'WARNING', ## Set to 'DEBUG' in local
'class': 'logging.StreamHandler', 'class': 'logging.StreamHandler',
'formatter': 'verbose', 'formatter': 'verbose',
}, },
'mail_admins': { 'mail_admins': {
'level': 'WARNING', 'level': 'WARNING',
'class': 'django.utils.log.AdminEmailHandler', 'class': 'django.utils.log.AdminEmailHandler',
'formatter': 'verbose', 'formatter': 'verbose',
'filters': ['StripSensitiveFormData'], 'filters': ['StripSensitiveFormData'],
}, },
'mail_registration': { 'mail_registration': {
'level': 'WARNING', 'level': 'WARNING',
'class': 'django.utils.log.AdminEmailHandler', 'class': 'django.utils.log.AdminEmailHandler',
'formatter': 'verbose', 'formatter': 'verbose',
}, },
'registration_logfile':{ 'registration_logfile':{
'level': 'INFO', 'level': 'INFO',
'class': 'logging.FileHandler', 'class': 'logging.FileHandler',
# filename declared in specific configuration files # filename declared in specific configuration files
'formatter': 'verbose', 'formatter': 'verbose',
}, },
'registration_error_log':{ 'registration_error_log':{
'level': 'INFO', 'level': 'INFO',
'class': 'logging.FileHandler', 'class': 'logging.FileHandler',
# filename declared in specific configuration files # filename declared in specific configuration files
'formatter': 'verbose', 'formatter': 'verbose',
}, },
}, },
} }
# Permissions for uploads # Permissions for uploads
FILE_UPLOAD_PERMISSIONS = 0o0644 FILE_UPLOAD_PERMISSIONS = 0o0644
@ -352,14 +352,14 @@ POSLI_MAILOVOU_NOTIFIKACI = False
# Logování chyb # Logování chyb
class InvalidTemplateVariable(str): class InvalidTemplateVariable(str):
def __mod__(self, variable): def __mod__(self, variable):
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
for line in traceback.walk_stack(None): for line in traceback.walk_stack(None):
if 'context' in line[0].f_locals and 'request' in line[0].f_locals['context']: if 'context' in line[0].f_locals and 'request' in line[0].f_locals['context']:
logger.warning("Proměnná '%s' neexistuje: %s" % (variable, line[0].f_locals['context']['request'])) logger.warning("Proměnná '%s' neexistuje: %s" % (variable, line[0].f_locals['context']['request']))
break break
return '' return ''
TEMPLATES[0]['OPTIONS']['string_if_invalid'] = InvalidTemplateVariable('%s') TEMPLATES[0]['OPTIONS']['string_if_invalid'] = InvalidTemplateVariable('%s')
# Django 3.2 vyžaduje explicitní nastavení autoklíče, zatím nechápu proč # Django 3.2 vyžaduje explicitní nastavení autoklíče, zatím nechápu proč

96
mamweb/settings_local.py

@ -11,16 +11,16 @@ import os.path
from .settings_common import * from .settings_common import *
MIDDLEWARE += ( MIDDLEWARE += (
'debug_toolbar.middleware.DebugToolbarMiddleware', 'debug_toolbar.middleware.DebugToolbarMiddleware',
) )
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
INSTALLED_APPS += ( INSTALLED_APPS += (
'debug_toolbar', 'debug_toolbar',
'django_extensions', 'django_extensions',
) )
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
@ -37,10 +37,10 @@ ALLOWED_HOSTS.append('localhost')
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db-local.sqlite3'), 'NAME': os.path.join(BASE_DIR, 'db-local.sqlite3'),
} }
} }
#DATABASES = { #DATABASES = {
# 'default': { # 'default': {
@ -52,46 +52,46 @@ DATABASES = {
# LOGGING # LOGGING
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': True, 'disable_existing_loggers': True,
'filters': { 'filters': {
'require_debug_false': { 'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse' '()': 'django.utils.log.RequireDebugFalse'
} }
}, },
'formatters': { 'formatters': {
'simple': { 'simple': {
'format': '%(asctime)s - %(name)s - %(levelname)-8s - %(message)s', 'format': '%(asctime)s - %(name)s - %(levelname)-8s - %(message)s',
}, },
}, },
'handlers': { 'handlers': {
'dummy': { 'dummy': {
'class': 'logging.NullHandler', 'class': 'logging.NullHandler',
}, },
'console': { 'console': {
'level': 'DEBUG', 'level': 'DEBUG',
'class': 'logging.StreamHandler', 'class': 'logging.StreamHandler',
'formatter': 'simple', 'formatter': 'simple',
}, },
}, },
'loggers': { 'loggers': {
# Vypisovani databazovych dotazu do konzole # Vypisovani databazovych dotazu do konzole
#'django.db.backends': { #'django.db.backends': {
# 'level': 'DEBUG', # 'level': 'DEBUG',
# 'handlers': ['console'], # 'handlers': ['console'],
# 'propagate': False, # 'propagate': False,
#}, #},
'werkzeug': { 'werkzeug': {
'handlers': ['console'], 'handlers': ['console'],
'level': 'DEBUG', 'level': 'DEBUG',
'propagate': True, 'propagate': True,
}, },
'': { '': {
'handlers': ['console'], 'handlers': ['console'],
'level': 'DEBUG', 'level': 'DEBUG',
'propagate': False, 'propagate': False,
}, },
}, },
} }
# set to 'DEBUG' for EXTRA verbose output # set to 'DEBUG' for EXTRA verbose output

20
mamweb/settings_prod.py

@ -16,8 +16,8 @@ from .settings_common import *
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
INSTALLED_APPS += ( INSTALLED_APPS += (
'django_extensions', 'django_extensions',
) )
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
assert not SECRET_KEY.startswith('12345') assert not SECRET_KEY.startswith('12345')
@ -34,14 +34,14 @@ ALLOWED_HOSTS = ['mam.mff.cuni.cz', 'www.mam.mff.cuni.cz', 'atrey.karlin.mff.cun
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'mam_prod', 'NAME': 'mam_prod',
'USER': 'mam-web', 'USER': 'mam-web',
'TEST': { 'TEST': {
'NAME': 'mam-prod-testdb', 'NAME': 'mam-prod-testdb',
}, },
}, },
} }
import os import os

28
mamweb/settings_test.py

@ -13,16 +13,16 @@ import os.path
from .settings_common import * # zatim nutne, casem snad vyresime # noqa from .settings_common import * # zatim nutne, casem snad vyresime # noqa
MIDDLEWARE += ( MIDDLEWARE += (
'debug_toolbar.middleware.DebugToolbarMiddleware', 'debug_toolbar.middleware.DebugToolbarMiddleware',
) )
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
INSTALLED_APPS += ( INSTALLED_APPS += (
'debug_toolbar', 'debug_toolbar',
'django_extensions', 'django_extensions',
) )
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ')^u=i65*zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzv9l+zo)n' SECRET_KEY = ')^u=i65*zmr_k53a*@f4q_+ji^o@!pgpef*5&8c7zzv9l+zo)n'
@ -38,21 +38,21 @@ ALLOWED_HOSTS = ['*.mam.mff.cuni.cz', 'atrey.karlin.mff.cuni.cz', 'mam.mff.cuni.
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'mam_test', 'NAME': 'mam_test',
'USER': 'mam-web', 'USER': 'mam-web',
'TEST': { 'TEST': {
'NAME': 'mam-test-testdb', 'NAME': 'mam-test-testdb',
}, },
}, },
} }
import os import os
SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz' SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz'
ADMINS = [ ADMINS = [
('M&M ERRORs', 'mam-errors@mam.mff.cuni.cz'), ('M&M ERRORs', 'mam-errors@mam.mff.cuni.cz'),
] ]

18
seminar/models/base.py

@ -4,18 +4,18 @@ from django.db import models
class SeminarModelBase(models.Model): class SeminarModelBase(models.Model):
class Meta: class Meta:
abstract = True abstract = True
def verejne(self): def verejne(self):
return False return False
# def get_absolute_url(self): # def get_absolute_url(self):
# return "https://" + str(get_current_site(None)) + self.verejne_url() # return "https://" + str(get_current_site(None)) + self.verejne_url()
def admin_url(self): def admin_url(self):
model_name = self.__class__.__name__.lower() model_name = self.__class__.__name__.lower()
return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, )) return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, ))
# def verejne_url(self): # def verejne_url(self):
# return None # return None

42
seminar/models/novinky.py

@ -9,30 +9,30 @@ from . import personalni as pm
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Novinky(models.Model): class Novinky(models.Model):
class Meta: class Meta:
verbose_name = 'Novinka' verbose_name = 'Novinka'
verbose_name_plural = 'Novinky' verbose_name_plural = 'Novinky'
ordering = ['-datum'] ordering = ['-datum']
datum = models.DateField(auto_now_add=True) datum = models.DateField(auto_now_add=True)
text = models.TextField('Text novinky', blank=True, null=True) text = models.TextField('Text novinky', blank=True, null=True)
obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/', obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/',
null=True, blank=True) null=True, blank=True)
obrazek_maly = ImageSpecField(source='obrazek', obrazek_maly = ImageSpecField(source='obrazek',
processors=[ processors=[
ResizeToFit(350, 200, upscale=False) ResizeToFit(350, 200, upscale=False)
], ],
options={'quality': 95}) options={'quality': 95})
autor = models.ForeignKey(pm.Organizator, verbose_name='Autor novinky', null=True, autor = models.ForeignKey(pm.Organizator, verbose_name='Autor novinky', null=True,
on_delete=models.SET_NULL) on_delete=models.SET_NULL)
zverejneno = models.BooleanField('Zveřejněno', default=False) zverejneno = models.BooleanField('Zveřejněno', default=False)
def __str__(self): def __str__(self):
if self.text: if self.text:
return '[' + str(self.datum) + '] ' + self.text[0:50] return '[' + str(self.datum) + '] ' + self.text[0:50]
else: else:
return '[' + str(self.datum) + '] ' return '[' + str(self.datum) + '] '

234
seminar/models/odevzdavatko.py

@ -18,68 +18,68 @@ from seminar.models import base as bm
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Reseni(bm.SeminarModelBase): class Reseni(bm.SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_reseni' db_table = 'seminar_reseni'
verbose_name = 'Řešení' verbose_name = 'Řešení'
verbose_name_plural = 'Řešení' verbose_name_plural = 'Řešení'
#ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to.
ordering = ['-cas_doruceni'] ordering = ['-cas_doruceni']
# Interní ID # Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. # 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(am.Problem, verbose_name='problém', help_text='Problém',
through='Hodnoceni') through='Hodnoceni')
resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení', resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení',
help_text='Seznam autorů řešení', through='Reseni_Resitele') help_text='Seznam autorů řešení', through='Reseni_Resitele')
cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True)
FORMA_PAPIR = 'papir' FORMA_PAPIR = 'papir'
FORMA_EMAIL = 'email' FORMA_EMAIL = 'email'
FORMA_UPLOAD = 'upload' FORMA_UPLOAD = 'upload'
FORMA_CHOICES = [ FORMA_CHOICES = [
(FORMA_PAPIR, 'Papírové řešení'), (FORMA_PAPIR, 'Papírové řešení'),
(FORMA_EMAIL, 'Emailem'), (FORMA_EMAIL, 'Emailem'),
(FORMA_UPLOAD, 'Upload přes web'), (FORMA_UPLOAD, 'Upload přes web'),
] ]
forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
default=FORMA_EMAIL) default=FORMA_EMAIL)
text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení', text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení',
blank=True, null=True, related_name="reseni_cely_set", blank=True, null=True, related_name="reseni_cely_set",
on_delete=models.PROTECT) on_delete=models.PROTECT)
poznamka = models.TextField('neveřejná poznámka', blank=True, poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešení (plain text)') help_text='Neveřejná poznámka k řešení (plain text)')
zverejneno = models.BooleanField('řešení zveřejněno', default=False, zverejneno = models.BooleanField('řešení zveřejněno', default=False,
help_text='Udává, zda je řešení zveřejněno') help_text='Udává, zda je řešení zveřejněno')
def verejne_url(self): def verejne_url(self):
return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id]))
def absolute_url(self): def absolute_url(self):
return "https://" + str(get_current_site(None)) + self.verejne_url() return "https://" + str(get_current_site(None)) + self.verejne_url()
# má OneToOneField s: # má OneToOneField s:
# Konfera # Konfera
# má ForeignKey s: # má ForeignKey s:
# Hodnoceni # Hodnoceni
def sum_body(self): def sum_body(self):
return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"]
def __str__(self): def __str__(self):
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
# NOTE: Potenciální DB HOG (bez select_related) # NOTE: Potenciální DB HOG (bez select_related)
def deadline_reseni(self): def deadline_reseni(self):
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
## Pravdepodobne uz nebude potreba: ## Pravdepodobne uz nebude potreba:
# def save(self, *args, **kwargs): # def save(self, *args, **kwargs):
@ -89,112 +89,112 @@ class Reseni(bm.SeminarModelBase):
# super(Reseni, self).save(*args, **kwargs) # super(Reseni, self).save(*args, **kwargs)
class Hodnoceni(bm.SeminarModelBase): class Hodnoceni(bm.SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_hodnoceni' db_table = 'seminar_hodnoceni'
verbose_name = 'Hodnocení' verbose_name = 'Hodnocení'
verbose_name_plural = 'Hodnocení' verbose_name_plural = 'Hodnocení'
# Interní ID # Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=True, null=True) blank=True, null=True)
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) 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 # 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(am.Deadline, verbose_name='deadline pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
problem = models.ForeignKey(am.Problem, verbose_name='problém', problem = models.ForeignKey(am.Problem, verbose_name='problém',
related_name='hodnoceni', on_delete=models.PROTECT) related_name='hodnoceni', on_delete=models.PROTECT)
feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
def __str__(self): def __str__(self):
return "{}, {}, {}".format(self.problem, self.reseni, self.body) return "{}, {}, {}".format(self.problem, self.reseni, self.body)
def generate_filename(self, filename): def generate_filename(self, filename):
return os.path.join( return os.path.join(
settings.SEMINAR_RESENI_DIR, settings.SEMINAR_RESENI_DIR,
am.aux_generate_filename(self, filename) am.aux_generate_filename(self, filename)
) )
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class PrilohaReseni(bm.SeminarModelBase): class PrilohaReseni(bm.SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_priloha_reseni' db_table = 'seminar_priloha_reseni'
verbose_name = 'Příloha řešení' verbose_name = 'Příloha řešení'
verbose_name_plural = 'Přílohy řešení' verbose_name_plural = 'Přílohy řešení'
ordering = ['reseni', 'vytvoreno'] ordering = ['reseni', 'vytvoreno']
# Interní ID # Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy',
on_delete=models.CASCADE) on_delete=models.CASCADE)
vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False)
soubor = models.FileField('soubor', upload_to = generate_filename) soubor = models.FileField('soubor', upload_to = generate_filename)
poznamka = models.TextField('neveřejná poznámka', blank=True, poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu')
res_poznamka = models.TextField('poznámka řešitele', blank=True, res_poznamka = models.TextField('poznámka řešitele', blank=True,
help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje') help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje')
def __str__(self): def __str__(self):
return str(self.soubor) return str(self.soubor)
def split(self): def split(self):
"Vrátí cestu rozsekanou po složkách. To se hodí v templatech" "Vrátí cestu rozsekanou po složkách. To se hodí v templatech"
# Věřím, že tohle funguje, případně použít os.path nebo pathlib. # Věřím, že tohle funguje, případně použít os.path nebo pathlib.
return self.soubor.url.split('/') return self.soubor.url.split('/')
# Vazebna tabulka. Mozna se generuje automaticky. # Vazebna tabulka. Mozna se generuje automaticky.
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Reseni_Resitele(models.Model): class Reseni_Resitele(models.Model):
class Meta: class Meta:
db_table = 'seminar_reseni_resitele' db_table = 'seminar_reseni_resitele'
verbose_name = 'Řešení řešitelů' verbose_name = 'Řešení řešitelů'
verbose_name_plural = 'Řešení řešitelů' verbose_name_plural = 'Řešení řešitelů'
ordering = ['reseni', 'resitele'] ordering = ['reseni', 'resitele']
# Interní ID # Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
# podil - jakou merou se ktery resitel podilel na danem reseni # podil - jakou merou se ktery resitel podilel na danem reseni
# - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni # - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni
def __str__(self): def __str__(self):
return '{} od {}'.format(self.reseni, self.resitel) return '{} od {}'.format(self.reseni, self.resitel)
# NOTE: Poteciální DB HOG bez select_related # NOTE: Poteciální DB HOG bez select_related
class ReseniNode(tm.TreeNode): class ReseniNode(tm.TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_otistene_reseni' db_table = 'seminar_nodes_otistene_reseni'
verbose_name = 'Otištěné řešení (Node)' verbose_name = 'Otištěné řešení (Node)'
verbose_name_plural = 'Otištěná řešení (Node)' verbose_name_plural = 'Otištěná řešení (Node)'
reseni = models.ForeignKey(Reseni, reseni = models.ForeignKey(Reseni,
on_delete=models.PROTECT, on_delete=models.PROTECT,
verbose_name = 'reseni') verbose_name = 'reseni')
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "ReseniNode: "+str(self.reseni) self.nazev = "ReseniNode: "+str(self.reseni)
def getOdkazStr(self): def getOdkazStr(self):
return str(self.reseni) return str(self.reseni)

18
seminar/testutils.py

@ -474,15 +474,15 @@ def get_text():
def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
tema = Tema.objects.create( tema = Tema.objects.create(
nazev=nazev, nazev=nazev,
stav=Problem.STAV_ZADANY, stav=Problem.STAV_ZADANY,
zamereni="M", zamereni="M",
autor=rnd.choice(organizatori), autor=rnd.choice(organizatori),
garant=rnd.choice(organizatori), garant=rnd.choice(organizatori),
kod=str(kod), kod=str(kod),
tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
rocnik=rocnik, rocnik=rocnik,
abstrakt = lorem.paragraph() abstrakt = lorem.paragraph()
) )
# Generování struktury k tématu # Generování struktury k tématu

2
seminar/views/views_all.py

@ -686,7 +686,7 @@ def formularOKView(request, text=''):
] ]
context = { context = {
'odkazy': odkazy, 'odkazy': odkazy,
'text': text, 'text': text,
} }
return render(request, template_name, context) return render(request, template_name, context)

50
soustredeni/admin.py

@ -6,38 +6,38 @@ from seminar.models import soustredeni as m
class SoustredeniUcastniciInline(admin.TabularInline): class SoustredeniUcastniciInline(admin.TabularInline):
model = m.Soustredeni_Ucastnici model = m.Soustredeni_Ucastnici
extra = 1 extra = 1
fields = ['resitel','poznamka'] fields = ['resitel','poznamka']
autocomplete_fields = ['resitel'] autocomplete_fields = ['resitel']
ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni'] ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni']
formfield_overrides = { formfield_overrides = {
models.TextField: {'widget': widgets.TextInput} models.TextField: {'widget': widgets.TextInput}
} }
def get_queryset(self,request): def get_queryset(self,request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
return qs.select_related('resitel','soustredeni') return qs.select_related('resitel','soustredeni')
class SoustredeniOrganizatoriInline(admin.TabularInline): class SoustredeniOrganizatoriInline(admin.TabularInline):
model = m.Soustredeni.organizatori.through model = m.Soustredeni.organizatori.through
extra = 1 extra = 1
fields = ['organizator','poznamka'] fields = ['organizator','poznamka']
autocomplete_fields = ['organizator'] autocomplete_fields = ['organizator']
ordering = ['organizator__osoba__jmeno','organizator__prijmeni'] ordering = ['organizator__osoba__jmeno','organizator__prijmeni']
formfield_overrides = { formfield_overrides = {
models.TextField: {'widget': widgets.TextInput} models.TextField: {'widget': widgets.TextInput}
} }
def get_queryset(self,request): def get_queryset(self,request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
return qs.select_related('organizator', 'soustredeni') return qs.select_related('organizator', 'soustredeni')
@admin.register(m.Soustredeni) @admin.register(m.Soustredeni)
class SoustredeniAdmin(admin.ModelAdmin): class SoustredeniAdmin(admin.ModelAdmin):
model = m.Soustredeni model = m.Soustredeni
inline_type = 'tabular' inline_type = 'tabular'
inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline]

90
treenode/admin.py

@ -9,80 +9,80 @@ import seminar.models as m
@admin.register(m.TreeNode) @admin.register(m.TreeNode)
class TreeNodeAdmin(PolymorphicParentModelAdmin): class TreeNodeAdmin(PolymorphicParentModelAdmin):
base_model = m.TreeNode base_model = m.TreeNode
child_models = [ child_models = [
m.RocnikNode, m.RocnikNode,
m.CisloNode, m.CisloNode,
m.MezicisloNode, m.MezicisloNode,
m.TemaVCisleNode, m.TemaVCisleNode,
m.UlohaZadaniNode, m.UlohaZadaniNode,
m.PohadkaNode, m.PohadkaNode,
m.UlohaVzorakNode, m.UlohaVzorakNode,
m.TextNode, m.TextNode,
m.CastNode, m.CastNode,
m.OrgTextNode, m.OrgTextNode,
] ]
actions = ['aktualizuj_nazvy'] actions = ['aktualizuj_nazvy']
# XXX: nejspíš je to totální DB HOG, nechcete to použít moc často. # XXX: nejspíš je to totální DB HOG, nechcete to použít moc často.
def aktualizuj_nazvy(self, request, queryset): def aktualizuj_nazvy(self, request, queryset):
newqs = queryset.get_real_instances() newqs = queryset.get_real_instances()
for tn in newqs: for tn in newqs:
tn.aktualizuj_nazev() tn.aktualizuj_nazev()
tn.save() tn.save()
self.message_user(request, "Názvy aktualizovány.") self.message_user(request, "Názvy aktualizovány.")
aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy"
@admin.register(m.RocnikNode) @admin.register(m.RocnikNode)
class RocnikNodeAdmin(PolymorphicChildModelAdmin): class RocnikNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.RocnikNode base_model = m.RocnikNode
show_in_index = True show_in_index = True
@admin.register(m.CisloNode) @admin.register(m.CisloNode)
class CisloNodeAdmin(PolymorphicChildModelAdmin): class CisloNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.CisloNode base_model = m.CisloNode
show_in_index = True show_in_index = True
@admin.register(m.MezicisloNode) @admin.register(m.MezicisloNode)
class MezicisloNodeAdmin(PolymorphicChildModelAdmin): class MezicisloNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.MezicisloNode base_model = m.MezicisloNode
show_in_index = True show_in_index = True
@admin.register(m.TemaVCisleNode) @admin.register(m.TemaVCisleNode)
class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.TemaVCisleNode base_model = m.TemaVCisleNode
show_in_index = True show_in_index = True
@admin.register(m.UlohaZadaniNode) @admin.register(m.UlohaZadaniNode)
class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.UlohaZadaniNode base_model = m.UlohaZadaniNode
show_in_index = True show_in_index = True
@admin.register(m.PohadkaNode) @admin.register(m.PohadkaNode)
class PohadkaNodeAdmin(PolymorphicChildModelAdmin): class PohadkaNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.PohadkaNode base_model = m.PohadkaNode
show_in_index = True show_in_index = True
@admin.register(m.UlohaVzorakNode) @admin.register(m.UlohaVzorakNode)
class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin): class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.UlohaVzorakNode base_model = m.UlohaVzorakNode
show_in_index = True show_in_index = True
@admin.register(m.TextNode) @admin.register(m.TextNode)
class TextNodeAdmin(PolymorphicChildModelAdmin): class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.TextNode base_model = m.TextNode
show_in_index = True show_in_index = True
@admin.register(m.CastNode) @admin.register(m.CastNode)
class TextNodeAdmin(PolymorphicChildModelAdmin): class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.CastNode base_model = m.CastNode
show_in_index = True show_in_index = True
fields = ('nadpis',) fields = ('nadpis',)
@admin.register(m.OrgTextNode) @admin.register(m.OrgTextNode)
class TextNodeAdmin(PolymorphicChildModelAdmin): class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.OrgTextNode base_model = m.OrgTextNode
show_in_index = True show_in_index = True

4
treenode/permissions.py

@ -2,6 +2,6 @@ from rest_framework.permissions import BasePermission
class AllowWrite(BasePermission): class AllowWrite(BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
return request.user.has_perm('auth.org') return request.user.has_perm('auth.org')

24
various/autentizace/utils.py

@ -6,16 +6,16 @@ from django.utils.http import urlsafe_base64_encode
def posli_reset_hesla(u, request=None): def posli_reset_hesla(u, request=None):
uid = urlsafe_base64_encode(force_bytes(u.pk)) uid = urlsafe_base64_encode(force_bytes(u.pk))
token = PasswordResetTokenGenerator().make_token(u) token = PasswordResetTokenGenerator().make_token(u)
url = "https://%s%s" % ( url = "https://%s%s" % (
str(get_current_site(request)), str(get_current_site(request)),
str(reverse_lazy("reset_password_confirm", args=[uid, token])) str(reverse_lazy("reset_password_confirm", args=[uid, token]))
) )
u.email_user( u.email_user(
subject="Vítej mezi řešiteli M&M!", subject="Vítej mezi řešiteli M&M!",
message="""Milý řešiteli, milá řešitelko, message="""Milý řešiteli, milá řešitelko,
tvůj e-mail byl právě zaregistrován na mam.matfyz.cz. Heslo si prosím nastav na: %s tvůj e-mail byl právě zaregistrován na mam.matfyz.cz. Heslo si prosím nastav na: %s
@ -26,6 +26,6 @@ Organizátoři M&M
Tento e-mail byl vygenerován automaticky, chceš-li nás kontaktovat, napiš nám na adresu mam@matfyz.cz. Tento e-mail byl vygenerován automaticky, chceš-li nás kontaktovat, napiš nám na adresu mam@matfyz.cz.
""" % url, """ % url,
# TODO: templates/autentizace a django/contrib/auth/forms.py říkají, jak na to lépe # TODO: templates/autentizace a django/contrib/auth/forms.py říkají, jak na to lépe
from_email="registrace@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? from_email="registrace@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení?
) )

8
various/log_filters.py

@ -2,10 +2,10 @@ from logging import Filter, INFO
from django.urls import reverse from django.urls import reverse
class Http404AsInfoFilter(Filter): class Http404AsInfoFilter(Filter):
def filter(self, record): def filter(self, record):
if record.name == 'django.request' and record.status_code == 404: if record.name == 'django.request' and record.status_code == 404:
record.levelno = INFO record.levelno = INFO
return 1 # Keep the log record return 1 # Keep the log record
class StripSensitiveFormDataFilter(Filter): class StripSensitiveFormDataFilter(Filter):
def filter(self, record): def filter(self, record):

Loading…
Cancel
Save