Browse Source

Merge branch 'python3' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into python3

export_seznamu_prednasek
parent
commit
2c53ddcec8
  1. 17
      convert_spaces_to_tabs.sh
  2. 62
      galerie/admin.py
  3. 6
      galerie/forms.py
  4. 175
      galerie/models.py
  5. 10
      galerie/urls.py
  6. 403
      galerie/views.py
  7. 28
      korektury/admin.py
  8. 18
      korektury/forms.py
  9. 300
      korektury/models.py
  10. 6
      korektury/urls.py
  11. 354
      korektury/views.py
  12. 154
      prednasky/admin.py
  13. 2
      prednasky/forms.py
  14. 14
      prednasky/models.py
  15. 12
      prednasky/urls.py
  16. 132
      prednasky/views.py
  17. 970
      seminar/admin.py
  18. 2
      seminar/forms.py
  19. 9
      seminar/management/commands/testdata.py
  20. 2
      seminar/migrations/0023_add_novinky.py
  21. 2
      seminar/migrations/0049_auto_20190430_2354.py
  22. 12
      seminar/models.py
  23. 154
      seminar/tests.py
  24. 245
      seminar/testutils.py
  25. 122
      seminar/tools.py
  26. 182
      seminar/urls.py
  27. 116
      seminar/utils.py
  28. 1516
      seminar/views.py

17
convert_spaces_to_tabs.sh

@ -0,0 +1,17 @@
#!/bin/sh
if test "$#" -lt 1
then
echo "Usage: $0 file ..."
exit 2
fi
for file in "$@"
do
# Do the sed magic: keep replacing 4 spaces at the begining of line
sed -i -re '
: loop
s/^( *) /\1 /
t loop
' "$file"
done

62
galerie/admin.py

@ -10,45 +10,45 @@ from autocomplete_light import shortcuts as autocomplete_light
# akction # akction
def zverejnit_fotogalerii(modeladmin, request, queryset): def zverejnit_fotogalerii(modeladmin, request, queryset):
'''zverejni vybranou fotogalerii i jeji vsechny podgalerie''' '''zverejni vybranou fotogalerii i jeji vsechny podgalerie'''
for galerie in queryset: for galerie in queryset:
galerie.zobrazit = 0 galerie.zobrazit = 0
galerie.save() galerie.save()
zverejnit_fotogalerii(modeladmin, request, zverejnit_fotogalerii(modeladmin, request,
Galerie.objects.filter(galerie_up = galerie)) Galerie.objects.filter(galerie_up = galerie))
zverejnit_fotogalerii.short_description = 'Zveřejnit fotogalerie' zverejnit_fotogalerii.short_description = 'Zveřejnit fotogalerie'
def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset): def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset):
'''zneverjni vybranou fotogalerii i jeji vsechny podgalerie''' '''zneverjni vybranou fotogalerii i jeji vsechny podgalerie'''
for galerie in queryset: for galerie in queryset:
galerie.zobrazit = 1 galerie.zobrazit = 1
galerie.save() galerie.save()
prepnout_fotogalerii_do_org_rezimu(modeladmin, request, prepnout_fotogalerii_do_org_rezimu(modeladmin, request,
Galerie.objects.filter(galerie_up = galerie)) Galerie.objects.filter(galerie_up = galerie))
prepnout_fotogalerii_do_org_rezimu.short_description = \ prepnout_fotogalerii_do_org_rezimu.short_description = \
'Přepnout do režimu úprav (zneveřejní galerii)' 'Přepnout do režimu úprav (zneveřejní galerii)'
class GalerieInline(admin.TabularInline): class GalerieInline(admin.TabularInline):
model = Obrazek model = Obrazek
fields = ['obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag'] fields = ['obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag']
readonly_fields = ['nazev', 'obrazek_maly_tag'] readonly_fields = ['nazev', 'obrazek_maly_tag']
formfield_overrides = { formfield_overrides = {
models.TextField: {'widget': forms.TextInput}, models.TextField: {'widget': forms.TextInput},
} }
class ObrazekAdmin(admin.ModelAdmin): class ObrazekAdmin(admin.ModelAdmin):
list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag') list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag')
class GalerieAdmin(admin.ModelAdmin): class GalerieAdmin(admin.ModelAdmin):
form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek']) form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek'])
model = Galerie model = Galerie
fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi')
list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny') list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny')
inlines = [GalerieInline] inlines = [GalerieInline]
actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu]
save_on_top = True save_on_top = True
ordering = ['galerie_up__nazev', 'poradi'] ordering = ['galerie_up__nazev', 'poradi']
admin.site.register(Obrazek, ObrazekAdmin) admin.site.register(Obrazek, ObrazekAdmin)
admin.site.register(Galerie, GalerieAdmin) admin.site.register(Galerie, GalerieAdmin)

6
galerie/forms.py

@ -4,8 +4,8 @@ from django import forms
from seminar.models import Soustredeni from seminar.models import Soustredeni
class KomentarForm(forms.Form): class KomentarForm(forms.Form):
komentar = forms.CharField(label = "Komentář:", max_length = 300, required=False) komentar = forms.CharField(label = "Komentář:", max_length = 300, required=False)
class NewGalerieForm(forms.Form): class NewGalerieForm(forms.Form):
nazev = forms.CharField(label = "Název galerie", max_length = 100) nazev = forms.CharField(label = "Název galerie", max_length = 100)
#popis = forms.CharField(label = "Popis", required = False, max_length = 2000, widget = forms.Textarea) #popis = forms.CharField(label = "Popis", required = False, max_length = 2000, widget = forms.Textarea)

175
galerie/models.py

@ -14,109 +14,108 @@ VZDY=0
ORG=1 ORG=1
NIKDY=2 NIKDY=2
VIDITELNOST = ( VIDITELNOST = (
(VZDY, 'Vždy'), (VZDY, 'Vždy'),
(ORG, 'Organizátorům'), (ORG, 'Organizátorům'),
(NIKDY, 'Nikdy'), (NIKDY, 'Nikdy'),
) )
# tyhle funkce jsou tady jen kvůli starým migracím, které se na ně odkazují # tyhle funkce jsou tady jen kvůli starým migracím, které se na ně odkazují
# až se ty migrace někdy squashnou, tak by mělo být možné funkce smazat # až se ty migrace někdy squashnou, tak by mělo být možné funkce smazat
def obrazek_filename_maly(): def obrazek_filename_maly():
pass pass
def obrazek_filename_stredni(): def obrazek_filename_stredni():
pass pass
def obrazek_filename_velky(): def obrazek_filename_velky():
pass pass
def obrazek_filename(self, filename): def obrazek_filename(self, filename):
gal = self.galerie gal = self.galerie
cislo_gal = force_unicode(gal.pk) cislo_gal = force_unicode(gal.pk)
# najdi kořenovou galerii # najdi kořenovou galerii
while (gal.galerie_up): while (gal.galerie_up):
gal = gal.galerie_up gal = gal.galerie_up
# soustředění je v cestě jen pokud galerie pod nějaké patří # soustředění je v cestě jen pokud galerie pod nějaké patří
cesta = ( cesta = (
['Galerie'] + ['Galerie'] +
(["soustredeni_" + force_unicode(gal.soustredeni.pk)] if gal.soustredeni else []) + (["soustredeni_" + force_unicode(gal.soustredeni.pk)] if gal.soustredeni else []) +
["galerie_" + cislo_gal, force_unicode(self.nazev)] ["galerie_" + cislo_gal, force_unicode(self.nazev)]
) )
return os.path.join(*cesta) return os.path.join(*cesta)
class Obrazek(models.Model): class Obrazek(models.Model):
obrazek_velky = models.ImageField(upload_to=obrazek_filename, obrazek_velky = models.ImageField(upload_to=obrazek_filename,
help_text = "Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.") help_text = "Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.")
obrazek_stredni = ImageSpecField(source='obrazek_velky', obrazek_stredni = ImageSpecField(source='obrazek_velky',
processors=[Transpose(Transpose.AUTO), ResizeToFit(900, 675, upscale=False)], processors=[Transpose(Transpose.AUTO), ResizeToFit(900, 675, upscale=False)],
options={'quality': 95}) options={'quality': 95})
obrazek_maly = ImageSpecField(source='obrazek_velky', obrazek_maly = ImageSpecField(source='obrazek_velky',
processors=[Transpose(Transpose.AUTO), ResizeToFit(167, 167, upscale=False)], processors=[Transpose(Transpose.AUTO), ResizeToFit(167, 167, upscale=False)],
options={'quality': 95}) options={'quality': 95})
nazev = models.CharField('Název', max_length=50, blank=True, null=True) nazev = models.CharField('Název', max_length=50, blank=True, null=True)
popis = models.TextField('Popis', blank=True, null=True) popis = models.TextField('Popis', blank=True, null=True)
datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True) datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True)
galerie = models.ForeignKey('Galerie', blank=True, null=True) galerie = models.ForeignKey('Galerie', blank=True, null=True)
poradi = models.IntegerField('Pořadí', blank=True, null=True) poradi = models.IntegerField('Pořadí', blank=True, null=True)
def __unicode__(self): def __unicode__(self):
return unicode(self.obrazek_velky.name) return unicode(self.obrazek_velky.name)
class Meta: class Meta:
verbose_name = 'Obrázek' verbose_name = 'Obrázek'
verbose_name_plural = 'Obrázky' verbose_name_plural = 'Obrázky'
ordering = ['nazev'] ordering = ['nazev']
def obrazek_maly_tag(self): def obrazek_maly_tag(self):
return u'<img src="{}">'.format(self.obrazek_maly.url) return u'<img src="{}">'.format(self.obrazek_maly.url)
obrazek_maly_tag.short_description = "Náhled" obrazek_maly_tag.short_description = "Náhled"
obrazek_maly_tag.allow_tags = True obrazek_maly_tag.allow_tags = True
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# obrázek potřebuje název, protože se z něj generuje cesta pro jeho uložení # obrázek potřebuje název, protože se z něj generuje cesta pro jeho uložení
# (a pak se podle něj taky řadí) # (a pak se podle něj taky řadí)
if self.nazev is None: if self.nazev is None:
self.nazev = os.path.basename(self.obrazek_velky.name) self.nazev = os.path.basename(self.obrazek_velky.name)
super(Obrazek, self).save(*args, **kwargs) super(Obrazek, self).save(*args, **kwargs)
class Galerie(models.Model): class Galerie(models.Model):
nazev = models.CharField('Název', max_length=100) nazev = models.CharField('Název', max_length=100)
datum_vytvoreni = models.DateTimeField('Datum vytvoření', auto_now_add = True) datum_vytvoreni = models.DateTimeField('Datum vytvoření', auto_now_add = True)
datum_zmeny = models.DateTimeField('Datum poslední změny', auto_now = True) datum_zmeny = models.DateTimeField('Datum poslední změny', auto_now = True)
popis = models.TextField('Popis', blank = True, null = True) popis = models.TextField('Popis', blank = True, null = True)
titulni_obrazek = models.ForeignKey(Obrazek, blank = True, null = True, related_name = "+", on_delete = models.SET_NULL) titulni_obrazek = models.ForeignKey(Obrazek, blank = True, null = True, related_name = "+", on_delete = models.SET_NULL)
zobrazit = models.IntegerField('Zobrazit?', default = ORG, choices = VIDITELNOST) zobrazit = models.IntegerField('Zobrazit?', default = ORG, choices = VIDITELNOST)
galerie_up = models.ForeignKey('Galerie', blank = True, null = True) galerie_up = models.ForeignKey('Galerie', blank = True, null = True)
soustredeni = models.ForeignKey(Soustredeni, blank = True, null = True) soustredeni = models.ForeignKey(Soustredeni, blank = True, null = True)
poradi = models.IntegerField('Pořadí', blank = True, null = True) poradi = models.IntegerField('Pořadí', blank = True, null = True)
def __unicode__(self): def __unicode__(self):
return self.nazev return self.nazev
class Meta: class Meta:
verbose_name = 'Galerie' verbose_name = 'Galerie'
verbose_name_plural = 'Galerie' verbose_name_plural = 'Galerie'
#def link_na_preview(self): #def link_na_preview(self):
#"""Odkaz na galerii, používá se v admin rozhranní. """ #"""Odkaz na galerii, používá se v admin rozhranní. """
#return '<a href="/fotogalerie/galerie/%s/">Preview</a>' % self.id #return '<a href="/fotogalerie/galerie/%s/">Preview</a>' % self.id
#link_na_preview.allow_tags = True #link_na_preview.allow_tags = True
#link_na_preview.short_description = 'Zobrazit galerii' #link_na_preview.short_description = 'Zobrazit galerii'
# #
#def je_publikovano(self): #def je_publikovano(self):
#"""Vraci True, pokud je tato galerie publikovana. """ #"""Vraci True, pokud je tato galerie publikovana. """
#if self.zobrazit == VZDY: #if self.zobrazit == VZDY:
#return True #return True
#if self.zobrazit == PODLE_CLANKU: #if self.zobrazit == PODLE_CLANKU:
#for clanek in self.clanek_set.all(): #for clanek in self.clanek_set.all():
#if clanek.je_publikovano(): #if clanek.je_publikovano():
#return True #return True
#return False #return False
# #
#@staticmethod #@staticmethod
#def publikovane_galerie(): #def publikovane_galerie():
#"""Vraci galerie, ktere uz maji byt publikovane.""" #"""Vraci galerie, ktere uz maji byt publikovane."""
#clanky = Blog.models.Clanek.publikovane_clanky() #clanky = Blog.models.Clanek.publikovane_clanky()
#return Galerie.objects.filter(Q(zobrazit=VZDY) | (Q(clanek__in=clanky) & Q(zobrazit=PODLE_CLANKU))).distinct() #return Galerie.objects.filter(Q(zobrazit=VZDY) | (Q(clanek__in=clanky) & Q(zobrazit=PODLE_CLANKU))).distinct()

