Merge branch 'data_migrations' into odevzdavatko
This commit is contained in:
commit
a8f91e1222
21 changed files with 946 additions and 835 deletions
4
Makefile
4
Makefile
|
@ -34,8 +34,8 @@ install_web: venv_check
|
|||
pip install --upgrade setuptools
|
||||
# Instalace závislostí webu
|
||||
pip install -r requirements.txt --upgrade
|
||||
# Po vygenerování testdat spusť ./manage.py loaddata sitetree_new.json, ať máš menu
|
||||
# Pro synchronizaci flatpages spusť make sync_prod_flatpages
|
||||
# Po vygenerování testdat spusť ./manage.py loaddata data/*, ať máš menu a další modely
|
||||
:x
|
||||
|
||||
install_venv:
|
||||
${VENV} ${VENV_PATH}
|
||||
|
|
File diff suppressed because it is too large
Load diff
12
fix_json.py
Executable file
12
fix_json.py
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("input", type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('output', type=argparse.FileType('w', encoding='utf-8'))
|
||||
args = parser.parse_args()
|
||||
|
||||
data = json.load(args.input)
|
||||
json.dump(data, args.output, ensure_ascii=False, sort_keys=True, indent='\t')
|
|
@ -186,7 +186,7 @@ class KorekturyView(generic.TemplateView):
|
|||
if email:
|
||||
emails.discard(email)
|
||||
|
||||
if not settings.SEND_EMAIL_NOTIFICATIONS:
|
||||
if not settings.POSLI_MAILOVOU_NOTIFIKACI:
|
||||
print("Poslal bych upozornění na tyto adresy: ", " ".join(emails))
|
||||
return
|
||||
|
||||
|
|
|
@ -308,4 +308,4 @@ CISLO_IMG_DIR = os.path.join('cislo', 'img')
|
|||
|
||||
|
||||
# E-MAIL NOTIFICATIONS
|
||||
SEND_EMAIL_NOTIFICATIONS = False
|
||||
POSLI_MAILOVOU_NOTIFIKACI = False
|
||||
|
|
|
@ -66,4 +66,4 @@ LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/
|
|||
|
||||
|
||||
# E-MAIL NOTIFICATIONS
|
||||
SEND_EMAIL_NOTIFICATIONS = True
|
||||
POSLI_MAILOVOU_NOTIFIKACI = True
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 476 KiB |
|
@ -31,6 +31,10 @@
|
|||
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
|
||||
</script>
|
||||
|
||||
{# Případné skripty widgetů formulářů #}
|
||||
{% if form %}
|
||||
{{form.media}}
|
||||
{% endif %}
|
||||
{# script specifický pro stránku #}
|
||||
{% block script %}{% endblock %}
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db import models
|
||||
from django.forms import widgets
|
||||
|
||||
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
|
||||
from reversion.admin import VersionAdmin
|
||||
from django_reverse_admin import ReverseModelAdmin
|
||||
from solo.admin import SingletonModelAdmin
|
||||
|
||||
|
||||
# Todo: reversion
|
||||
|
||||
import seminar.models as m
|
||||
|
@ -14,8 +17,6 @@ admin.site.register(m.Skola)
|
|||
admin.site.register(m.Prijemce)
|
||||
admin.site.register(m.Rocnik)
|
||||
admin.site.register(m.Cislo)
|
||||
admin.site.register(m.Organizator)
|
||||
admin.site.register(m.Soustredeni)
|
||||
|
||||
@admin.register(m.Osoba)
|
||||
class OsobaAdmin(admin.ModelAdmin):
|
||||
|
@ -42,7 +43,14 @@ class OsobaAdmin(admin.ModelAdmin):
|
|||
org.save()
|
||||
udelej_orgem.short_description = "Udělej vybraných osob organizátory"
|
||||
|
||||
@admin.register(m.Organizator)
|
||||
class OrganizatorAdmin(admin.ModelAdmin):
|
||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'prezdivka']
|
||||
|
||||
@admin.register(m.Resitel)
|
||||
class ResitelAdmin(admin.ModelAdmin):
|
||||
search_fields = ['jmeno', 'prijmeni', 'prezdivka']
|
||||
ordering = ('osoba__jmeno','osoba__prijmeni')
|
||||
|
||||
@admin.register(m.Problem)
|
||||
class ProblemAdmin(PolymorphicParentModelAdmin):
|
||||
|
@ -74,15 +82,55 @@ class KonferaAdmin(PolymorphicChildModelAdmin):
|
|||
base_model = m.Konfera
|
||||
show_in_index = True
|
||||
|
||||
|
||||
class TextAdminInline(admin.TabularInline):
|
||||
model = m.Text
|
||||
formfield_overrides = {
|
||||
models.TextField: {'widget': widgets.TextInput}
|
||||
}
|
||||
exclude = ['text_zkraceny_set','text_zkraceny']
|
||||
|
||||
admin.site.register(m.Text)
|
||||
|
||||
class ResitelInline(admin.TabularInline):
|
||||
model = m.Resitel
|
||||
extra = 1
|
||||
admin.site.register(m.Resitel)
|
||||
|
||||
class SoustredeniUcastniciInline(admin.TabularInline):
|
||||
model = m.Soustredeni_Ucastnici
|
||||
extra = 1
|
||||
fields = ['resitel','poznamka']
|
||||
autocomplete_fields = ['resitel']
|
||||
ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni']
|
||||
formfield_overrides = {
|
||||
models.TextField: {'widget': widgets.TextInput}
|
||||
}
|
||||
|
||||
def get_queryset(self,request):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('resitel','soustredeni')
|
||||
|
||||
class SoustredeniOrganizatoriInline(admin.TabularInline):
|
||||
model = m.Soustredeni.organizatori.through
|
||||
extra = 1
|
||||
fields = ['organizator','poznamka']
|
||||
autocomplete_fields = ['organizator']
|
||||
ordering = ['organizator__osoba__jmeno','organizator__prijmeni']
|
||||
formfield_overrides = {
|
||||
models.TextField: {'widget': widgets.TextInput}
|
||||
}
|
||||
|
||||
def get_queryset(self,request):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('organizator', 'soustredeni')
|
||||
|
||||
|
||||
@admin.register(m.Soustredeni)
|
||||
class SoustredeniAdmin(admin.ModelAdmin):
|
||||
model = m.Soustredeni
|
||||
inline_type = 'tabular'
|
||||
inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline]
|
||||
|
||||
|
||||
class PrilohaReseniInline(admin.TabularInline):
|
||||
model = m.PrilohaReseni
|
||||
|
@ -92,6 +140,7 @@ admin.site.register(m.PrilohaReseni)
|
|||
class Reseni_ResiteleInline(admin.TabularInline):
|
||||
model = m.Reseni_Resitele
|
||||
|
||||
|
||||
@admin.register(m.Reseni)
|
||||
class ReseniAdmin(ReverseModelAdmin):
|
||||
base_model = m.Reseni
|
||||
|
@ -106,7 +155,6 @@ admin.site.register(m.Hodnoceni)
|
|||
admin.site.register(m.Pohadka)
|
||||
admin.site.register(m.Obrazek)
|
||||
|
||||
|
||||
# Polymorfismus pro stromy
|
||||
# TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ class ProfileEditForm(forms.Form):
|
|||
max_value=date.today().year+8,
|
||||
required=True)
|
||||
zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True)
|
||||
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=True)
|
||||
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=False)
|
||||
|
||||
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
|
||||
# def clean_username(self):
|
||||
|
|
|
@ -31,6 +31,8 @@ from unidecode import unidecode # Používám pro získání ID odkazu (ještě
|
|||
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
from django.core.mail import EmailMessage
|
||||
from seminar.utils import aktivniResitele
|
||||
|
||||
class SeminarModelBase(models.Model):
|
||||
|
||||
|
@ -624,9 +626,43 @@ class Cislo(SeminarModelBase):
|
|||
return None
|
||||
return c
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.__original_verejne = self.verejne_db
|
||||
|
||||
def posli_cislo_mailem(self):
|
||||
# parametry e-mailu
|
||||
odkaz = self.get_absolute_url()
|
||||
|
||||
poslat_z_mailu = 'zadani@mam.mff.cuni.cz'
|
||||
predmet = 'Vyšlo číslo {}'.format(self.kod())
|
||||
text_mailu = 'Ahoj,\n' \
|
||||
'na adrese {} najdete nejnovější číslo.\n' \
|
||||
'Vaše M&M\n'.format(odkaz)
|
||||
|
||||
# Prijemci e-mailu
|
||||
emaily = map(lambda r: r.osoba.email, filter(lambda r: r.zasilat_cislo_emailem, aktivniResitele(self)))
|
||||
|
||||
if not settings.POSLI_MAILOVOU_NOTIFIKACI:
|
||||
print("Poslal bych upozornění na tyto adresy: ", " ".join(emaily))
|
||||
return
|
||||
|
||||
email = EmailMessage(
|
||||
subject=predmet,
|
||||
body=text_mailu,
|
||||
from_email=poslat_z_mailu,
|
||||
bcc=list(emaily)
|
||||
#bcc = příjemci skryté kopie
|
||||
)
|
||||
|
||||
email.send()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
self.vygeneruj_nahled()
|
||||
# Při zveřejnění pošle mail
|
||||
if self.verejne_db and not self.__original_verejne:
|
||||
self.posli_cislo_mailem()
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.cislonode.save()
|
||||
|
|
BIN
seminar/static/images/no-picture.png
Normal file
BIN
seminar/static/images/no-picture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
|
@ -32,7 +32,7 @@
|
|||
{% if c.titulka_nahled %}
|
||||
<img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px>
|
||||
{% else %}
|
||||
<img src="" alt="no image" height=180px>
|
||||
{% load static %} <img src="{% static 'images/no-picture.png' %}" height=180px alt="no-picture">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
<h2><strong>Soustředění</strong></h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="/admin/seminar/soustredeni/add/">přidat soustředění</a></li>
|
||||
<li><strong>přednášky</strong>
|
||||
|
||||
<ul>
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
{% load staticfiles %}
|
||||
|
||||
{% block script %}
|
||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||
{{form.media}}
|
||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% block script %}
|
||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||
{{form.media}}
|
||||
<script src="{% static 'seminar/dynamic_formsets.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
{% load staticfiles %}
|
||||
|
||||
{% block script %}
|
||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||
{{form.media}}
|
||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
<script>
|
||||
function preddeadline() {
|
||||
alert("Řešení, která nám přijdou do tohoto deadlinu, se pokusíme opravit co nejdříve, abyste měli ještě šanci si je ještě opravit před definitivním deadlinem čísla.");
|
||||
}
|
||||
function sousdeadline() {
|
||||
alert("Body za řešení, která nám přijdou do tohoto deadlinu, se ještě započítají pro účast na připravovaném soustředění.");
|
||||
}
|
||||
</script>
|
||||
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load humanize %}
|
||||
|
@ -8,7 +17,17 @@
|
|||
|
||||
{% if nejblizsi_deadline %}
|
||||
<div class="odpocet">
|
||||
<p><b><big>Do konce <a href="/zadani/aktualni/">odeslání řešení</a> {% if typ_deadline == 'soustredeni' %}(pro účast na soustředění) {% elif typ_deadline == 'preddeadline' %}(pro otištění došlých řešení) {% endif %}zbývá:
|
||||
<p><b><big>Do
|
||||
{% if typ_deadline == 'soustredeni' %}
|
||||
<a href="" onClick="sousdeadline()"
|
||||
title="Body za řešení, která nám přijdou do tohoto deadlinu, se ještě započítají pro účast na připravovaném soustředění.">
|
||||
deadlinu</a> odeslání <a href="/zadani/aktualni/">řešení
|
||||
</a> pro účast na soustředění
|
||||
|
||||
{% elif typ_deadline == 'preddeadline' %} <a href="" onClick="preddeadline()"
|
||||
title="Řešení, která nám přijdou do tohoto deadlinu, se pokusíme opravit co nejdříve, abyste měli ještě šanci si je ještě opravit před definitivním deadlinem čísla.">1. deadlinu</a> aktuálního <a href="/zadani/aktualni/">čísla</a>
|
||||
{% else %} deadlinu aktuálního <a href="/zadani/aktualni/">čísla</a>
|
||||
{% endif %}zbývá:
|
||||
{{nejblizsi_deadline|timeuntil}}</big></b></p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -13,8 +13,8 @@ urlpatterns = [
|
|||
path('co-je-MaM/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'),
|
||||
|
||||
# Archiv
|
||||
path('archiv/rocniky/', views.ArchivView.as_view(), name="seninar_archiv_rocniky"),
|
||||
path('archiv/temata/', views.ArchivTemataView.as_view(), name="seninar_archiv_temata"),
|
||||
path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"),
|
||||
path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"),
|
||||
|
||||
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
|
||||
path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'),
|
||||
|
@ -63,7 +63,7 @@ urlpatterns = [
|
|||
path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'),
|
||||
|
||||
# Clanky
|
||||
path('clanky/resitel/', views.ClankyResitelView.as_view(), name='clanky_resitel'),
|
||||
path('archiv/clanky/', views.ClankyResitelView.as_view(), name='clanky_resitel'),
|
||||
#path('clanky/org/', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'),
|
||||
|
||||
# Aesop
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from dal import autocomplete
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db.models import Q
|
||||
|
||||
import seminar.models as m
|
||||
from .helpers import LoginRequiredAjaxMixin
|
||||
|
|
|
@ -568,6 +568,8 @@ class TitulniStranaView(generic.ListView):
|
|||
|
||||
try:
|
||||
nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0]
|
||||
if nejblizsi_deadline[0] == deadline_soustredeni[0]:
|
||||
nejblizsi_deadline = deadline_soustredeni
|
||||
except IndexError:
|
||||
nejblizsi_deadline = (None, None) # neni zadna aktualni deadline
|
||||
|
||||
|
|
Loading…
Reference in a new issue