10
galerie/urls.py

@ -4,10 +4,10 @@ from django.conf.urls import include, url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^(?P<pk>\d+)/$', views.nahled), url(r'^(?P<pk>\d+)/$', views.nahled),
url(r'^(?P<pk>\d+)/(?P<fotka>\d+)/$', views.detail), url(r'^(?P<pk>\d+)/(?P<fotka>\d+)/$', views.detail),
url(r'^(?P<galerie>\d+)/new/$', views.new_galerie), url(r'^(?P<galerie>\d+)/new/$', views.new_galerie),
url(r'^(?P<galerie>\d+)/plus/(?P<subgalerie>\d+)/$', views.plus_galerie), url(r'^(?P<galerie>\d+)/plus/(?P<subgalerie>\d+)/$', views.plus_galerie),
url(r'^(?P<galerie>\d+)/minus/(?P<subgalerie>\d+)/$', views.minus_galerie), url(r'^(?P<galerie>\d+)/minus/(?P<subgalerie>\d+)/$', views.minus_galerie),
] ]

403
galerie/views.py

@ -12,222 +12,221 @@ from seminar.models import Soustredeni
from galerie.forms import KomentarForm, NewGalerieForm from galerie.forms import KomentarForm, NewGalerieForm
def zobrazit(galerie, request): def zobrazit(galerie, request):
preview = False preview = False
if galerie.zobrazit >= 1: if galerie.zobrazit >= 1:
if request.user.is_staff: if request.user.is_staff:
preview = True; preview = True;
else: else:
raise Http404 raise Http404
return preview return preview
def cesta_od_korene(g): def cesta_od_korene(g):
"""Vrátí seznam galerií od kořene ke g""" """Vrátí seznam galerií od kořene ke g"""
cesta = [] cesta = []
while g != None: while g != None:
cesta.append(g) cesta.append(g)
g = g.galerie_up g = g.galerie_up
return reversed(cesta) return reversed(cesta)
def nahled(request, pk, soustredeni): def nahled(request, pk, soustredeni):
"""Zobrazeni nahledu vsech fotek ve skupine.""" """Zobrazeni nahledu vsech fotek ve skupine."""
galerie = get_object_or_404(Galerie, pk=pk) galerie = get_object_or_404(Galerie, pk=pk)
podgalerie = Galerie.objects.filter(galerie_up = galerie).order_by('poradi') podgalerie = Galerie.objects.filter(galerie_up = galerie).order_by('poradi')
if not request.user.is_staff: if not request.user.is_staff:
podgalerie = podgalerie.filter(zobrazit__lt=1) podgalerie = podgalerie.filter(zobrazit__lt=1)
obrazky = Obrazek.objects.filter(galerie = galerie) obrazky = Obrazek.objects.filter(galerie = galerie)
preview = zobrazit(galerie, request) preview = zobrazit(galerie, request)
sourozenci = [] sourozenci = []
if galerie.galerie_up: if galerie.galerie_up:
sourozenci = galerie.galerie_up.galerie_set.all().order_by('poradi') sourozenci = galerie.galerie_up.galerie_set.all().order_by('poradi')
if not request.user.is_staff: if not request.user.is_staff:
sourozenci = sourozenci.filter(zobrazit__lt=1) sourozenci = sourozenci.filter(zobrazit__lt=1)
predchozi = None predchozi = None
nasledujici = None nasledujici = None
minuly = None minuly = None
for g in sourozenci: for g in sourozenci:
if g.pk == galerie.pk: if g.pk == galerie.pk:
predchozi = minuly predchozi = minuly
if minuly != None and minuly.pk == galerie.pk: if minuly != None and minuly.pk == galerie.pk:
nasledujici = g nasledujici = g
break break
minuly = g minuly = g
cesta = cesta_od_korene(galerie) cesta = cesta_od_korene(galerie)
return render(request, 'galerie/GalerieNahled.html', return render(request, 'galerie/GalerieNahled.html',
{'galerie' : galerie, {'galerie' : galerie,
'podgalerie' : podgalerie, 'podgalerie' : podgalerie,
'obrazky' : obrazky, 'obrazky' : obrazky,
'preview' : preview, 'preview' : preview,
'cesta': cesta, 'cesta': cesta,
'sourozenci': sourozenci, 'sourozenci': sourozenci,
'predchozi': predchozi, 'predchozi': predchozi,
'nasledujici': nasledujici, 'nasledujici': nasledujici,
}) })
def detail(request, pk, fotka, soustredeni): def detail(request, pk, fotka, soustredeni):
"""Zobrazeni nahledu fotky s id 'fotka'.""" """Zobrazeni nahledu fotky s id 'fotka'."""
MAX_VYSKA = 900 MAX_VYSKA = 900
MAX_SIRKA = 900 MAX_SIRKA = 900
MAX_VYSKA_MALA = 100 MAX_VYSKA_MALA = 100
MAX_SIRKA_MALA = 200 MAX_SIRKA_MALA = 200
NAHLEDU = 1 NAHLEDU = 1
galerie = get_object_or_404(Galerie, pk=pk) galerie = get_object_or_404(Galerie, pk=pk)
preview = zobrazit(galerie, request) preview = zobrazit(galerie, request)
obrazek = get_object_or_404(Obrazek, pk=fotka) obrazek = get_object_or_404(Obrazek, pk=fotka)
obrazky = galerie.obrazek_set.all() obrazky = galerie.obrazek_set.all()
# vytvoreni a obslouzeni formulare # vytvoreni a obslouzeni formulare
if request.method == 'POST': if request.method == 'POST':
form = KomentarForm(request.POST) form = KomentarForm(request.POST)
if form.is_valid(): if form.is_valid():
obrazek.popis = form.cleaned_data['komentar'] obrazek.popis = form.cleaned_data['komentar']
obrazek.save() obrazek.save()
else: else:
form = KomentarForm({'komentar': obrazek.popis}) form = KomentarForm({'komentar': obrazek.popis})
# Poradi aktualniho obrazku v galerii/stitku. # Poradi aktualniho obrazku v galerii/stitku.
for i in range(len(obrazky)): for i in range(len(obrazky)):
if obrazky[i] == obrazek: if obrazky[i] == obrazek:
znacka = i znacka = i
break break
else: else:
# Obrazek neni v galerii/stitku. # Obrazek neni v galerii/stitku.
raise Http404 raise Http404
# Nacteni okolnich obrazku a galerii
# Nacteni okolnich obrazku a galerii # TODO vyjmout zjisteni predchozich a nasledujicich galerii
# TODO vyjmout zjisteni predchozich a nasledujicich galerii # a udelat z toho funkci, ktera se pouzije u nahledu
# a udelat z toho funkci, ktera se pouzije u nahledu predchozi_galerie = None
predchozi_galerie = None nasledujici_galerie = None
nasledujici_galerie = None obrazky_dalsi = obrazky[znacka+1:znacka+NAHLEDU+1]
obrazky_dalsi = obrazky[znacka+1:znacka+NAHLEDU+1] if (znacka+1) > NAHLEDU:
if (znacka+1) > NAHLEDU: obrazky_predchozi = obrazky[znacka-NAHLEDU:znacka]
obrazky_predchozi = obrazky[znacka-NAHLEDU:znacka] else:
else: obrazky_predchozi = obrazky[0:znacka]
obrazky_predchozi = obrazky[0:znacka] if galerie.poradi > 1:
if galerie.poradi > 1: predchozi_galerie = Galerie.objects.\
predchozi_galerie = Galerie.objects.\ filter(galerie_up=galerie.galerie_up).\
filter(galerie_up=galerie.galerie_up).\ filter(poradi=(galerie.poradi-1))
filter(poradi=(galerie.poradi-1)) if predchozi_galerie:
if predchozi_galerie: predchozi_galerie = predchozi_galerie[0]
predchozi_galerie = predchozi_galerie[0] else:
else: predchozi_galerie = None
predchozi_galerie = None if (znacka+1) == len(obrazky):
if (znacka+1) == len(obrazky): nasledujici_galerie = Galerie.objects.\
nasledujici_galerie = Galerie.objects.\ filter(galerie_up=galerie.galerie_up).\
filter(galerie_up=galerie.galerie_up).\ filter(poradi=(galerie.poradi+1))
filter(poradi=(galerie.poradi+1)) if nasledujici_galerie:
if nasledujici_galerie: nasledujici_galerie = nasledujici_galerie[0]
nasledujici_galerie = nasledujici_galerie[0] else:
else: nasledujici_galerie = None
nasledujici_galerie = None
# Preskalovani obrazku do vybraneho prostoru.
# Preskalovani obrazku do vybraneho prostoru. vyska = obrazek.obrazek_stredni.height
vyska = obrazek.obrazek_stredni.height sirka = obrazek.obrazek_stredni.width
sirka = obrazek.obrazek_stredni.width if vyska > MAX_VYSKA:
if vyska > MAX_VYSKA: sirka = sirka * MAX_VYSKA / vyska
sirka = sirka * MAX_VYSKA / vyska vyska = MAX_VYSKA
vyska = MAX_VYSKA if sirka > MAX_SIRKA:
if sirka > MAX_SIRKA: vyska = vyska * MAX_SIRKA / sirka
vyska = vyska * MAX_SIRKA / sirka sirka = MAX_SIRKA
sirka = MAX_SIRKA
return render(request, 'galerie/Galerie.html',
return render(request, 'galerie/Galerie.html', {'galerie' : galerie,
{'galerie' : galerie, 'predchozi_galerie' : predchozi_galerie,
'predchozi_galerie' : predchozi_galerie, 'nasledujici_galerie' : nasledujici_galerie,
'nasledujici_galerie' : nasledujici_galerie, 'obrazek' : obrazek,
'obrazek' : obrazek, 'vyska' : vyska,
'vyska' : vyska, 'sirka' : sirka,
'sirka' : sirka, 'obrazky_predchozi' : obrazky_predchozi,
'obrazky_predchozi' : obrazky_predchozi, 'obrazky_dalsi' : obrazky_dalsi,
'obrazky_dalsi' : obrazky_dalsi, 'preview' : preview,
'preview' : preview, 'form' : form,
'form' : form, 'cesta': cesta_od_korene(galerie),
'cesta': cesta_od_korene(galerie), })
})
def new_galerie(request, galerie, soustredeni): def new_galerie(request, galerie, soustredeni):
# zjistime k jakemu soustredeni se vaze nove vytvarena galerie # zjistime k jakemu soustredeni se vaze nove vytvarena galerie
soustredeni = get_object_or_404(Soustredeni, pk = soustredeni) soustredeni = get_object_or_404(Soustredeni, pk = soustredeni)
# pokud je parametr galerie 0, pak jde o hlavni galerii # pokud je parametr galerie 0, pak jde o hlavni galerii
# kdyz je nejaky jiny, pak je pk galerie pod kterou tu dalsi vytvarim # kdyz je nejaky jiny, pak je pk galerie pod kterou tu dalsi vytvarim
if int(galerie) == 0: if int(galerie) == 0:
galerie_up = False galerie_up = False
galerie_text = "Hlavní fotogalerie soustředění" galerie_text = "Hlavní fotogalerie soustředění"
else: else:
galerie_up = get_object_or_404(Galerie, pk = int(galerie)) galerie_up = get_object_or_404(Galerie, pk = int(galerie))
galerie_text = "podgalerii ke galerii " + str(galerie_up) galerie_text = "podgalerii ke galerii " + str(galerie_up)
# obsluha formulare umoznujiciho multiple nahravani fotek # obsluha formulare umoznujiciho multiple nahravani fotek
if request.method == 'POST': if request.method == 'POST':
form = NewGalerieForm(request.POST, request.FILES) form = NewGalerieForm(request.POST, request.FILES)
if form.is_valid(): if form.is_valid():
# vytvoreni nove galerie # vytvoreni nove galerie
gal = Galerie() gal = Galerie()
gal.nazev = form.cleaned_data['nazev'] gal.nazev = form.cleaned_data['nazev']
#gal.popis = form.cleaned_data['popis'] # popis nepouzivame #gal.popis = form.cleaned_data['popis'] # popis nepouzivame
gal.zobrazit = 1 # galerie je v procesu vytvareni gal.zobrazit = 1 # galerie je v procesu vytvareni
''' pokud je to podgalerie pridej nadrazenou galerii ''' pokud je to podgalerie pridej nadrazenou galerii
a nadrazene soustredeni nechej volne, a nadrazene soustredeni nechej volne,
pokud je to hlavni galerie, tak nadrazena galerie neexistuje, pokud je to hlavni galerie, tak nadrazena galerie neexistuje,
ale v takovem pripade musi byt nadrazene soustredeni a ne jinak ''' ale v takovem pripade musi byt nadrazene soustredeni a ne jinak '''
if galerie_up: if galerie_up:
gal.galerie_up = galerie_up gal.galerie_up = galerie_up
else: else:
gal.soustredeni = soustredeni gal.soustredeni = soustredeni
if gal.galerie_up: if gal.galerie_up:
gal.poradi = int(len(gal.galerie_up.galerie_set.all())) + 1 gal.poradi = int(len(gal.galerie_up.galerie_set.all())) + 1
gal.save() gal.save()
# zpracovani obrazku v galerii # zpracovani obrazku v galerii
for obr in request.FILES.getlist('obr'): for obr in request.FILES.getlist('obr'):
o = Obrazek() o = Obrazek()
o.obrazek_velky = obr o.obrazek_velky = obr
o.nazev = str(obr) o.nazev = str(obr)
o.galerie = gal o.galerie = gal
o.save() o.save()
# presmerovani na prave vzniklou galerii # presmerovani na prave vzniklou galerii
return HttpResponseRedirect('../../' + str(gal.pk)) return HttpResponseRedirect('../../' + str(gal.pk))
else: else:
form = NewGalerieForm() form = NewGalerieForm()
return render(request, 'galerie/GalerieNew.html', return render(request, 'galerie/GalerieNew.html',
{ {
'form' : form, 'form' : form,
'soustredeni' : soustredeni, 'soustredeni' : soustredeni,
'galerie_text' : galerie_text, 'galerie_text' : galerie_text,
}) })
def plus_galerie(request, galerie, soustredeni, subgalerie): def plus_galerie(request, galerie, soustredeni, subgalerie):
galerie = get_object_or_404(Galerie, pk=subgalerie) galerie = get_object_or_404(Galerie, pk=subgalerie)
if galerie.poradi: if galerie.poradi:
galerie.poradi += 1 galerie.poradi += 1
else: else:
galerie.poradi = int(len(galerie.galerie_up.galerie_set.all())) galerie.poradi = int(len(galerie.galerie_up.galerie_set.all()))
galerie.save() galerie.save()
return HttpResponseRedirect('../../') return HttpResponseRedirect('../../')
def minus_galerie(request, galerie, soustredeni, subgalerie): def minus_galerie(request, galerie, soustredeni, subgalerie):
galerie = get_object_or_404(Galerie, pk=subgalerie) galerie = get_object_or_404(Galerie, pk=subgalerie)
if galerie.poradi: if galerie.poradi:
galerie.poradi -= 1 galerie.poradi -= 1
else: else:
galerie.poradi = 1 galerie.poradi = 1
galerie.save() galerie.save()
return HttpResponseRedirect('../../') return HttpResponseRedirect('../../')

28
korektury/admin.py

@ -6,21 +6,21 @@ from korektury.models import KorekturovanePDF
# Register your models here. # Register your models here.
class KorekturovanePDFAdmin(VersionAdmin): class KorekturovanePDFAdmin(VersionAdmin):
readonly_fields = ['cas', 'stran'] readonly_fields = ['cas', 'stran']
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
if obj: if obj:
return self.readonly_fields + ['pdf'] return self.readonly_fields + ['pdf']
return self.readonly_fields return self.readonly_fields
fieldsets = [ fieldsets = [
(None, (None,
{'fields': {'fields':
['pdf', 'cas', 'org', 'stran', 'nazev', 'komentar']}), ['pdf', 'cas', 'org', 'stran', 'nazev', 'komentar']}),
# (u'PDF', {'fields': ['pdf']}), # (u'PDF', {'fields': ['pdf']}),
] ]
list_display = ['nazev', 'cas', 'stran', 'org'] list_display = ['nazev', 'cas', 'stran', 'org']
list_filter = [] list_filter = []
search_fields = [] search_fields = []
admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin) admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin)

18
korektury/forms.py

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

300
korektury/models.py

@ -17,171 +17,171 @@ from unidecode import unidecode
def generate_filename(self, filename): def generate_filename(self, filename):
clean = get_valid_filename( clean = get_valid_filename(
unidecode( unidecode(
filename.replace('/', '-').replace('\0', '').replace(":", "_") filename.replace('/', '-').replace('\0', '').replace(":", "_")
) )
) )
fname = "%s_%s" % ( fname = "%s_%s" % (
timezone.now().strftime('%Y-%m-%d-%H_%M'), timezone.now().strftime('%Y-%m-%d-%H_%M'),
clean) clean)
return os.path.join(settings.KOREKTURY_PDF_DIR, fname) return os.path.join(settings.KOREKTURY_PDF_DIR, fname)
#@reversion.register(ignore_duplicates=True) #@reversion.register(ignore_duplicates=True)
#@python_2_unicode_compatible #@python_2_unicode_compatible
class KorekturovanePDF(models.Model): class KorekturovanePDF(models.Model):
class Meta: class Meta:
ordering = ['-cas'] ordering = ['-cas']
db_table = 'korekturovane_cislo' db_table = 'korekturovane_cislo'
verbose_name = u'PDF k opravám' verbose_name = u'PDF k opravám'
verbose_name_plural = u'PDF k opravám' verbose_name_plural = u'PDF k opravám'
#Interní ID #Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
cas = models.DateTimeField(u'čas vložení PDF',default=timezone.now,help_text = 'Čas vložení PDF') cas = models.DateTimeField(u'čas vložení PDF',default=timezone.now,help_text = 'Čas vložení PDF')
nazev = models.CharField(u'název PDF',blank = True,max_length=50, help_text='Název (např. 22.1 verze 4) korekturovaného PDF') nazev = models.CharField(u'název PDF',blank = True,max_length=50, help_text='Název (např. 22.1 verze 4) korekturovaného PDF')
komentar = models.TextField(u'komentář k PDF',blank = True, help_text='Komentář ke korekturovanému PDF (např. na co se zaměřit)') komentar = models.TextField(u'komentář k PDF',blank = True, help_text='Komentář ke korekturovanému PDF (např. na co se zaměřit)')
pdf = models.FileField(u'PDF', upload_to = generate_filename) pdf = models.FileField(u'PDF', upload_to = generate_filename)
org = models.ForeignKey(Organizator, blank=True, org = models.ForeignKey(Organizator, blank=True,
help_text='Zodpovědný organizátor za obsah', help_text='Zodpovědný organizátor za obsah',
null=True, null=True,
default=None) default=None)
stran = models.IntegerField(u'počet stran', help_text='Počet stran PDF', stran = models.IntegerField(u'počet stran', help_text='Počet stran PDF',
default=0) default=0)
STATUS_PRIDAVANI = 'pridavani' STATUS_PRIDAVANI = 'pridavani'
STATUS_ZANASENI = 'zanaseni' STATUS_ZANASENI = 'zanaseni'
STATUS_ZASTARALE = 'zastarale' STATUS_ZASTARALE = 'zastarale'
STATUS_CHOICES = ( STATUS_CHOICES = (
(STATUS_PRIDAVANI, u'Přidávání korektur'), (STATUS_PRIDAVANI, u'Přidávání korektur'),
(STATUS_ZANASENI, u'Korektury jsou zanášeny'), (STATUS_ZANASENI, u'Korektury jsou zanášeny'),
(STATUS_ZASTARALE, u'Stará verze, nekorigovat'), (STATUS_ZASTARALE, u'Stará verze, nekorigovat'),
) )
status = models.CharField(u'stav PDF',max_length=16, choices=STATUS_CHOICES, blank=False, status = models.CharField(u'stav PDF',max_length=16, choices=STATUS_CHOICES, blank=False,
default = STATUS_PRIDAVANI) default = STATUS_PRIDAVANI)
#TODO Nepovinný foreign key k číslu #TODO Nepovinný foreign key k číslu
def get_prefix(self): def get_prefix(self):
"""Vrať řetězec, ke kterému se připojí číslo a .png""" """Vrať řetězec, ke kterému se připojí číslo a .png"""
# vrátíme jméno souboru bez cesty # vrátíme jméno souboru bez cesty
return os.path.basename(self.pdf.file.name) return os.path.basename(self.pdf.file.name)
def convert(self): def convert(self):
"""Vytvoří jedno png za každou stranu pdf a uloží se""" """Vytvoří jedno png za každou stranu pdf a uloží se"""
dirname = os.path.join(settings.MEDIA_ROOT, settings.KOREKTURY_IMG_DIR) dirname = os.path.join(settings.MEDIA_ROOT, settings.KOREKTURY_IMG_DIR)
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.mkdir(dirname) os.mkdir(dirname)
self.stran = 0 self.stran = 0
while True: while True:
res = subprocess.call([ res = subprocess.call([
"convert", "convert",
"-density", "180x180", "-density", "180x180",
"-geometry", " 1024x1448", "-geometry", " 1024x1448",
"%s[%d]" % (self.pdf.path, self.stran), "%s[%d]" % (self.pdf.path, self.stran),
os.path.join( os.path.join(
dirname, dirname,
"%s-%d.png" % (self.get_prefix(), self.stran) "%s-%d.png" % (self.get_prefix(), self.stran)
) )
]) ])
if res == 1: if res == 1:
break break
self.stran += 1 self.stran += 1
# Změnil se počet stran, ukládáme # Změnil se počet stran, ukládáme
super(KorekturovanePDF, self).save() super(KorekturovanePDF, self).save()
def save(self): def save(self):
# Pokud se nezmenilo PDF, tak nepregenerovavej nahledy # Pokud se nezmenilo PDF, tak nepregenerovavej nahledy
try: try:
original = KorekturovanePDF.objects.get(pk=self.pk) original = KorekturovanePDF.objects.get(pk=self.pk)
if original.pdf == self.pdf: if original.pdf == self.pdf:
super(KorekturovanePDF, self).save() super(KorekturovanePDF, self).save()
return return
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
# uložíme nahrávané pdf # uložíme nahrávané pdf
super(KorekturovanePDF, self).save() super(KorekturovanePDF, self).save()
# uložíme png a změněný počet stran # uložíme png a změněný počet stran
self.convert() self.convert()
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
@python_2_unicode_compatible @python_2_unicode_compatible
class Oprava(models.Model): class Oprava(models.Model):
class Meta: class Meta:
db_table = 'opravy' db_table = 'opravy'
verbose_name = u'Oprava' verbose_name = u'Oprava'
verbose_name_plural = u'Opravy' verbose_name_plural = u'Opravy'
ordering = ['y','x'] ordering = ['y','x']
#Interní ID #Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
pdf = models.ForeignKey(KorekturovanePDF, default=-1) pdf = models.ForeignKey(KorekturovanePDF, default=-1)
strana = models.IntegerField(u'strana s opravou', help_text='Strana s opravou (od 0)') strana = models.IntegerField(u'strana s opravou', help_text='Strana s opravou (od 0)')
x = models.IntegerField(u'x-ová souřadnice bugu') x = models.IntegerField(u'x-ová souřadnice bugu')
y = models.IntegerField(u'y-ová souřadnice bugu') y = models.IntegerField(u'y-ová souřadnice bugu')
STATUS_K_OPRAVE = 'k_oprave' STATUS_K_OPRAVE = 'k_oprave'
STATUS_OPRAVENO = 'opraveno' STATUS_OPRAVENO = 'opraveno'
STATUS_NENI_CHYBA = 'neni_chyba' STATUS_NENI_CHYBA = 'neni_chyba'
STATUS_K_ZANESENI = 'k_zaneseni' STATUS_K_ZANESENI = 'k_zaneseni'
STATUS_CHOICES = ( STATUS_CHOICES = (
(STATUS_K_OPRAVE, u'K opravě'), (STATUS_K_OPRAVE, u'K opravě'),
(STATUS_OPRAVENO, u'Opraveno'), (STATUS_OPRAVENO, u'Opraveno'),
(STATUS_NENI_CHYBA, u'Není chyba'), (STATUS_NENI_CHYBA, u'Není chyba'),
(STATUS_K_ZANESENI, u'K zanesení do TeXu'), (STATUS_K_ZANESENI, u'K zanesení do TeXu'),
) )
status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False, status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False,
default = STATUS_K_OPRAVE) default = STATUS_K_OPRAVE)
autor = models.ForeignKey(Organizator, blank = True, autor = models.ForeignKey(Organizator, blank = True,
help_text='Autor opravy', help_text='Autor opravy',
null = True) null = True)
text = models.TextField(u'text opravy',blank = True, help_text='Text opravy') text = models.TextField(u'text opravy',blank = True, help_text='Text opravy')
# def __init__(self,dictionary): # def __init__(self,dictionary):
# for k,v in dictionary.items(): # for k,v in dictionary.items():
# setattr(self,k,v) # setattr(self,k,v)
def __str__(self): def __str__(self):
return force_unicode(u'%s od %s: %s'%(self.status,self.autor,self.text)) return force_unicode(u'%s od %s: %s'%(self.status,self.autor,self.text))
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
@python_2_unicode_compatible @python_2_unicode_compatible
class Komentar(models.Model): class Komentar(models.Model):
class Meta: class Meta:
db_table = 'komentare' db_table = 'komentare'
verbose_name = u'Komentář k opravě' verbose_name = u'Komentář k opravě'
verbose_name_plural = u'Komentáře k opravě' verbose_name_plural = u'Komentáře k opravě'
ordering = ['cas'] ordering = ['cas']
#Interní ID #Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
cas = models.DateTimeField(u'čas komentáře',default=timezone.now,help_text = 'Čas zadání komentáře') cas = models.DateTimeField(u'čas komentáře',default=timezone.now,help_text = 'Čas zadání komentáře')
oprava = models.ForeignKey(Oprava) oprava = models.ForeignKey(Oprava)
autor = models.ForeignKey(Organizator, blank = True, autor = models.ForeignKey(Organizator, blank = True,
help_text = u'Autor komentáře', help_text = u'Autor komentáře',
null = True) null = True)
text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře') text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře')
def __str__(self): def __str__(self):
return force_unicode(u'%s od %s: %s'%(self.cas,self.autor,self.text)) return force_unicode(u'%s od %s: %s'%(self.cas,self.autor,self.text))

6
korektury/urls.py

@ -6,7 +6,7 @@ from . import views
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
urlpatterns = [ urlpatterns = [
url(r'^korektury/$', staff_member_required(views.KorekturyListView.as_view()), name='korektury-list'), url(r'^korektury/$', staff_member_required(views.KorekturyListView.as_view()), name='korektury-list'),
url(r'^korektury/(?P<pdf>\d+)/$', staff_member_required(views.KorekturyView.as_view()), name='korektury'), url(r'^korektury/(?P<pdf>\d+)/$', staff_member_required(views.KorekturyView.as_view()), name='korektury'),
url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'), url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'),
] ]

354
korektury/views.py

@ -15,186 +15,186 @@ import os
import unicodedata import unicodedata
class KorekturyHelpView(generic.TemplateView): class KorekturyHelpView(generic.TemplateView):
template_name = 'korektury/help.html' template_name = 'korektury/help.html'
class KorekturyListView(generic.ListView): class KorekturyListView(generic.ListView):
model = KorekturovanePDF model = KorekturovanePDF
template_name = 'korektury/seznam.html' template_name = 'korektury/seznam.html'
### Korektury ### Korektury
class KorekturyView(generic.TemplateView): class KorekturyView(generic.TemplateView):
model = Oprava model = Oprava
template_name = 'korektury/opraf.html' template_name = 'korektury/opraf.html'
form_class = OpravaForm form_class = OpravaForm
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
form = self.form_class(request.POST) form = self.form_class(request.POST)
q = request.POST q = request.POST
scroll = q.get('scroll') scroll = q.get('scroll')
# prirazeni autora podle prihlaseni # prirazeni autora podle prihlaseni
autor_user = request.user autor_user = request.user
# pokud existuje ucet (user), ale neni to organizator = 403 # pokud existuje ucet (user), ale neni to organizator = 403
autor = Organizator.objects.filter(user=autor_user).first() autor = Organizator.objects.filter(user=autor_user).first()
if not autor: if not autor:
return HttpResponseForbidden() return HttpResponseForbidden()
if not scroll: if not scroll:
scroll = 0 scroll = 0
action = q.get('action') action = q.get('action')
if (action == ''): # Přidej if (action == ''): # Přidej
x = int(q.get('x')) x = int(q.get('x'))
y = int(q.get('y')) y = int(q.get('y'))
text = q.get('txt') text = q.get('txt')
strana = int(q.get('img-id')[4:]) strana = int(q.get('img-id')[4:])
pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) pdf = KorekturovanePDF.objects.get(id=q.get('pdf'))
op = Oprava(x=x,y=y, autor=autor, text=text, strana=strana,pdf = pdf) op = Oprava(x=x,y=y, autor=autor, text=text, strana=strana,pdf = pdf)
op.save() op.save()
self.send_email_notification_komentar(op, autor, text) self.send_email_notification_komentar(op, autor, text)
elif (action == 'del'): elif (action == 'del'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
op.delete() op.delete()
elif (action == 'update'): elif (action == 'update'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
text = q.get('txt') text = q.get('txt')
op.autor = autor op.autor = autor
op.text = text op.text = text
op.save() op.save()
elif (action == 'undone'): elif (action == 'undone'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
op.status = op.STATUS_K_OPRAVE op.status = op.STATUS_K_OPRAVE
op.save() op.save()
elif (action == 'done'): elif (action == 'done'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
op.status = op.STATUS_OPRAVENO op.status = op.STATUS_OPRAVENO
op.save() op.save()
elif (action == 'ready'): elif (action == 'ready'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
op.status = op.STATUS_K_ZANESENI op.status = op.STATUS_K_ZANESENI
op.save() op.save()
elif (action == 'wontfix'): elif (action == 'wontfix'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
op.status = op.STATUS_NENI_CHYBA op.status = op.STATUS_NENI_CHYBA
op.save() op.save()
elif (action == 'comment'): elif (action == 'comment'):
id = int(q.get('id')) id = int(q.get('id'))
op = Oprava.objects.get(id=id) op = Oprava.objects.get(id=id)
text = q.get('txt') text = q.get('txt')
kom = Komentar(oprava=op,autor=autor,text=text) kom = Komentar(oprava=op,autor=autor,text=text)
kom.save() kom.save()
self.send_email_notification_komentar(op, autor, text) self.send_email_notification_komentar(op, autor, text)
elif (action == 'update-comment'): elif (action == 'update-comment'):
id = int(q.get('id')) id = int(q.get('id'))
kom = Komentar.objects.get(id=id) kom = Komentar.objects.get(id=id)
text = q.get('txt') text = q.get('txt')
kom.text = text kom.text = text
kom.autor = autor kom.autor = autor
kom.save() kom.save()
elif (action == 'del-comment'): elif (action == 'del-comment'):
id = int(q.get('id')) id = int(q.get('id'))
kom = Komentar.objects.get(id=id) kom = Komentar.objects.get(id=id)
kom.delete() kom.delete()
elif (action == 'set-state'): elif (action == 'set-state'):
pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) pdf = KorekturovanePDF.objects.get(id=q.get('pdf'))
if (q.get('state') == 'adding'): if (q.get('state') == 'adding'):
pdf.status = pdf.STATUS_PRIDAVANI pdf.status = pdf.STATUS_PRIDAVANI
elif (q.get('state') == 'comitting'): elif (q.get('state') == 'comitting'):
pdf.status = pdf.STATUS_ZANASENI pdf.status = pdf.STATUS_ZANASENI
elif (q.get('state') == 'deprecated'): elif (q.get('state') == 'deprecated'):
pdf.status = pdf.STATUS_ZASTARALE pdf.status = pdf.STATUS_ZASTARALE
pdf.save() pdf.save()
context = self.get_context_data() context = self.get_context_data()
context['scroll'] = scroll context['scroll'] = scroll
context['autor'] = autor context['autor'] = autor
return render(request, 'korektury/opraf.html',context) return render(request, 'korektury/opraf.html',context)
def send_email_notification_komentar(self, oprava, autor, text): def send_email_notification_komentar(self, oprava, autor, text):
''' Rozesle e-mail pri pridani komentare, ''' Rozesle e-mail pri pridani komentare,
ktery obsahuje text komentare. ktery obsahuje text komentare.
''' '''
# parametry e-mailu # parametry e-mailu
odkaz = "https://mam.mff.cuni.cz/korektury/{}/".format(oprava.pdf.pk) odkaz = "https://mam.mff.cuni.cz/korektury/{}/".format(oprava.pdf.pk)
from_email = 'korekturovatko@mam.mff.cuni.cz' from_email = 'korekturovatko@mam.mff.cuni.cz'
subject = 'Nová korektura od {} v {}'.format(autor, subject = 'Nová korektura od {} v {}'.format(autor,
oprava.pdf.nazev) oprava.pdf.nazev)
text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\ text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\
\nodkaz do korekturovátka: {}\n\ \nodkaz do korekturovátka: {}\n\
\nVaše korekturovátko\n".format(text, odkaz) \nVaše korekturovátko\n".format(text, odkaz)
# Prijemci e-mailu # Prijemci e-mailu
emails = set() emails = set()
# e-mail autora korektury # e-mail autora korektury
email = oprava.autor.user.email email = oprava.autor.user.email
if email: if email:
emails.add(email) emails.add(email)
# nalezeni e-mailu na autory komentaru # nalezeni e-mailu na autory komentaru
for komentar in oprava.komentar_set.all(): for komentar in oprava.komentar_set.all():
email_komentujiciho = komentar.autor.user.email email_komentujiciho = komentar.autor.user.email
if email_komentujiciho: if email_komentujiciho:
emails.add(email_komentujiciho) emails.add(email_komentujiciho)
# zodpovedny org # zodpovedny org
if oprava.pdf.org: if oprava.pdf.org:
email_zobpovedny = oprava.pdf.org.user.email email_zobpovedny = oprava.pdf.org.user.email
if email_zobpovedny: if email_zobpovedny:
emails.add(email_zobpovedny) emails.add(email_zobpovedny)
# odstran e-mail autora opravy # odstran e-mail autora opravy
email = autor.user.email email = autor.user.email
if email: if email:
emails.discard(email) emails.discard(email)
if not settings.SEND_EMAIL_NOTIFICATIONS: if not settings.SEND_EMAIL_NOTIFICATIONS:
print("Poslal bych upozornění na tyto adresy: ", " ".join(emails)) print("Poslal bych upozornění na tyto adresy: ", " ".join(emails))
return return
send_mail(subject, text, from_email, list(emails)) send_mail(subject, text, from_email, list(emails))
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(KorekturyView,self).get_context_data(**kwargs) context = super(KorekturyView,self).get_context_data(**kwargs)
pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf']) pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf'])
context['pdf'] = pdf context['pdf'] = pdf
context['img_prefix'] = pdf.get_prefix() context['img_prefix'] = pdf.get_prefix()
context['img_path'] = settings.KOREKTURY_IMG_DIR context['img_path'] = settings.KOREKTURY_IMG_DIR
context['img_indexes'] = range(pdf.stran) context['img_indexes'] = range(pdf.stran)
context['form_oprava'] = OpravaForm() context['form_oprava'] = OpravaForm()
opravy = Oprava.objects.filter(pdf=self.kwargs['pdf']) opravy = Oprava.objects.filter(pdf=self.kwargs['pdf'])
zasluhy = {} zasluhy = {}
for o in opravy: for o in opravy:
if o.autor in zasluhy: if o.autor in zasluhy:
zasluhy[o.autor]+=1 zasluhy[o.autor]+=1
else: else:
zasluhy[o.autor]=1 zasluhy[o.autor]=1
o.komentare = o.komentar_set.all() o.komentare = o.komentar_set.all()
for k in o.komentare: for k in o.komentare:
if k.autor in zasluhy: if k.autor in zasluhy:
zasluhy[k.autor] += 1 zasluhy[k.autor] += 1
else: else:
zasluhy[k.autor] = 1 zasluhy[k.autor] = 1
zasluhy = [ zasluhy = [
{'autor': jmeno, 'pocet': pocet} {'autor': jmeno, 'pocet': pocet}
for (jmeno, pocet) in zasluhy.items() for (jmeno, pocet) in zasluhy.items()
] ]
zasluhy.sort(key=lambda z: z['pocet'], reverse=True) zasluhy.sort(key=lambda z: z['pocet'], reverse=True)
strany = set(o.strana for o in opravy) strany = set(o.strana for o in opravy)
opravy_na_stranu = [{'strana': s, 'op_id': opravy.filter(strana=s)} for s in strany] opravy_na_stranu = [{'strana': s, 'op_id': opravy.filter(strana=s)} for s in strany]
context['opravy_strany'] = opravy_na_stranu context['opravy_strany'] = opravy_na_stranu
context['opravy'] = opravy context['opravy'] = opravy
context['zasluhy'] = zasluhy context['zasluhy'] = zasluhy
return context return context
def form_valid(self,form): def form_valid(self,form):
return super(KorekturyView,self).form_valid(form) return super(KorekturyView,self).form_valid(form)

154
prednasky/admin.py

@ -10,91 +10,91 @@ from seminar.models import Soustredeni
class Seznam_PrednaskaInline(admin.TabularInline): class Seznam_PrednaskaInline(admin.TabularInline):
model = Prednaska.seznamy.through model = Prednaska.seznamy.through
extra = 0 extra = 0
def prednaska__nazev(self, obj): def prednaska__nazev(self, obj):
return mark_safe( return mark_safe(
u"<a href='/admin/prednasky/prednaska/{}'>{}</a>".format( u"<a href='/admin/prednasky/prednaska/{}'>{}</a>".format(
obj.prednaska.id, obj.prednaska.id,
escape(obj.prednaska.nazev) escape(obj.prednaska.nazev)
) )
) )
def prednaska__popis(self, obj): def prednaska__popis(self, obj):
return mark_safe( return mark_safe(
u"<div style='width: 200px'>{}</div>".format( u"<div style='width: 200px'>{}</div>".format(
escape(obj.prednaska.popis) escape(obj.prednaska.popis)
) )
) )
def prednaska__anotace(self, obj): def prednaska__anotace(self, obj):
return obj.prednaska.anotace return obj.prednaska.anotace
def prednaska__org(self, obj): def prednaska__org(self, obj):
return obj.prednaska.org return obj.prednaska.org
def prednaska__obor(self, obj): def prednaska__obor(self, obj):
return obj.prednaska.obor return obj.prednaska.obor
prednaska__nazev.short_description = u'Přednáška' prednaska__nazev.short_description = u'Přednáška'
prednaska__popis.short_description = u'Popis pro orgy' prednaska__popis.short_description = u'Popis pro orgy'
prednaska__anotace.short_description = u'Anotace' prednaska__anotace.short_description = u'Anotace'
prednaska__org.short_description = u'Org' prednaska__org.short_description = u'Org'
prednaska__obor.short_description = u'Obor' prednaska__obor.short_description = u'Obor'
readonly_fields = [ readonly_fields = [
'prednaska__nazev', 'prednaska__nazev',
'prednaska__obor', 'prednaska__obor',
'prednaska__org', 'prednaska__org',
'prednaska__popis', 'prednaska__popis',
'prednaska__anotace', 'prednaska__anotace',
] ]
exclude = ['prednaska'] exclude = ['prednaska']
def has_add_permission(self, req): return False def has_add_permission(self, req): return False
class SeznamAdmin(VersionAdmin): class SeznamAdmin(VersionAdmin):
list_display = ['soustredeni', 'stav'] list_display = ['soustredeni', 'stav']
inlines = [Seznam_PrednaskaInline] inlines = [Seznam_PrednaskaInline]
admin.site.register(Seznam, SeznamAdmin) admin.site.register(Seznam, SeznamAdmin)
class PrednaskaAdmin(VersionAdmin): class PrednaskaAdmin(VersionAdmin):
list_display = ['nazev', 'org', 'obor'] list_display = ['nazev', 'org', 'obor']
list_filter = ['org', 'obor'] list_filter = ['org', 'obor']
search_fields = [] search_fields = []
filter_horizontal = ('seznamy', ) filter_horizontal = ('seznamy', )
actions = ['move_to_soustredeni'] actions = ['move_to_soustredeni']
def move_to_soustredeni(self, request, queryset): def move_to_soustredeni(self, request, queryset):
sous = Soustredeni.objects.first() sous = Soustredeni.objects.first()
seznam = Seznam.objects.filter(soustredeni=sous, stav=STAV_NAVRH) seznam = Seznam.objects.filter(soustredeni=sous, stav=STAV_NAVRH)
if len(seznam) == 0: if len(seznam) == 0:
self.message_user( self.message_user(
request, request,
u"Není definován seznam pro aktuální soustředění, " u"Není definován seznam pro aktuální soustředění, "
u"nic se neprovedlo", u"nic se neprovedlo",
messages.ERROR messages.ERROR
) )
return return
seznam = seznam[0] seznam = seznam[0]
for prednaska in queryset: for prednaska in queryset:
prednaska.seznamy.add(seznam) prednaska.seznamy.add(seznam)
prednaska.save() prednaska.save()
self.message_user( self.message_user(
request, request,
u"Vybrané přednášky ({}) přidány jako návrhy na nejbližší " u"Vybrané přednášky ({}) přidány jako návrhy na nejbližší "
u"soustředění".format(len(queryset)) u"soustředění".format(len(queryset))
) )
move_to_soustredeni.short_description = ( move_to_soustredeni.short_description = (
u"Přidat přednášky do návrhu na nejbližší soustředění" u"Přidat přednášky do návrhu na nejbližší soustředění"
) )
admin.site.register(Prednaska, PrednaskaAdmin) admin.site.register(Prednaska, PrednaskaAdmin)

2
prednasky/forms.py

@ -2,7 +2,7 @@
from django import forms from django import forms
class NewPrednaskyForm(forms.Form): class NewPrednaskyForm(forms.Form):
ucastnik = forms.CharField(label = 'Tvoje jméno', max_length = 100) ucastnik = forms.CharField(label = 'Tvoje jméno', max_length = 100)

14
prednasky/models.py

@ -29,15 +29,15 @@ class Seznam(models.Model):
stav = models.IntegerField('Stav',choices=STAV_CHOICES,default = STAV_NAVRH) stav = models.IntegerField('Stav',choices=STAV_CHOICES,default = STAV_NAVRH)
def __str__(self): def __str__(self):
return force_unicode("Seznam {}přednášek na {}".format("návrhů " return force_unicode("Seznam {}přednášek na {}".format("návrhů "
if self.stav == STAV_NAVRH else "", self.soustredeni)) if self.stav == STAV_NAVRH else "", self.soustredeni))
CHOICES_OBTIZNOST = ( CHOICES_OBTIZNOST = (
(1, 'Lehká'), (1, 'Lehká'),
(2, 'Střední'), (2, 'Střední'),
(3, 'Těžká'), (3, 'Těžká'),
) )
CHOICES_BODY = ( CHOICES_BODY = (
(-1, '-1'), (-1, '-1'),
@ -82,5 +82,5 @@ class Hlasovani(models.Model):
def __str__(self): def __str__(self):
return force_unicode("{} dal {} bodů {} v seznamu {}".format(self.ucastnik, return force_unicode("{} dal {} bodů {} v seznamu {}".format(self.ucastnik,
self.body, self.prednaska, self.seznam)) self.body, self.prednaska, self.seznam))

12
prednasky/urls.py

@ -6,10 +6,10 @@ from . import views
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
urlpatterns = [ urlpatterns = [
url(r'^prednasky/$', views.newPrednaska), url(r'^prednasky/$', views.newPrednaska),
url(r'^prednasky/hotovo$', views.Prednaska_hotovo), url(r'^prednasky/hotovo$', views.Prednaska_hotovo),
url(r'^prednasky/metaseznam_prednasek$', staff_member_required(views.MetaSeznamListView.as_view()), name='metaseznam-list'), url(r'^prednasky/metaseznam_prednasek$', staff_member_required(views.MetaSeznamListView.as_view()), name='metaseznam-list'),
url(r'^prednasky/seznam_prednasek/(?P<seznam>\d+)/$', staff_member_required(views.SeznamListView.as_view()), name='seznam-list'), url(r'^prednasky/seznam_prednasek/(?P<seznam>\d+)/$', staff_member_required(views.SeznamListView.as_view()), name='seznam-list'),
url(r'^prednasky/seznam_prednasek/(?P<seznam>\d+)/export$', staff_member_required(views.SeznamExportView), name='seznam-export'), url(r'^prednasky/seznam_prednasek/(?P<seznam>\d+)/export$', staff_member_required(views.SeznamExportView), name='seznam-export'),
# url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'), # url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'),
] ]

132
prednasky/views.py

@ -10,83 +10,83 @@ from seminar.models import Soustredeni
from prednasky.forms import NewPrednaskyForm from prednasky.forms import NewPrednaskyForm
def newPrednaska(request): def newPrednaska(request):
# hlasovani se vztahuje k nejnovejsimu soustredeni # hlasovani se vztahuje k nejnovejsimu soustredeni
sous = Soustredeni.objects.first() sous = Soustredeni.objects.first()
seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first() seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first()
print(seznam) print(seznam)
# obsluha formulare # obsluha formulare
if request.method == 'POST': if request.method == 'POST':
form = NewPrednaskyForm(request.POST, request.FILES) form = NewPrednaskyForm(request.POST, request.FILES)
if form.is_valid(): if form.is_valid():
jmeno = form.cleaned_data['ucastnik'] jmeno = form.cleaned_data['ucastnik']
for i in request.POST: for i in request.POST:
if i[0] == 'q': if i[0] == 'q':
hlasovani = Hlasovani() hlasovani = Hlasovani()
print("q:"+i[1:]) print("q:"+i[1:])
hlasovani.prednaska = Prednaska.objects.filter(pk = int(i[1:]))[0] hlasovani.prednaska = Prednaska.objects.filter(pk = int(i[1:]))[0]
hlasovani.body = int(request.POST[i]) hlasovani.body = int(request.POST[i])
hlasovani.ucastnik = jmeno hlasovani.ucastnik = jmeno
hlasovani.seznam = seznam hlasovani.seznam = seznam
hlasovani.save() hlasovani.save()
# presmerovani na prave vzniklou galerii # presmerovani na prave vzniklou galerii
return HttpResponseRedirect('./hotovo') return HttpResponseRedirect('./hotovo')
else: else:
form = NewPrednaskyForm() form = NewPrednaskyForm()
return render( return render(
request, request,
'prednasky/base.html', 'prednasky/base.html',
{'form': form, 'prednasky': seznam} {'form': form, 'prednasky': seznam}
) )
def Prednaska_hotovo(request): def Prednaska_hotovo(request):
return render(request, 'prednasky/hotovo.html') return render(request, 'prednasky/hotovo.html')
class MetaSeznamListView(generic.ListView): class MetaSeznamListView(generic.ListView):
model = Seznam model = Seznam
template_name = 'prednasky/metaseznam_prednasek.html' template_name = 'prednasky/metaseznam_prednasek.html'
class SeznamListView(generic.ListView): class SeznamListView(generic.ListView):
template_name = 'prednasky/seznam_prednasek.html' template_name = 'prednasky/seznam_prednasek.html'
def get_queryset(self): def get_queryset(self):
self.seznam = get_object_or_404(Seznam, id=self.kwargs["seznam"]) self.seznam = get_object_or_404(Seznam, id=self.kwargs["seznam"])
prednasky = Prednaska.objects.filter(seznamy=self.seznam).order_by( prednasky = Prednaska.objects.filter(seznamy=self.seznam).order_by(
'org__user__first_name', 'org__user__last_name' 'org__user__first_name', 'org__user__last_name'
).annotate(body=Sum('hlasovani__body')) ).annotate(body=Sum('hlasovani__body'))
return prednasky return prednasky
def SeznamExportView(request, seznam): def SeznamExportView(request, seznam):
u"""Vypíše výsledky hlasování ve formátu pro prologovský optimalizátor""" u"""Vypíše výsledky hlasování ve formátu pro prologovský optimalizátor"""
# TODO zřejmě se nepoužívá, časem vyřadit? nahradit tabulkou vhodnější pro # TODO zřejmě se nepoužívá, časem vyřadit? nahradit tabulkou vhodnější pro
# lidi? # lidi?
hlasovani = Hlasovani.objects.filter(seznam=seznam) hlasovani = Hlasovani.objects.filter(seznam=seznam)
prednasky = Prednaska.objects.filter(seznamy=seznam) prednasky = Prednaska.objects.filter(seznamy=seznam)
orgove = set(p.org for p in prednasky) orgove = set(p.org for p in prednasky)
ucastnici = set(h.ucastnik for h in hlasovani) ucastnici = set(h.ucastnik for h in hlasovani)
for p in prednasky: for p in prednasky:
p.body = [] p.body = []
for u in ucastnici: for u in ucastnici:
try: try:
p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body) p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body)
except ObjectDoesNotExist: except ObjectDoesNotExist:
# účastník nehlasoval # účastník nehlasoval
p.body.append("?") p.body.append("?")
for h in hlasovani: for h in hlasovani:
h.ucastnik = hash(h.ucastnik) h.ucastnik = hash(h.ucastnik)
return render( return render(
request, request,
'prednasky/seznam_prednasek_export.txt', 'prednasky/seznam_prednasek_export.txt',
{"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove}, {"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove},
content_type="text/plain" content_type="text/plain"
) )

970
seminar/admin.py

File diff suppressed because it is too large

2
seminar/forms.py

@ -1,6 +1,6 @@
from django import forms from django import forms
class NameForm(forms.Form): class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100) your_name = forms.CharField(label='Your name', max_length=100)

9
seminar/management/commands/testdata.py

@ -17,7 +17,7 @@ User = django.contrib.auth.get_user_model()
class Command(BaseCommand): class Command(BaseCommand):
help = "Clear database and load testing data." help = "Clear database and load testing data."
def handle(self, **options): def handle(self, *args, **options):
assert settings.DEBUG == True assert settings.DEBUG == True
dbfile = settings.DATABASES['default']['NAME'] dbfile = settings.DATABASES['default']['NAME']
if os.path.exists(dbfile): if os.path.exists(dbfile):
@ -26,9 +26,10 @@ class Command(BaseCommand):
call_command('migrate', noinput=True) call_command('migrate', noinput=True)
self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...')
create_test_data(size=8) create_test_data(size=8)
self.stdout.write('Vytvoreno %d uzivatelu, %d skol, %d resitelu, %d rocniku, %d cisel, %d problemu, %d reseni.' % self.stdout.write('Vytvoreno %d uzivatelu, %d skol, %d resitelu, %d rocniku, %d cisel,'
(User.objects.count(), Skola.objects.count(), Resitel.objects.count(), Rocnik.objects.count(), ' %d problemu, %d reseni.'.format(User.objects.count(), Skola.objects.count(),
Cislo.objects.count(), Problem.objects.count(), Reseni.objects.count())) Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),
Problem.objects.count(), Reseni.objects.count()))

2
seminar/migrations/0023_add_novinky.py

@ -20,7 +20,7 @@ class Migration(migrations.Migration):
('datum', models.DateField(auto_now_add=True)), ('datum', models.DateField(auto_now_add=True)),
('text', models.TextField(null=True, verbose_name=b'Text novinky', blank=True)), ('text', models.TextField(null=True, verbose_name=b'Text novinky', blank=True)),
('obrazek', models.ImageField(upload_to=b'image_novinky/%Y/%m/%d/', null=True, verbose_name=b'Obr\xc3\xa1zek', blank=True)), ('obrazek', models.ImageField(upload_to=b'image_novinky/%Y/%m/%d/', null=True, verbose_name=b'Obr\xc3\xa1zek', blank=True)),
('zverejneno', models.BooleanField(default=b'False', verbose_name=b'Zve\xc5\x99ejn\xc4\x9bno')), ('zverejneno', models.BooleanField(default=False, verbose_name=b'Zve\xc5\x99ejn\xc4\x9bno')),
('autor', models.ForeignKey(verbose_name=b'Autor novinky', to=settings.AUTH_USER_MODEL)), ('autor', models.ForeignKey(verbose_name=b'Autor novinky', to=settings.AUTH_USER_MODEL)),
], ],
options={ options={

2
seminar/migrations/0049_auto_20190430_2354.py

@ -289,7 +289,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='novinky', model_name='novinky',
name='zverejneno', name='zverejneno',
field=models.BooleanField(default='False', verbose_name='Zveřejněno'), field=models.BooleanField(default=False, verbose_name='Zveřejněno'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='organizator', model_name='organizator',

12
seminar/models.py

@ -64,7 +64,8 @@ class Osoba(SeminarModelBase):
prezdivka = models.CharField('přezdívka', max_length=256) prezdivka = models.CharField('přezdívka', max_length=256)
# User, pokud má na webu účet # User, pokud má na webu účet
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, verbose_name='uživatel') user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name='uživatel')
# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování) # 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) pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
@ -119,7 +120,8 @@ class Osoba(SeminarModelBase):
return force_unicode('%s %s' % (self.jmeno, self.prijmeni)) return force_unicode('%s %s' % (self.jmeno, self.prijmeni))
def inicial_krestni(self): def inicial_krestni(self):
return force_unicode('%s.' % (self.jmeno[0])) jmena = self.jmeno.split()
return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena])
def __str__(self): def __str__(self):
return force_unicode("Osoba({})".format(self.plne_jmeno())) return force_unicode("Osoba({})".format(self.plne_jmeno()))
@ -400,7 +402,7 @@ class Cislo(SeminarModelBase):
'zveřejněna výsledkovka', 'zveřejněna výsledkovka',
default=False, default=False,
help_text='Je-li false u veřejného čísla,\ help_text='Je-li false u veřejného čísla,\
není výsledkovka zatím veřejná.') není výsledkovka zatím veřejná.')
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 číslu (plain text)') help_text='Neveřejná poznámka k číslu (plain text)')
@ -828,7 +830,7 @@ class Hodnoceni(SeminarModelBase):
# Django neumí jednoduše serializovat partial nebo třídu s __call__ # Django neumí jednoduše serializovat partial nebo třídu s __call__
# (https://docs.djangoproject.com/en/1.8/topics/migrations/), # (https://docs.djangoproject.com/en/1.8/topics/migrations/),
# neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru # neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru
# podle adresáře řešíme takto. # podle adresáře řešíme takto.
## ##
@ -1082,7 +1084,7 @@ class Obrazek(SeminarModelBase):
null=True, blank=True) null=True, blank=True)
text = models.ForeignKey(Text, verbose_name='text', text = models.ForeignKey(Text, verbose_name='text',
help_text='text, ve kterém se obrázek vyskytuje', null=False, blank=False) help_text='text, ve kterém se obrázek vyskytuje', null=False, blank=False)
do_cisla_barevny = models.FileField('barevný obrázek do čísla', do_cisla_barevny = models.FileField('barevný obrázek do čísla',
help_text = 'Barevná verze obrázku do čísla', help_text = 'Barevná verze obrázku do čísla',

154
seminar/tests.py

@ -14,81 +14,81 @@ from seminar import ovvpfile
from seminar import utils from seminar import utils
class SeminarBasicTests(TestCase): class SeminarBasicTests(TestCase):
def setUp(self): def setUp(self):
create_test_data(size=2) create_test_data(size=2)
self.client = Client() self.client = Client()
def tearDown(self): def tearDown(self):
call_command('flush', noinput=True, verbosity=0, interactive=False) call_command('flush', noinput=True, verbosity=0, interactive=False)
self.cleint = None self.cleint = None
def test_rocniky(self): def test_rocniky(self):
r19 = Rocnik.objects.get(rocnik=21) r19 = Rocnik.objects.get(rocnik=21)
self.assertEqual(r19.roman(), 'XXI') self.assertEqual(r19.roman(), 'XXI')
def test_render_cislo_e2e(self): def test_render_cislo_e2e(self):
cs = Cislo.objects.all() cs = Cislo.objects.all()
for c in cs[:4]: for c in cs[:4]:
url = c.verejne_url() url = c.verejne_url()
r = self.client.get(url) r = self.client.get(url)
assert r.status_code == 200 assert r.status_code == 200
assert len(r.content) >= 100 assert len(r.content) >= 100
# TODO: Validate cntent as HTML # TODO: Validate cntent as HTML
def test_render_problem_e2e(self): def test_render_problem_e2e(self):
ps = Problem.objects.all() ps = Problem.objects.all()
for p in ps[:4]: for p in ps[:4]:
url = p.verejne_url() url = p.verejne_url()
r = self.client.get(url) r = self.client.get(url)
assert r.status_code == 200 assert r.status_code == 200
assert len(r.content) >= 100 assert len(r.content) >= 100
# TODO: Validate cntent as HTML # TODO: Validate cntent as HTML
def test_export_e2e(self): def test_export_e2e(self):
i_url = '/aesop-export/index.csv' i_url = '/aesop-export/index.csv'
i_r = self.client.get(i_url) i_r = self.client.get(i_url)
assert i_r.status_code == 200 assert i_r.status_code == 200
ls = i_r.content.strip().split('\n') ls = i_r.content.strip().split('\n')
for u in [ls[0], ls[-1]]: for u in [ls[0], ls[-1]]:
ex_r = self.client.get('/aesop-export/' + u) ex_r = self.client.get('/aesop-export/' + u)
assert ex_r.status_code == 200 assert ex_r.status_code == 200
assert len(ex_r.content) >= 100 assert len(ex_r.content) >= 100
o = ovvpfile.parse(ex_r.content) o = ovvpfile.parse(ex_r.content)
assert o.headers['version'] == '1' assert o.headers['version'] == '1'
def test_admin_url(self): def test_admin_url(self):
for m in [Skola, Resitel, Rocnik, Cislo, Problem, Reseni, Nastaveni]: for m in [Skola, Resitel, Rocnik, Cislo, Problem, Reseni, Nastaveni]:
o = m.objects.first() o = m.objects.first()
url = o.admin_url() url = o.admin_url()
assert url assert url
view = resolve(url) view = resolve(url)
assert view assert view
def test_verejne_url(self): def test_verejne_url(self):
for m in [Rocnik, Cislo, Problem]: for m in [Rocnik, Cislo, Problem]:
p = Problem.objects.first() p = Problem.objects.first()
url = p.verejne_url() url = p.verejne_url()
assert url assert url
view = resolve(url) view = resolve(url)
assert view assert view
def test_ovvpfile(self): def test_ovvpfile(self):
filetext = "H1\ta\nH2\tb\tc\n\nx\ty\tz\n0\t1\t2\n3\t4\t5\n" filetext = "H1\ta\nH2\tb\tc\n\nx\ty\tz\n0\t1\t2\n3\t4\t5\n"
o = ovvpfile.parse(filetext) o = ovvpfile.parse(filetext)
assert len(o.headers) == 2 assert len(o.headers) == 2
assert o.headers['H2'] == 'b\tc' assert o.headers['H2'] == 'b\tc'
assert o.columns == ['x','y','z'] assert o.columns == ['x','y','z']
assert len(o.rows) == 2 assert len(o.rows) == 2
assert o.rows[0]['z'] == '2' assert o.rows[0]['z'] == '2'
t = o.to_string() t = o.to_string()
assert t == filetext assert t == filetext
def test_roman(self): def test_roman(self):
for i in [0, 1, 23, 2015, 1999, 42, 4, 400, 78, 4321, 8765, 999]: for i in [0, 1, 23, 2015, 1999, 42, 4, 400, 78, 4321, 8765, 999]:
r = utils.roman(i) r = utils.roman(i)
fr = utils.from_roman(r) fr = utils.from_roman(r)
assert fr == i assert fr == i

245
seminar/testutils.py

@ -4,6 +4,7 @@ import datetime
import random import random
import django.contrib.auth import django.contrib.auth
from django.db import transaction from django.db import transaction
import unidecode
from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici
@ -14,89 +15,163 @@ User = django.contrib.auth.get_user_model()
@transaction.atomic @transaction.atomic
def create_test_data(size = 6, rnd = None): def create_test_data(size = 6, rnd = None):
assert size >= 1 assert size >= 1
# pevna pseudo-nahodnost # pevna pseudo-nahodnost
rnd = rnd or random.Random(x=42) rnd = rnd or random.Random(x=42)
# static URL stranky # static URL stranky
s = Site.objects.filter(name="example.com") # FIXME: nakopirovat sem vsechny z produkcni databaze
f = FlatPage.objects.create(url="/", title="Seminář M&M", content = "<p>V&iacute;tejte na str&aacute;nce semin&aacute;ře MaM!</p>") s = Site.objects.filter(name="example.com")
f.sites.add(s[0]) f = FlatPage.objects.create(url="/", title="Seminář M&M", content = "<p>V&iacute;tejte na str&aacute;nce semin&aacute;ře MaM!</p>")
f.save() f.sites.add(s[0])
f.save()
# users
admin = User.objects.create_superuser(username='admin', email='', password='admin') # users
admin = User.objects.create_superuser(username='admin', email='', password='admin')
usernames = ['anet', 'bara', 'cyril', 'david', 'eva', 'filip']
orgs = [] usernames = ['anet', 'bara', 'cyril', 'david', 'eva', 'filip']
for org in usernames[:size]: orgs = []
o = User.objects.create_user(username=org, password=org) for org in usernames[:size]:
o.first_name = org.capitalize() o = User.objects.create_user(username=org, password=org)
o.save() o.first_name = org.capitalize()
orgs.append(o) o.save()
orgs.append(o)
# skoly
Skola.objects.create(mesto = u'Praha', stat='CZ', psc='101 00', ulice=u'Krátká 5', nazev=u'První ZŠ', je_zs=True, je_ss=False) # skoly
Skola.objects.create(mesto = u'Praha', stat='CZ', psc='101 00', ulice=u'Krátká 5', nazev=u'První SŠ', je_zs=False, je_ss=True) skoly = []
Skola.objects.create(mesto = u'Praha', stat='CZ', psc='102 00', ulice=u'Dlouhá 5', nazev=u'Druhá SŠ', je_zs=False, je_ss=True) skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='101 00', ulice='Krátká 5',
Skola.objects.create(mesto = u'Praha', stat='CZ', psc='103 00', ulice=u'Široká 3', nazev=u'Třetí SŠ a ZŠ', je_zs=True, je_ss=True) nazev='První ZŠ', je_zs=True, je_ss=False))
Skola.objects.create(mesto = u'Ostrava', stat='CZ', psc='700 00', ulice=u'Hluboká 42', nazev=u'Hutní gympl', je_zs=False, je_ss=True) skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='101 00', ulice='Krátká 5',
Skola.objects.create(mesto = u'Humenné', stat='SK', psc='012 34', ulice=u'Pltká 1', nazev=u'Sredná škuola', je_zs=False, je_ss=True) nazev='První SŠ', je_zs=False, je_ss=True))
skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='102 00', ulice='Dlouhá 5',
# resitele nazev='Druhá SŠ', je_zs=False, je_ss=True))
jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril'] skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='103 00', ulice='Široká 3',
jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie'] nazev='Třetí SŠ a ZŠ', je_zs=True, je_ss=True))
prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', 'Pokora', 'Koch', 'Szegedy', 'Rudý'] skoly.append(Skola.objects.create(mesto = 'Ostrava', stat='CZ', psc='700 00', ulic='Hluboká 42',
prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', 'Mrkvová', 'Suchá', 'Lovelace', 'Holcová'] nazev='Hutní gympl', je_zs=False, je_ss=True))
for i in range(3*size): skoly.append(Skola.objects.create(mesto = 'Humenné', stat='SK', psc='012 34', ulice='Pltká 1',
skola = rnd.choice(Skola.objects.all()) nazev='Sredná škuola', je_zs=False, je_ss=True))
pohlavi = rnd.randint(0,1) #FIXME pridat kontaktni osobu alespon nekde
jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) skoly.append(zlinska = Skola.objects.create(mesto = 'Zlín', stat='CZ', psc='76001',
prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) ulice='náměstí T.G. Masaryka 2734-9', nazev='Gymnázium a Střední jazyková škola s právem SJZ',
Resitel.objects.create(skola = skola, datum_prihlaseni = datetime.date(rnd.randint(2002, 2014), rnd.randint(1,12), 1), kratky_nazev="GaSJŠspSJZ", je_zs=True, je_ss=True))
jmeno =jmeno, prijmeni = prijmeni, rok_maturity = rnd.randint(2015, 2019),
stat = skola.stat, zasilat = Resitel.ZASILAT_NIKAM, pohlavi_muz = pohlavi) # osoby
resitele = list(Resitel.objects.all()) jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel']
jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', 'Marta Iva', 'Shu Shan']
# rocniky prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', 'Pokora',
last_rocnik = 21 'Koch', 'Szegedy', 'Rudý', "von Neumann", "d'Este"]
for ri in range(last_rocnik - size, last_rocnik + 1): prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', 'Mrkvová',
r = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) 'Suchá', 'Lovelace', 'Holcová', 'Rui']
prezdivka = ['Kaki', 'Hurdur', 'Maracuja', 'Bobbo', None, None, None, None, None, None, None,
# cisla 'Riki', 'Sapa', None, '', '---', 'Koko']
cisel = rnd.randint(4, 6) domain = ['example.com', 'dolujeme.eu', 'mff.cuni.cz', 'strcprstskrzkrk.cz', 'british.co.uk',
'splachni.to', 'haha.org']
cs = {} seznam_ulic = ['Krátká', 'Vlhká', 'Jungmanova', '17. listopadu', '4. října', 'Roztocká',
for ci in range(1, cisel + 1): 'Forstova', 'Generála Františka Janouška', 'Náměstí Války', 'Svratecké náměstí',
vydano = datetime.date(r.prvni_rok, ci + 6, 1) 'Zelená lhota', 'Z Plynu', 'K Jezeru', 'U Kocourkova', 'Uštěpačná', 'Ostrorepská',
deadline = datetime.date(r.prvni_rok, ci + 8, 1) if ci + 2 < cisel else None 'Zubří']
c = Cislo.objects.create(rocnik = r, cislo = str(ci), datum_vydani=vydano, datum_deadline=deadline, verejne_db=True) seznam_mest = ['Praha', 'Brno', 'Ostrava', 'Horní Jelení', 'Dolní Zábrdovice', 'Prdelkov',
cs[ci] = c 'Stará myslivna', 'Kocourkov', 'Šalingrad', 'Medvědí hora', 'Basilej',
'Unterschiedlich', 'Old York', 'Lancastershire', 'Vóloďháza']
# problemy resene v ci
seq='#ABCDEFGHIJKLMNOPQRSTUVWXYZ' osoby = []
if ci >= 3: for i in range(3 * size):
for pi in range(1, ((size + 1) // 2) + 1): pohlavi = rnd.randint(0,1)
p = Problem.objects.create(autor = rnd.choice(orgs), cislo_zadani=cs[ci-2], cislo_reseni=cs[ci], jmeno = rnd.choice([jmena_m, jmena_f][pohlavi])
opravovatel = rnd.choice(orgs), kod = str(pi), nazev = u'Dummy úloha %s-%s' % (seq[ci], seq[pi]), prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi])
stav = Problem.STAV_ZADANY, typ = Problem.TYP_ULOHA, body = rnd.randint(1, 5)) prezdivka = rnd.choice(prezdivka)
p.text_problemu = (u"<p>Text problému <strong>%s.%s %s</strong><em> [id %d]</em> za %d body.</p>" % email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)])
(p.cislo_zadani.kod(), p.kod, p.nazev, p.id, p.body)) telefon = [rnd.choice([k for k in range(10)]) for i in range(10)]
p.text_problemu_org = u"<p><strong>Neveřejný</strong> text problému.</p>" narozeni = datetime.date(rnd.randint(1980, 2020), rnd.randint(1, 12), rnd.randint(1, 28))
p.save() ulic = rnd.choice([seznam_ulic])
cp = rnd.int(1, 99)
poc_reseni = rnd.randint(size // 2, size * 2) ulice = " ".join(ulic, cp)
res_sel = rnd.sample(resitele, min(poc_reseni, len(resitele) - 2)) mesto = rnd.choice([seznam_mest])
for resitel in res_sel: psc = [rnd.choice([k for k in range(10)]) for i in range(6)]
res = Reseni.objects.create(problem = p, resitel = resitel, osoby.append(Osoba.objects.create(jmeno = jmeno, prijmeni = prijmeni, prezdivka = prezdivka,
body = rnd.randint(0, p.body), cislo_body = cs[ci]) pohlavi_muz = pohlavi, email = email, telefon = telefon, datum_narozeni = narozeni,
ulice = ulice, mesto = mesto, psc = psc,
sous = Soustredeni.objects.create(rocnik=Rocnik.objects.first(), verejne_db=True, misto=u'Někde', datum_registrace = datetime.date(rnd.randint(2019, 2029), rnd.randint(1, 12),
datum_zacatku=datetime.date(2000, 11, 23), datum_konce=datetime.date(2000, 11, 27)) rnd.randint(1, 28))))
for res in rnd.sample(resitele, 6): #TODO pridat foto male a velke. Jak?
Soustredeni_Ucastnici.objects.create(resitel=res, soustredeni=sous)
sous.save() # resitele a organizatori
last_rocnik = 25
nastaveni = Nastaveni.objects.create(aktualni_rocnik = Rocnik.objects.last(), resitele = []
aktualni_cislo = Cislo.objects.all()[1]) organizatori = []
for os in osoby:
rand = rnd.randint(0, 8)
if not (rand % 8 == 0):
resitele.append(Resitel.objects.create(osoba = os, skola = rnd.choice([skoly]),
rok_maturity = rnd.randint(2019, 2029),
zasilat = rnd.choice(Resitel.ZASILAT_CHOICES)))
else:
pusobnost = rnd.randint(1, last_rocnik)
od = 1993 + last_rocnik - pusobnost
do = od + rnd.randint(1, 8)
# aktualni organizatori jeste nemaji vyplnene organizuje_do
if do > datetime.now().year:
do = None
organizatori.append(Organizator.objects.create(osoba = os, organizuje_od = od,
organizuje_do = do))
# rocniky
for ri in range(min(last_rocnik - size, 1), last_rocnik + 1):
r = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri)
# cisla
cisel = rnd.randint(4, 8)
slovnik_cisel = {}
for ci in range(1, cisel + 1):
if ci >= 3:
vydano = datetime.date(r.prvni_rok, ci + 6, rnd.randint(1, 28))
deadline = datetime.date(r.prvni_rok, ci + 8, rnd.randint(1, 28))
else:
vydano = datetime.date(r.druhy_rok, ci - 3, rnd.randint(1, 28))
# posledni 2 cisla v rocniku nemaji deadline
if (ci + 2 > cisel):
deadline = datetime.date(r.druhy_rok, ci - 1, rnd.randint(1, 28))
else:
deadline = None
cislo = Cislo.objects.create(rocnik = r, cislo = str(ci), datum_vydani=vydano,
datum_deadline=deadline, verejne_db=True)
slovnik_cisel[ci] = cislo
# FIXME: misto typu ruzne typy objektu a vnoreni do sebe
# TODO: vytvorit temata s ruznymi vlakny
# TODO: nagenerovat starsim rocnikum pohadku
# problemy resene v ci
# seq='#ABCDEFGHIJKLMNOPQRSTUVWXYZ'
# if ci >= 3:
# for pi in range(1, ((size + 1) // 2) + 1):
# p = Problem.objects.create(autor = rnd.choice(orgs), cislo_zadani=cs[ci-2],
# cislo_reseni=cs[ci], opravovatel = rnd.choice(orgs), kod = str(pi),
# nazev = u'Dummy úloha %s-%s' % (seq[ci], seq[pi]),
# stav = Problem.STAV_ZADANY, typ = Problem.TYP_ULOHA, body = rnd.randint(1, 5))
# p.text_problemu = (u"<p>Text problému <strong>%s.%s %s</strong><em> [id %d]</em> za %d body.</p>" %
# (p.cislo_zadani.kod(), p.kod, p.nazev, p.id, p.body))
# p.text_problemu_org = u"<p><strong>Neveřejný</strong> text problému.</p>"
# p.save()
#
# poc_reseni = rnd.randint(size // 2, size * 2)
# res_sel = rnd.sample(resitele, min(poc_reseni, len(resitele) - 2))
# for resitel in res_sel:
# res = Reseni.objects.create(problem = p, resitel = resitel,
# body = rnd.randint(0, p.body), cislo_body = cs[ci])
#
# TODO: nahodne nagenerovat problemum reseni a prilohy reseni, hodnoceni
# TODO: vice soustredeni a k nim nahodne podmnoziny organizatoru a ucastniku
sous = Soustredeni.objects.create(rocnik=Rocnik.objects.first(), verejne_db=True, misto=u'Někde',
datum_zacatku=datetime.date(2000, 11, 23), datum_konce=datetime.date(2000, 11, 27))
for res in rnd.sample(resitele, 6):
Soustredeni_Ucastnici.objects.create(resitel=res, soustredeni=sous)
sous.save()
nastaveni = Nastaveni.objects.create(aktualni_rocnik = Rocnik.objects.last(),
aktualni_cislo = Cislo.objects.all()[1])

122
seminar/tools.py

@ -5,78 +5,78 @@ from .models import Resitel
from reversion import revisions as reversion from reversion import revisions as reversion
def merge_props(r1, r2, prop, pretend=True, smaller=False, equal=True): def merge_props(r1, r2, prop, pretend=True, smaller=False, equal=True):
"""Merge r2.`prop` into r1.`prop`. """Merge r2.`prop` into r1.`prop`.
If r1.`prop` unset, use r1.`prop`=r2.`prop`. If r1.`prop` unset, use r1.`prop`=r2.`prop`.
If both set and equal=True, warn if not equal. If both set and equal=True, warn if not equal.
If both set and smaller=True, use the smaller one. If both set and smaller=True, use the smaller one.
With pretend=True does not modify r1. With pretend=True does not modify r1.
""" """
a1 = r1.__getattribute__(prop) a1 = r1.__getattribute__(prop)
a2 = r2.__getattribute__(prop) a2 = r2.__getattribute__(prop)
if not a1: if not a1:
if not pretend: if not pretend:
r1.__setattr__(prop, a2) r1.__setattr__(prop, a2)
elif a2: elif a2:
if equal and a1 != a2: if equal and a1 != a2:
log.warn(u"merge: Ruzna %s: %s VS %s", prop, a1, a2) log.warn(u"merge: Ruzna %s: %s VS %s", prop, a1, a2)
if smaller: if smaller:
if not pretend: if not pretend:
r1.__setattr__(prop, min(a1, a2)) r1.__setattr__(prop, min(a1, a2))
def merge_Resitel(rbase, rmerge, pretend=True): def merge_Resitel(rbase, rmerge, pretend=True):
"""Zahrne data a vztahy Resitele rmerge do Resitele rbase, pak smaze rmerge. """Zahrne data a vztahy Resitele rmerge do Resitele rbase, pak smaze rmerge.
Selze pro uzivatele s user!=NULL. S pretend=True nezmeni databazi. Selze pro uzivatele s user!=NULL. S pretend=True nezmeni databazi.
""" """
# Ma relace: skola # Ma relace: skola
# Je v relaci: user, reseni, soustredeni_ucastnici, vysledky_base(VIEW) # Je v relaci: user, reseni, soustredeni_ucastnici, vysledky_base(VIEW)
log.info(u"merge: %s <- %s", unicode(rbase), unicode(rmerge)) log.info(u"merge: %s <- %s", unicode(rbase), unicode(rmerge))
assert not rbase.user assert not rbase.user
assert not rmerge.user assert not rmerge.user
assert rbase != rmerge assert rbase != rmerge
if (rbase.jmeno != rmerge.jmeno) or (rbase.prijmeni != rmerge.prijmeni): if (rbase.jmeno != rmerge.jmeno) or (rbase.prijmeni != rmerge.prijmeni):
log.error(u"merge: Ruzna jmena: %s VS %s", rbase, rmerge) log.error(u"merge: Ruzna jmena: %s VS %s", rbase, rmerge)
if rbase.rok_maturity != rmerge.rok_maturity: if rbase.rok_maturity != rmerge.rok_maturity:
log.error(u"merge: Ruzne roky maturity: %s VS %s", rbase.rok_maturity, rmerge.rok_maturity) log.error(u"merge: Ruzne roky maturity: %s VS %s", rbase.rok_maturity, rmerge.rok_maturity)
with reversion.create_revision(): with reversion.create_revision():
reversion.set_comment('Merge duplicitnich Resitelu: %r <- %r' % (rbase.pk, rmerge.pk)) reversion.set_comment('Merge duplicitnich Resitelu: %r <- %r' % (rbase.pk, rmerge.pk))
merge_props(rbase, rmerge, 'skola', pretend=pretend) merge_props(rbase, rmerge, 'skola', pretend=pretend)
merge_props(rbase, rmerge, 'datum_narozeni', pretend=pretend) merge_props(rbase, rmerge, 'datum_narozeni', pretend=pretend)
merge_props(rbase, rmerge, 'datum_prihlaseni', pretend=pretend) merge_props(rbase, rmerge, 'datum_prihlaseni', pretend=pretend)
merge_props(rbase, rmerge, 'datum_souhlasu_zasilani', pretend=pretend, smaller=True, equal=False) merge_props(rbase, rmerge, 'datum_souhlasu_zasilani', pretend=pretend, smaller=True, equal=False)
merge_props(rbase, rmerge, 'datum_souhlasu_udaje', pretend=pretend, smaller=True, equal=False) merge_props(rbase, rmerge, 'datum_souhlasu_udaje', pretend=pretend, smaller=True, equal=False)
merge_props(rbase, rmerge, 'email', pretend=pretend) merge_props(rbase, rmerge, 'email', pretend=pretend)
if rmerge.import_mamoper_id and not pretend: if rmerge.import_mamoper_id and not pretend:
rbase.import_mamoper_id += ' ' + rmerge.import_mamoper_id rbase.import_mamoper_id += ' ' + rmerge.import_mamoper_id
if rmerge.poznamka and not pretend: if rmerge.poznamka and not pretend:
rbase.poznamka += ' ' + rmerge.poznamka rbase.poznamka += ' ' + rmerge.poznamka
merge_props(rbase, rmerge, 'mesto', pretend=pretend) merge_props(rbase, rmerge, 'mesto', pretend=pretend)
merge_props(rbase, rmerge, 'pohlavi_muz', pretend=pretend) merge_props(rbase, rmerge, 'pohlavi_muz', pretend=pretend)
merge_props(rbase, rmerge, 'psc', pretend=pretend) merge_props(rbase, rmerge, 'psc', pretend=pretend)
merge_props(rbase, rmerge, 'stat', pretend=pretend) merge_props(rbase, rmerge, 'stat', pretend=pretend)
merge_props(rbase, rmerge, 'telefon', pretend=pretend) merge_props(rbase, rmerge, 'telefon', pretend=pretend)
merge_props(rbase, rmerge, 'ulice', pretend=pretend) merge_props(rbase, rmerge, 'ulice', pretend=pretend)
merge_props(rbase, rmerge, 'zasilat', pretend=pretend) merge_props(rbase, rmerge, 'zasilat', pretend=pretend)
for res in rmerge.reseni.all(): for res in rmerge.reseni.all():
if not pretend: if not pretend:
res.resitel = rbase res.resitel = rbase
res.save() res.save()
for uc in rmerge.soustredeni_ucastnici_set.all(): for uc in rmerge.soustredeni_ucastnici_set.all():
if not pretend: if not pretend:
uc.resitel = rbase uc.resitel = rbase
uc.save() uc.save()
if not pretend: if not pretend:
rmerge.delete() rmerge.delete()
rbase.save() rbase.save()

182
seminar/urls.py

@ -8,108 +8,108 @@ from django.views.generic.base import RedirectView
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
urlpatterns = [ urlpatterns = [
# REDIRECTy # REDIRECTy
url(r'^jak-resit/$', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), url(r'^jak-resit/$', RedirectView.as_view(url='/co-je-MaM/jak-resit/')),
# Organizatori # Organizatori
url(r'^co-je-MaM/organizatori/$', views.CojemamOrganizatoriView.as_view(), name='organizatori'), url(r'^co-je-MaM/organizatori/$', views.CojemamOrganizatoriView.as_view(), name='organizatori'),
url(r'^co-je-MaM/organizatori/organizovali/$', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'), url(r'^co-je-MaM/organizatori/organizovali/$', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'),
# Archiv # Archiv
url(r'^archiv/cisla/$', views.ArchivView.as_view()), url(r'^archiv/cisla/$', views.ArchivView.as_view()),
url(r'^archiv/temata/$', views.ArchivTemataView.as_view()), url(r'^archiv/temata/$', views.ArchivTemataView.as_view()),
url(r'^rocnik/(?P<rocnik>\d+)/$', views.RocnikView.as_view(), name='seminar_rocnik'), url(r'^rocnik/(?P<rocnik>\d+)/$', views.RocnikView.as_view(), name='seminar_rocnik'),
#url(r'^cislo/(?P<rocnik>\d+)\.(?P<cislo>[0-9-]+)/$', views.CisloView.as_view(), name='seminar_cislo'), #url(r'^cislo/(?P<rocnik>\d+)\.(?P<cislo>[0-9-]+)/$', views.CisloView.as_view(), name='seminar_cislo'),
url(r'^problem/(?P<pk>\d+)/$', views.ProblemView.as_view(), name='seminar_problem'), url(r'^problem/(?P<pk>\d+)/$', views.ProblemView.as_view(), name='seminar_problem'),
#url(r'^problem/(?P<pk>\d+)/(?P<prispevek>\d+)/$', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), #url(r'^problem/(?P<pk>\d+)/(?P<prispevek>\d+)/$', views.PrispevekView.as_view(), name='seminar_problem_prispevek'),
# Soustredeni # Soustredeni
url( url(
r'^soustredeni/probehlo/$', r'^soustredeni/probehlo/$',
views.SoustredeniListView.as_view(), views.SoustredeniListView.as_view(),
name='seminar_seznam_soustredeni' name='seminar_seznam_soustredeni'
), ),
url( url(
r'^soustredeni/probehlo/(?P<soustredeni>\d+)/$', r'^soustredeni/probehlo/(?P<soustredeni>\d+)/$',
views.SoustredeniView.as_view(), views.SoustredeniView.as_view(),
name='seminar_soustredeni' name='seminar_soustredeni'
), ),
url( url(
r'^soustredeni/(?P<soustredeni>\d+)/seznam_ucastniku$', r'^soustredeni/(?P<soustredeni>\d+)/seznam_ucastniku$',
staff_member_required(views.SoustredeniUcastniciView.as_view()), staff_member_required(views.SoustredeniUcastniciView.as_view()),
name='soustredeni_ucastnici' name='soustredeni_ucastnici'
), ),
url( url(
r'^soustredeni/(?P<soustredeni>\d+)/maily_ucastniku$', r'^soustredeni/(?P<soustredeni>\d+)/maily_ucastniku$',
staff_member_required(views.SoustredeniMailyUcastnikuView.as_view()), staff_member_required(views.SoustredeniMailyUcastnikuView.as_view()),
name='maily_ucastniku' name='maily_ucastniku'
), ),
url( url(
r'^soustredeni/(?P<soustredeni>\d+)/stvrzenky/(?P<first_num>\d+)$', r'^soustredeni/(?P<soustredeni>\d+)/stvrzenky/(?P<first_num>\d+)$',
staff_member_required(views.soustredeniStvrzenkyExportView), staff_member_required(views.soustredeniStvrzenkyExportView),
name='soustredeni_stvrzenky' name='soustredeni_stvrzenky'
), ),
url( url(
r'^soustredeni/(?P<soustredeni>\d+)/export_ucastniku$', r'^soustredeni/(?P<soustredeni>\d+)/export_ucastniku$',
staff_member_required(views.soustredeniUcastniciExportView), staff_member_required(views.soustredeniUcastniciExportView),
name='soustredeni_ucastnici_export' name='soustredeni_ucastnici_export'
), ),
url( url(
r'^soustredeni/(?P<soustredeni>\d+)/fotogalerie/', r'^soustredeni/(?P<soustredeni>\d+)/fotogalerie/',
include('galerie.urls') include('galerie.urls')
), ),
# Zadani # Zadani
url(r'^zadani/aktualni/$', views.AktualniZadaniView, name='seminar_aktualni_zadani'), url(r'^zadani/aktualni/$', views.AktualniZadaniView, name='seminar_aktualni_zadani'),
url(r'^zadani/temata/$', views.ZadaniTemataView, name='seminar_temata'), url(r'^zadani/temata/$', views.ZadaniTemataView, name='seminar_temata'),
#url(r'^zadani/vysledkova-listina/$', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), #url(r'^zadani/vysledkova-listina/$', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'),
url(r'^$', views.TitulniStranaView.as_view(), name='titulni_strana'), url(r'^$', views.TitulniStranaView.as_view(), name='titulni_strana'),
url(r'^stare-novinky/$', views.StareNovinkyView.as_view(), name='stare_novinky'), url(r'^stare-novinky/$', views.StareNovinkyView.as_view(), name='stare_novinky'),
# Clanky # Clanky
url(r'^clanky/resitel/$', views.ClankyResitelView.as_view(), name='clanky_resitel'), url(r'^clanky/resitel/$', views.ClankyResitelView.as_view(), name='clanky_resitel'),
#url(r'^clanky/org/$', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'), #url(r'^clanky/org/$', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'),
# Aesop # Aesop
url(r'^aesop-export/mam-rocnik-(?P<prvni_rok>\d+)\.csv$', export.ExportRocnikView.as_view(), name='seminar_export_rocnik'), url(r'^aesop-export/mam-rocnik-(?P<prvni_rok>\d+)\.csv$', export.ExportRocnikView.as_view(), name='seminar_export_rocnik'),
url(r'^aesop-export/mam-sous-(?P<datum_zacatku>[\d-]+)\.csv$', export.ExportSousView.as_view(), name='seminar_export_sous'), url(r'^aesop-export/mam-sous-(?P<datum_zacatku>[\d-]+)\.csv$', export.ExportSousView.as_view(), name='seminar_export_sous'),
url(r'^aesop-export/index.csv$', export.ExportIndexView.as_view(), name='seminar_export_index'), url(r'^aesop-export/index.csv$', export.ExportIndexView.as_view(), name='seminar_export_index'),
# Stranky viditelne pouze pro orgy: # Stranky viditelne pouze pro orgy:
#url( #url(
# r'^rocnik/(?P<rocnik>\d+)/vysledkovka.tex$', # r'^rocnik/(?P<rocnik>\d+)/vysledkovka.tex$',
# staff_member_required(views.RocnikVysledkovkaView.as_view()), # staff_member_required(views.RocnikVysledkovkaView.as_view()),
# name='seminar_cislo_vysledkovka' # name='seminar_cislo_vysledkovka'
#), #),
#url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/vysledkovka.tex$', #url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/vysledkovka.tex$',
# staff_member_required(views.CisloVysledkovkaView.as_view()), name='seminar_cislo_vysledkovka'), # staff_member_required(views.CisloVysledkovkaView.as_view()), name='seminar_cislo_vysledkovka'),
url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/obalky.pdf$', url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/obalky.pdf$',
staff_member_required(views.cisloObalkyView), name='seminar_cislo_obalky'), staff_member_required(views.cisloObalkyView), name='seminar_cislo_obalky'),
#url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/tituly.tex$', #url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/tituly.tex$',
# staff_member_required(views.TitulyView), name='seminar_cislo_titul'), # staff_member_required(views.TitulyView), name='seminar_cislo_titul'),
url(r'^stav$', url(r'^stav$',
staff_member_required(views.StavDatabazeView), name='stav_databaze'), staff_member_required(views.StavDatabazeView), name='stav_databaze'),
url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/obalkovani$', url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/obalkovani$',
staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'), staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'),
url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/tex-download.json$', url(r'^cislo/(?P<rocnik>\d+).(?P<cislo>[0-9-]+)/tex-download.json$',
staff_member_required(views.texDownloadView), name='seminar_tex_download'), staff_member_required(views.texDownloadView), name='seminar_tex_download'),
url(r'^soustredeni/(?P<soustredeni>\d+)/obalky.pdf', url(r'^soustredeni/(?P<soustredeni>\d+)/obalky.pdf',
staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'),
url(r'^tex-upload/login/$', views.LoginView, name='seminar_login'), url(r'^tex-upload/login/$', views.LoginView, name='seminar_login'),
url( url(
r'^tex-upload/$', r'^tex-upload/$',
staff_member_required(views.texUploadView), staff_member_required(views.texUploadView),
name='seminar_tex_upload' name='seminar_tex_upload'
), ),
url(r'^prihlaska/$',views.get_name), url(r'^prihlaska/$',views.get_name),
# Ceka na autocomplete v3 # Ceka na autocomplete v3
# url(r'^autocomplete/organizatori/$', # url(r'^autocomplete/organizatori/$',
# staff_member_required(views.OrganizatorAutocomplete.as_view()), # staff_member_required(views.OrganizatorAutocomplete.as_view()),
# name='seminar_autocomplete_organizator') # name='seminar_autocomplete_organizator')
] ]

116
seminar/utils.py

@ -6,77 +6,77 @@ from django.contrib.auth.decorators import user_passes_test
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
def histogram(seznam): def histogram(seznam):
d = {} d = {}
for i in seznam: for i in seznam:
if i not in d: if i not in d:
d[i] = 0 d[i] = 0
d[i] += 1 d[i] += 1
return d return d
roman_numerals = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1), roman_numerals = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')) ('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I'))
def roman(num): def roman(num):
res = "" res = ""
for i, n in roman_numerals: for i, n in roman_numerals:
res += n * (num // i) res += n * (num // i)
num %= i num %= i
return res return res
def from_roman(rom): def from_roman(rom):
if not rom: if not rom:
return 0 return 0
for i, n in roman_numerals: for i, n in roman_numerals:
if rom.upper().startswith(n): if rom.upper().startswith(n):
return i + from_roman(rom[len(n):]) return i + from_roman(rom[len(n):])
raise Exception('Invalid roman numeral: "%s"', rom) raise Exception('Invalid roman numeral: "%s"', rom)
def seznam_problemu(): def seznam_problemu():
from .models import Problem, Resitel, Rocnik, Reseni, Cislo from .models import Problem, Resitel, Rocnik, Reseni, Cislo
problemy = [] problemy = []
# Pomocna fce k formatovani problemovych hlasek # Pomocna fce k formatovani problemovych hlasek
def prb(cls, msg, objs=None): def prb(cls, msg, objs=None):
s = u'<b>%s:</b> %s' % (cls.__name__, msg) s = u'<b>%s:</b> %s' % (cls.__name__, msg)
if objs: if objs:
s += u' [' s += u' ['
for o in objs: for o in objs:
try: try:
url = o.admin_url() url = o.admin_url()
except: except:
url = None url = None
if url: if url:
s += u'<a href="%s">%s</a>, ' % (url, o.pk, ) s += u'<a href="%s">%s</a>, ' % (url, o.pk, )
else: else:
s += u'%s, ' % (o.pk, ) s += u'%s, ' % (o.pk, )
s = s[:-2] + u']' s = s[:-2] + u']'
problemy.append(s) problemy.append(s)
# Duplicita jmen # Duplicita jmen
jmena = {} jmena = {}
for r in Resitel.objects.all(): for r in Resitel.objects.all():
j = r.plne_jmeno() j = r.plne_jmeno()
if j not in jmena: if j not in jmena:
jmena[j] = [] jmena[j] = []
jmena[j].append(r) jmena[j].append(r)
for j in jmena: for j in jmena:
if len(jmena[j]) > 1: if len(jmena[j]) > 1:
prb(Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) prb(Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j])
# Data maturity a narození # Data maturity a narození
for r in Resitel.objects.all(): for r in Resitel.objects.all():
if not r.rok_maturity: if not r.rok_maturity:
prb(Resitel, u'Neznámý rok maturity', [r]) prb(Resitel, u'Neznámý rok maturity', [r])
if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10):
prb(Resitel, u'Podezřelé datum maturity', [r]) prb(Resitel, u'Podezřelé datum maturity', [r])
if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12): if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12):
prb(Resitel, u'Podezřelé datum narození', [r]) prb(Resitel, u'Podezřelé datum narození', [r])
# if not r.email: # if not r.email:
# prb(Resitel, u'Neznámý email', [r]) # prb(Resitel, u'Neznámý email', [r])
return problemy return problemy

1516
seminar/views.py

File diff suppressed because it is too large
Loading…
Cancel
Save