diff --git a/convert_spaces_to_tabs.sh b/convert_spaces_to_tabs.sh new file mode 100755 index 00000000..8f98f6ae --- /dev/null +++ b/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 diff --git a/galerie/admin.py b/galerie/admin.py index 9ab2c3ba..168a839c 100644 --- a/galerie/admin.py +++ b/galerie/admin.py @@ -10,45 +10,45 @@ from autocomplete_light import shortcuts as autocomplete_light # akction def zverejnit_fotogalerii(modeladmin, request, queryset): - '''zverejni vybranou fotogalerii i jeji vsechny podgalerie''' - for galerie in queryset: - galerie.zobrazit = 0 - galerie.save() - zverejnit_fotogalerii(modeladmin, request, - Galerie.objects.filter(galerie_up = galerie)) - zverejnit_fotogalerii.short_description = 'Zveřejnit fotogalerie' + '''zverejni vybranou fotogalerii i jeji vsechny podgalerie''' + for galerie in queryset: + galerie.zobrazit = 0 + galerie.save() + zverejnit_fotogalerii(modeladmin, request, + Galerie.objects.filter(galerie_up = galerie)) + zverejnit_fotogalerii.short_description = 'Zveřejnit fotogalerie' def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset): - '''zneverjni vybranou fotogalerii i jeji vsechny podgalerie''' - for galerie in queryset: - galerie.zobrazit = 1 - galerie.save() - prepnout_fotogalerii_do_org_rezimu(modeladmin, request, - Galerie.objects.filter(galerie_up = galerie)) - prepnout_fotogalerii_do_org_rezimu.short_description = \ - 'Přepnout do režimu úprav (zneveřejní galerii)' + '''zneverjni vybranou fotogalerii i jeji vsechny podgalerie''' + for galerie in queryset: + galerie.zobrazit = 1 + galerie.save() + prepnout_fotogalerii_do_org_rezimu(modeladmin, request, + Galerie.objects.filter(galerie_up = galerie)) + prepnout_fotogalerii_do_org_rezimu.short_description = \ + 'Přepnout do režimu úprav (zneveřejní galerii)' class GalerieInline(admin.TabularInline): - model = Obrazek - fields = ['obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag'] - readonly_fields = ['nazev', 'obrazek_maly_tag'] - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } + model = Obrazek + fields = ['obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag'] + readonly_fields = ['nazev', 'obrazek_maly_tag'] + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } 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): - form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek']) - model = Galerie - fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') - list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny') - inlines = [GalerieInline] - actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] - save_on_top = True - ordering = ['galerie_up__nazev', 'poradi'] + form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek']) + model = Galerie + fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') + list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny') + inlines = [GalerieInline] + actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] + save_on_top = True + ordering = ['galerie_up__nazev', 'poradi'] admin.site.register(Obrazek, ObrazekAdmin) admin.site.register(Galerie, GalerieAdmin) diff --git a/galerie/forms.py b/galerie/forms.py index 1cadb3fc..e6666884 100644 --- a/galerie/forms.py +++ b/galerie/forms.py @@ -4,8 +4,8 @@ from django import forms from seminar.models import Soustredeni 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): - nazev = forms.CharField(label = "Název galerie", max_length = 100) - #popis = forms.CharField(label = "Popis", required = False, max_length = 2000, widget = forms.Textarea) + nazev = forms.CharField(label = "Název galerie", max_length = 100) + #popis = forms.CharField(label = "Popis", required = False, max_length = 2000, widget = forms.Textarea) diff --git a/galerie/models.py b/galerie/models.py index 133551ec..b6ff4c5c 100644 --- a/galerie/models.py +++ b/galerie/models.py @@ -14,71 +14,71 @@ VZDY=0 ORG=1 NIKDY=2 VIDITELNOST = ( - (VZDY, 'Vždy'), - (ORG, 'Organizátorům'), - (NIKDY, 'Nikdy'), + (VZDY, 'Vždy'), + (ORG, 'Organizátorům'), + (NIKDY, 'Nikdy'), ) # 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 def obrazek_filename_maly(): - pass + pass def obrazek_filename_stredni(): - pass + pass def obrazek_filename_velky(): - pass + pass def obrazek_filename(self, filename): - gal = self.galerie - cislo_gal = force_unicode(gal.pk) + gal = self.galerie + cislo_gal = force_unicode(gal.pk) - # najdi kořenovou galerii - while (gal.galerie_up): - gal = gal.galerie_up + # najdi kořenovou galerii + while (gal.galerie_up): + gal = gal.galerie_up - # soustředění je v cestě jen pokud galerie pod nějaké patří - cesta = ( - ['Galerie'] + - (["soustredeni_" + force_unicode(gal.soustredeni.pk)] if gal.soustredeni else []) + - ["galerie_" + cislo_gal, force_unicode(self.nazev)] - ) + # soustředění je v cestě jen pokud galerie pod nějaké patří + cesta = ( + ['Galerie'] + + (["soustredeni_" + force_unicode(gal.soustredeni.pk)] if gal.soustredeni else []) + + ["galerie_" + cislo_gal, force_unicode(self.nazev)] + ) - return os.path.join(*cesta) + return os.path.join(*cesta) class Obrazek(models.Model): - 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.") - obrazek_stredni = ImageSpecField(source='obrazek_velky', - processors=[Transpose(Transpose.AUTO), ResizeToFit(900, 675, upscale=False)], - options={'quality': 95}) - obrazek_maly = ImageSpecField(source='obrazek_velky', - processors=[Transpose(Transpose.AUTO), ResizeToFit(167, 167, upscale=False)], - options={'quality': 95}) - nazev = models.CharField('Název', max_length=50, blank=True, null=True) - popis = models.TextField('Popis', blank=True, null=True) - datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True) - galerie = models.ForeignKey('Galerie', blank=True, null=True) - poradi = models.IntegerField('Pořadí', blank=True, null=True) - - def __unicode__(self): - return unicode(self.obrazek_velky.name) - - class Meta: - verbose_name = 'Obrázek' - verbose_name_plural = 'Obrázky' - ordering = ['nazev'] - - def obrazek_maly_tag(self): - return u''.format(self.obrazek_maly.url) - obrazek_maly_tag.short_description = "Náhled" - obrazek_maly_tag.allow_tags = True - - def save(self, *args, **kwargs): - # 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í) - if self.nazev is None: - self.nazev = os.path.basename(self.obrazek_velky.name) - super(Obrazek, self).save(*args, **kwargs) + 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.") + obrazek_stredni = ImageSpecField(source='obrazek_velky', + processors=[Transpose(Transpose.AUTO), ResizeToFit(900, 675, upscale=False)], + options={'quality': 95}) + obrazek_maly = ImageSpecField(source='obrazek_velky', + processors=[Transpose(Transpose.AUTO), ResizeToFit(167, 167, upscale=False)], + options={'quality': 95}) + nazev = models.CharField('Název', max_length=50, blank=True, null=True) + popis = models.TextField('Popis', blank=True, null=True) + datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True) + galerie = models.ForeignKey('Galerie', blank=True, null=True) + poradi = models.IntegerField('Pořadí', blank=True, null=True) + + def __unicode__(self): + return unicode(self.obrazek_velky.name) + + class Meta: + verbose_name = 'Obrázek' + verbose_name_plural = 'Obrázky' + ordering = ['nazev'] + + def obrazek_maly_tag(self): + return u''.format(self.obrazek_maly.url) + obrazek_maly_tag.short_description = "Náhled" + obrazek_maly_tag.allow_tags = True + + def save(self, *args, **kwargs): + # 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í) + if self.nazev is None: + self.nazev = os.path.basename(self.obrazek_velky.name) + super(Obrazek, self).save(*args, **kwargs) @@ -94,29 +94,29 @@ class Galerie(models.Model): poradi = models.IntegerField('Pořadí', blank = True, null = True) def __unicode__(self): - return self.nazev + return self.nazev class Meta: - verbose_name = 'Galerie' - verbose_name_plural = 'Galerie' + verbose_name = 'Galerie' + verbose_name_plural = 'Galerie' #def link_na_preview(self): - #"""Odkaz na galerii, používá se v admin rozhranní. """ - #return 'Preview' % self.id + #"""Odkaz na galerii, používá se v admin rozhranní. """ + #return 'Preview' % self.id #link_na_preview.allow_tags = True #link_na_preview.short_description = 'Zobrazit galerii' # #def je_publikovano(self): - #"""Vraci True, pokud je tato galerie publikovana. """ - #if self.zobrazit == VZDY: - #return True - #if self.zobrazit == PODLE_CLANKU: - #for clanek in self.clanek_set.all(): - #if clanek.je_publikovano(): - #return True - #return False + #"""Vraci True, pokud je tato galerie publikovana. """ + #if self.zobrazit == VZDY: + #return True + #if self.zobrazit == PODLE_CLANKU: + #for clanek in self.clanek_set.all(): + #if clanek.je_publikovano(): + #return True + #return False # #@staticmethod #def publikovane_galerie(): - #"""Vraci galerie, ktere uz maji byt publikovane.""" - #clanky = Blog.models.Clanek.publikovane_clanky() - #return Galerie.objects.filter(Q(zobrazit=VZDY) | (Q(clanek__in=clanky) & Q(zobrazit=PODLE_CLANKU))).distinct() + #"""Vraci galerie, ktere uz maji byt publikovane.""" + #clanky = Blog.models.Clanek.publikovane_clanky() + #return Galerie.objects.filter(Q(zobrazit=VZDY) | (Q(clanek__in=clanky) & Q(zobrazit=PODLE_CLANKU))).distinct() diff --git a/galerie/urls.py b/galerie/urls.py index 4b015e44..487ddc22 100644 --- a/galerie/urls.py +++ b/galerie/urls.py @@ -4,10 +4,10 @@ from django.conf.urls import include, url from . import views urlpatterns = [ - url(r'^(?P\d+)/$', views.nahled), - url(r'^(?P\d+)/(?P\d+)/$', views.detail), - url(r'^(?P\d+)/new/$', views.new_galerie), - url(r'^(?P\d+)/plus/(?P\d+)/$', views.plus_galerie), - url(r'^(?P\d+)/minus/(?P\d+)/$', views.minus_galerie), + url(r'^(?P\d+)/$', views.nahled), + url(r'^(?P\d+)/(?P\d+)/$', views.detail), + url(r'^(?P\d+)/new/$', views.new_galerie), + url(r'^(?P\d+)/plus/(?P\d+)/$', views.plus_galerie), + url(r'^(?P\d+)/minus/(?P\d+)/$', views.minus_galerie), ] diff --git a/galerie/views.py b/galerie/views.py index 46860810..666f3da5 100644 --- a/galerie/views.py +++ b/galerie/views.py @@ -12,64 +12,64 @@ from seminar.models import Soustredeni from galerie.forms import KomentarForm, NewGalerieForm def zobrazit(galerie, request): - preview = False - if galerie.zobrazit >= 1: - if request.user.is_staff: - preview = True; - else: - raise Http404 - return preview + preview = False + if galerie.zobrazit >= 1: + if request.user.is_staff: + preview = True; + else: + raise Http404 + return preview def cesta_od_korene(g): - """Vrátí seznam galerií od kořene ke g""" - cesta = [] - while g != None: - cesta.append(g) - g = g.galerie_up - return reversed(cesta) + """Vrátí seznam galerií od kořene ke g""" + cesta = [] + while g != None: + cesta.append(g) + g = g.galerie_up + return reversed(cesta) def nahled(request, pk, soustredeni): - """Zobrazeni nahledu vsech fotek ve skupine.""" - galerie = get_object_or_404(Galerie, pk=pk) - - podgalerie = Galerie.objects.filter(galerie_up = galerie).order_by('poradi') - if not request.user.is_staff: - podgalerie = podgalerie.filter(zobrazit__lt=1) - - obrazky = Obrazek.objects.filter(galerie = galerie) - preview = zobrazit(galerie, request) - - sourozenci = [] - if galerie.galerie_up: - sourozenci = galerie.galerie_up.galerie_set.all().order_by('poradi') - if not request.user.is_staff: - sourozenci = sourozenci.filter(zobrazit__lt=1) - - predchozi = None - nasledujici = None - minuly = None - for g in sourozenci: - if g.pk == galerie.pk: - predchozi = minuly - if minuly != None and minuly.pk == galerie.pk: - nasledujici = g - break - minuly = g - - cesta = cesta_od_korene(galerie) - - return render(request, 'galerie/GalerieNahled.html', - {'galerie' : galerie, - 'podgalerie' : podgalerie, - 'obrazky' : obrazky, - 'preview' : preview, - 'cesta': cesta, - 'sourozenci': sourozenci, - 'predchozi': predchozi, - 'nasledujici': nasledujici, - }) + """Zobrazeni nahledu vsech fotek ve skupine.""" + galerie = get_object_or_404(Galerie, pk=pk) + + podgalerie = Galerie.objects.filter(galerie_up = galerie).order_by('poradi') + if not request.user.is_staff: + podgalerie = podgalerie.filter(zobrazit__lt=1) + + obrazky = Obrazek.objects.filter(galerie = galerie) + preview = zobrazit(galerie, request) + + sourozenci = [] + if galerie.galerie_up: + sourozenci = galerie.galerie_up.galerie_set.all().order_by('poradi') + if not request.user.is_staff: + sourozenci = sourozenci.filter(zobrazit__lt=1) + + predchozi = None + nasledujici = None + minuly = None + for g in sourozenci: + if g.pk == galerie.pk: + predchozi = minuly + if minuly != None and minuly.pk == galerie.pk: + nasledujici = g + break + minuly = g + + cesta = cesta_od_korene(galerie) + + return render(request, 'galerie/GalerieNahled.html', + {'galerie' : galerie, + 'podgalerie' : podgalerie, + 'obrazky' : obrazky, + 'preview' : preview, + 'cesta': cesta, + 'sourozenci': sourozenci, + 'predchozi': predchozi, + 'nasledujici': nasledujici, + }) def detail(request, pk, fotka, soustredeni): """Zobrazeni nahledu fotky s id 'fotka'.""" @@ -86,21 +86,21 @@ def detail(request, pk, fotka, soustredeni): # vytvoreni a obslouzeni formulare if request.method == 'POST': - form = KomentarForm(request.POST) - if form.is_valid(): - obrazek.popis = form.cleaned_data['komentar'] - obrazek.save() + form = KomentarForm(request.POST) + if form.is_valid(): + obrazek.popis = form.cleaned_data['komentar'] + obrazek.save() else: - form = KomentarForm({'komentar': obrazek.popis}) + form = KomentarForm({'komentar': obrazek.popis}) # Poradi aktualniho obrazku v galerii/stitku. for i in range(len(obrazky)): - if obrazky[i] == obrazek: - znacka = i - break + if obrazky[i] == obrazek: + znacka = i + break else: - # Obrazek neni v galerii/stitku. - raise Http404 + # Obrazek neni v galerii/stitku. + raise Http404 # Nacteni okolnich obrazku a galerii @@ -110,25 +110,25 @@ def detail(request, pk, fotka, soustredeni): nasledujici_galerie = None obrazky_dalsi = obrazky[znacka+1:znacka+NAHLEDU+1] if (znacka+1) > NAHLEDU: - obrazky_predchozi = obrazky[znacka-NAHLEDU:znacka] + obrazky_predchozi = obrazky[znacka-NAHLEDU:znacka] else: - obrazky_predchozi = obrazky[0:znacka] - if galerie.poradi > 1: - predchozi_galerie = Galerie.objects.\ - filter(galerie_up=galerie.galerie_up).\ - filter(poradi=(galerie.poradi-1)) - if predchozi_galerie: - predchozi_galerie = predchozi_galerie[0] - else: - predchozi_galerie = None + obrazky_predchozi = obrazky[0:znacka] + if galerie.poradi > 1: + predchozi_galerie = Galerie.objects.\ + filter(galerie_up=galerie.galerie_up).\ + filter(poradi=(galerie.poradi-1)) + if predchozi_galerie: + predchozi_galerie = predchozi_galerie[0] + else: + predchozi_galerie = None if (znacka+1) == len(obrazky): - nasledujici_galerie = Galerie.objects.\ - filter(galerie_up=galerie.galerie_up).\ - filter(poradi=(galerie.poradi+1)) - if nasledujici_galerie: - nasledujici_galerie = nasledujici_galerie[0] - else: - nasledujici_galerie = None + nasledujici_galerie = Galerie.objects.\ + filter(galerie_up=galerie.galerie_up).\ + filter(poradi=(galerie.poradi+1)) + if nasledujici_galerie: + nasledujici_galerie = nasledujici_galerie[0] + else: + nasledujici_galerie = None @@ -136,98 +136,98 @@ def detail(request, pk, fotka, soustredeni): vyska = obrazek.obrazek_stredni.height sirka = obrazek.obrazek_stredni.width if vyska > MAX_VYSKA: - sirka = sirka * MAX_VYSKA / vyska - vyska = MAX_VYSKA + sirka = sirka * MAX_VYSKA / vyska + vyska = MAX_VYSKA if sirka > MAX_SIRKA: - vyska = vyska * MAX_SIRKA / sirka - sirka = MAX_SIRKA + vyska = vyska * MAX_SIRKA / sirka + sirka = MAX_SIRKA return render(request, 'galerie/Galerie.html', - {'galerie' : galerie, - 'predchozi_galerie' : predchozi_galerie, - 'nasledujici_galerie' : nasledujici_galerie, - 'obrazek' : obrazek, - 'vyska' : vyska, - 'sirka' : sirka, - 'obrazky_predchozi' : obrazky_predchozi, - 'obrazky_dalsi' : obrazky_dalsi, - 'preview' : preview, - 'form' : form, - 'cesta': cesta_od_korene(galerie), - }) + {'galerie' : galerie, + 'predchozi_galerie' : predchozi_galerie, + 'nasledujici_galerie' : nasledujici_galerie, + 'obrazek' : obrazek, + 'vyska' : vyska, + 'sirka' : sirka, + 'obrazky_predchozi' : obrazky_predchozi, + 'obrazky_dalsi' : obrazky_dalsi, + 'preview' : preview, + 'form' : form, + 'cesta': cesta_od_korene(galerie), + }) def new_galerie(request, galerie, soustredeni): - # zjistime k jakemu soustredeni se vaze nove vytvarena galerie - soustredeni = get_object_or_404(Soustredeni, pk = soustredeni) - # pokud je parametr galerie 0, pak jde o hlavni galerii - # kdyz je nejaky jiny, pak je pk galerie pod kterou tu dalsi vytvarim - if int(galerie) == 0: - galerie_up = False - galerie_text = "Hlavní fotogalerie soustředění" - else: - galerie_up = get_object_or_404(Galerie, pk = int(galerie)) - galerie_text = "podgalerii ke galerii " + str(galerie_up) - - # obsluha formulare umoznujiciho multiple nahravani fotek - if request.method == 'POST': - form = NewGalerieForm(request.POST, request.FILES) - if form.is_valid(): - # vytvoreni nove galerie - gal = Galerie() - gal.nazev = form.cleaned_data['nazev'] - #gal.popis = form.cleaned_data['popis'] # popis nepouzivame - gal.zobrazit = 1 # galerie je v procesu vytvareni - ''' pokud je to podgalerie pridej nadrazenou galerii - a nadrazene soustredeni nechej volne, - pokud je to hlavni galerie, tak nadrazena galerie neexistuje, - ale v takovem pripade musi byt nadrazene soustredeni a ne jinak ''' - if galerie_up: - gal.galerie_up = galerie_up - else: - gal.soustredeni = soustredeni - if gal.galerie_up: - gal.poradi = int(len(gal.galerie_up.galerie_set.all())) + 1 - gal.save() - - # zpracovani obrazku v galerii - for obr in request.FILES.getlist('obr'): - o = Obrazek() - o.obrazek_velky = obr - o.nazev = str(obr) - o.galerie = gal - o.save() - - # presmerovani na prave vzniklou galerii - return HttpResponseRedirect('../../' + str(gal.pk)) - - else: - form = NewGalerieForm() - - - return render(request, 'galerie/GalerieNew.html', - { - 'form' : form, - 'soustredeni' : soustredeni, - 'galerie_text' : galerie_text, - }) + # zjistime k jakemu soustredeni se vaze nove vytvarena galerie + soustredeni = get_object_or_404(Soustredeni, pk = soustredeni) + # pokud je parametr galerie 0, pak jde o hlavni galerii + # kdyz je nejaky jiny, pak je pk galerie pod kterou tu dalsi vytvarim + if int(galerie) == 0: + galerie_up = False + galerie_text = "Hlavní fotogalerie soustředění" + else: + galerie_up = get_object_or_404(Galerie, pk = int(galerie)) + galerie_text = "podgalerii ke galerii " + str(galerie_up) + + # obsluha formulare umoznujiciho multiple nahravani fotek + if request.method == 'POST': + form = NewGalerieForm(request.POST, request.FILES) + if form.is_valid(): + # vytvoreni nove galerie + gal = Galerie() + gal.nazev = form.cleaned_data['nazev'] + #gal.popis = form.cleaned_data['popis'] # popis nepouzivame + gal.zobrazit = 1 # galerie je v procesu vytvareni + ''' pokud je to podgalerie pridej nadrazenou galerii + a nadrazene soustredeni nechej volne, + pokud je to hlavni galerie, tak nadrazena galerie neexistuje, + ale v takovem pripade musi byt nadrazene soustredeni a ne jinak ''' + if galerie_up: + gal.galerie_up = galerie_up + else: + gal.soustredeni = soustredeni + if gal.galerie_up: + gal.poradi = int(len(gal.galerie_up.galerie_set.all())) + 1 + gal.save() + + # zpracovani obrazku v galerii + for obr in request.FILES.getlist('obr'): + o = Obrazek() + o.obrazek_velky = obr + o.nazev = str(obr) + o.galerie = gal + o.save() + + # presmerovani na prave vzniklou galerii + return HttpResponseRedirect('../../' + str(gal.pk)) + + else: + form = NewGalerieForm() + + + return render(request, 'galerie/GalerieNew.html', + { + 'form' : form, + 'soustredeni' : soustredeni, + 'galerie_text' : galerie_text, + }) def plus_galerie(request, galerie, soustredeni, subgalerie): - galerie = get_object_or_404(Galerie, pk=subgalerie) - if galerie.poradi: - galerie.poradi += 1 - else: - galerie.poradi = int(len(galerie.galerie_up.galerie_set.all())) - galerie.save() - return HttpResponseRedirect('../../') + galerie = get_object_or_404(Galerie, pk=subgalerie) + if galerie.poradi: + galerie.poradi += 1 + else: + galerie.poradi = int(len(galerie.galerie_up.galerie_set.all())) + galerie.save() + return HttpResponseRedirect('../../') def minus_galerie(request, galerie, soustredeni, subgalerie): - galerie = get_object_or_404(Galerie, pk=subgalerie) - if galerie.poradi: - galerie.poradi -= 1 - else: - galerie.poradi = 1 - galerie.save() - return HttpResponseRedirect('../../') + galerie = get_object_or_404(Galerie, pk=subgalerie) + if galerie.poradi: + galerie.poradi -= 1 + else: + galerie.poradi = 1 + galerie.save() + return HttpResponseRedirect('../../') diff --git a/korektury/admin.py b/korektury/admin.py index 25b49e4e..79dfffb1 100644 --- a/korektury/admin.py +++ b/korektury/admin.py @@ -6,21 +6,21 @@ from korektury.models import KorekturovanePDF # Register your models here. class KorekturovanePDFAdmin(VersionAdmin): - readonly_fields = ['cas', 'stran'] + readonly_fields = ['cas', 'stran'] - def get_readonly_fields(self, request, obj=None): - if obj: - return self.readonly_fields + ['pdf'] - return self.readonly_fields + def get_readonly_fields(self, request, obj=None): + if obj: + return self.readonly_fields + ['pdf'] + return self.readonly_fields - fieldsets = [ - (None, - {'fields': - ['pdf', 'cas', 'org', 'stran', 'nazev', 'komentar']}), - # (u'PDF', {'fields': ['pdf']}), - ] - list_display = ['nazev', 'cas', 'stran', 'org'] - list_filter = [] - search_fields = [] + fieldsets = [ + (None, + {'fields': + ['pdf', 'cas', 'org', 'stran', 'nazev', 'komentar']}), + # (u'PDF', {'fields': ['pdf']}), + ] + list_display = ['nazev', 'cas', 'stran', 'org'] + list_filter = [] + search_fields = [] admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin) diff --git a/korektury/forms.py b/korektury/forms.py index 676d6b3a..7385e687 100644 --- a/korektury/forms.py +++ b/korektury/forms.py @@ -1,13 +1,13 @@ from django import forms class OpravaForm(forms.Form): - text = forms.CharField(max_length=256) - autor = forms.CharField(max_length=20) - x = forms.IntegerField() - y = forms.IntegerField() - scroll = forms.CharField(max_length=256) - pdf = forms.CharField(max_length=256) - img_id = forms.CharField(max_length=256) - id = forms.CharField(max_length=256) - action = forms.CharField(max_length=256) + text = forms.CharField(max_length=256) + autor = forms.CharField(max_length=20) + x = forms.IntegerField() + y = forms.IntegerField() + scroll = forms.CharField(max_length=256) + pdf = forms.CharField(max_length=256) + img_id = forms.CharField(max_length=256) + id = forms.CharField(max_length=256) + action = forms.CharField(max_length=256) diff --git a/korektury/models.py b/korektury/models.py index 47094f89..9b7929ce 100644 --- a/korektury/models.py +++ b/korektury/models.py @@ -17,171 +17,171 @@ from unidecode import unidecode def generate_filename(self, filename): - clean = get_valid_filename( - unidecode( - filename.replace('/', '-').replace('\0', '').replace(":", "_") - ) - ) - fname = "%s_%s" % ( - timezone.now().strftime('%Y-%m-%d-%H_%M'), - clean) - return os.path.join(settings.KOREKTURY_PDF_DIR, fname) + clean = get_valid_filename( + unidecode( + filename.replace('/', '-').replace('\0', '').replace(":", "_") + ) + ) + fname = "%s_%s" % ( + timezone.now().strftime('%Y-%m-%d-%H_%M'), + clean) + return os.path.join(settings.KOREKTURY_PDF_DIR, fname) #@reversion.register(ignore_duplicates=True) #@python_2_unicode_compatible class KorekturovanePDF(models.Model): - class Meta: - ordering = ['-cas'] - db_table = 'korekturovane_cislo' - verbose_name = u'PDF k opravám' - verbose_name_plural = u'PDF k opravám' - - #Interní ID - id = models.AutoField(primary_key = True) - - 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') - - 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) - - org = models.ForeignKey(Organizator, blank=True, - help_text='Zodpovědný organizátor za obsah', - null=True, - default=None) - - stran = models.IntegerField(u'počet stran', help_text='Počet stran PDF', - default=0) - STATUS_PRIDAVANI = 'pridavani' - STATUS_ZANASENI = 'zanaseni' - STATUS_ZASTARALE = 'zastarale' - STATUS_CHOICES = ( - (STATUS_PRIDAVANI, u'Přidávání korektur'), - (STATUS_ZANASENI, u'Korektury jsou zanášeny'), - (STATUS_ZASTARALE, u'Stará verze, nekorigovat'), - ) - status = models.CharField(u'stav PDF',max_length=16, choices=STATUS_CHOICES, blank=False, - default = STATUS_PRIDAVANI) + class Meta: + ordering = ['-cas'] + db_table = 'korekturovane_cislo' + verbose_name = u'PDF k opravám' + verbose_name_plural = u'PDF k opravám' + + #Interní ID + id = models.AutoField(primary_key = True) + + 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') + + 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) + + org = models.ForeignKey(Organizator, blank=True, + help_text='Zodpovědný organizátor za obsah', + null=True, + default=None) + + stran = models.IntegerField(u'počet stran', help_text='Počet stran PDF', + default=0) + STATUS_PRIDAVANI = 'pridavani' + STATUS_ZANASENI = 'zanaseni' + STATUS_ZASTARALE = 'zastarale' + STATUS_CHOICES = ( + (STATUS_PRIDAVANI, u'Přidávání korektur'), + (STATUS_ZANASENI, u'Korektury jsou zanášeny'), + (STATUS_ZASTARALE, u'Stará verze, nekorigovat'), + ) + status = models.CharField(u'stav PDF',max_length=16, choices=STATUS_CHOICES, blank=False, + default = STATUS_PRIDAVANI) - #TODO Nepovinný foreign key k číslu - - def get_prefix(self): - """Vrať řetězec, ke kterému se připojí číslo a .png""" - # vrátíme jméno souboru bez cesty - return os.path.basename(self.pdf.file.name) - - def convert(self): - """Vytvoří jedno png za každou stranu pdf a uloží se""" - dirname = os.path.join(settings.MEDIA_ROOT, settings.KOREKTURY_IMG_DIR) - if not os.path.exists(dirname): - os.mkdir(dirname) - self.stran = 0 - while True: - res = subprocess.call([ - "convert", - "-density", "180x180", - "-geometry", " 1024x1448", - "%s[%d]" % (self.pdf.path, self.stran), - os.path.join( - dirname, - "%s-%d.png" % (self.get_prefix(), self.stran) - ) - ]) - if res == 1: - break - self.stran += 1 - # Změnil se počet stran, ukládáme - super(KorekturovanePDF, self).save() - - def save(self): - # Pokud se nezmenilo PDF, tak nepregenerovavej nahledy - try: - original = KorekturovanePDF.objects.get(pk=self.pk) - if original.pdf == self.pdf: - super(KorekturovanePDF, self).save() - return - except ObjectDoesNotExist: - pass - # uložíme nahrávané pdf - super(KorekturovanePDF, self).save() - - # uložíme png a změněný počet stran - self.convert() + #TODO Nepovinný foreign key k číslu + + def get_prefix(self): + """Vrať řetězec, ke kterému se připojí číslo a .png""" + # vrátíme jméno souboru bez cesty + return os.path.basename(self.pdf.file.name) + + def convert(self): + """Vytvoří jedno png za každou stranu pdf a uloží se""" + dirname = os.path.join(settings.MEDIA_ROOT, settings.KOREKTURY_IMG_DIR) + if not os.path.exists(dirname): + os.mkdir(dirname) + self.stran = 0 + while True: + res = subprocess.call([ + "convert", + "-density", "180x180", + "-geometry", " 1024x1448", + "%s[%d]" % (self.pdf.path, self.stran), + os.path.join( + dirname, + "%s-%d.png" % (self.get_prefix(), self.stran) + ) + ]) + if res == 1: + break + self.stran += 1 + # Změnil se počet stran, ukládáme + super(KorekturovanePDF, self).save() + + def save(self): + # Pokud se nezmenilo PDF, tak nepregenerovavej nahledy + try: + original = KorekturovanePDF.objects.get(pk=self.pk) + if original.pdf == self.pdf: + super(KorekturovanePDF, self).save() + return + except ObjectDoesNotExist: + pass + # uložíme nahrávané pdf + super(KorekturovanePDF, self).save() + + # uložíme png a změněný počet stran + self.convert() @reversion.register(ignore_duplicates=True) @python_2_unicode_compatible class Oprava(models.Model): - class Meta: - db_table = 'opravy' - verbose_name = u'Oprava' - verbose_name_plural = u'Opravy' - ordering = ['y','x'] - - #Interní ID - id = models.AutoField(primary_key = True) - - pdf = models.ForeignKey(KorekturovanePDF, default=-1) - - strana = models.IntegerField(u'strana s opravou', help_text='Strana s opravou (od 0)') - - x = models.IntegerField(u'x-ová souřadnice bugu') - y = models.IntegerField(u'y-ová souřadnice bugu') - - STATUS_K_OPRAVE = 'k_oprave' - STATUS_OPRAVENO = 'opraveno' - STATUS_NENI_CHYBA = 'neni_chyba' - STATUS_K_ZANESENI = 'k_zaneseni' - STATUS_CHOICES = ( - (STATUS_K_OPRAVE, u'K opravě'), - (STATUS_OPRAVENO, u'Opraveno'), - (STATUS_NENI_CHYBA, u'Není chyba'), - (STATUS_K_ZANESENI, u'K zanesení do TeXu'), - ) - status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False, - default = STATUS_K_OPRAVE) - - autor = models.ForeignKey(Organizator, blank = True, - help_text='Autor opravy', - null = True) - - text = models.TextField(u'text opravy',blank = True, help_text='Text opravy') + class Meta: + db_table = 'opravy' + verbose_name = u'Oprava' + verbose_name_plural = u'Opravy' + ordering = ['y','x'] + + #Interní ID + id = models.AutoField(primary_key = True) + + pdf = models.ForeignKey(KorekturovanePDF, default=-1) + + strana = models.IntegerField(u'strana s opravou', help_text='Strana s opravou (od 0)') + + x = models.IntegerField(u'x-ová souřadnice bugu') + y = models.IntegerField(u'y-ová souřadnice bugu') + + STATUS_K_OPRAVE = 'k_oprave' + STATUS_OPRAVENO = 'opraveno' + STATUS_NENI_CHYBA = 'neni_chyba' + STATUS_K_ZANESENI = 'k_zaneseni' + STATUS_CHOICES = ( + (STATUS_K_OPRAVE, u'K opravě'), + (STATUS_OPRAVENO, u'Opraveno'), + (STATUS_NENI_CHYBA, u'Není chyba'), + (STATUS_K_ZANESENI, u'K zanesení do TeXu'), + ) + status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False, + default = STATUS_K_OPRAVE) + + autor = models.ForeignKey(Organizator, blank = True, + help_text='Autor opravy', + null = True) + + text = models.TextField(u'text opravy',blank = True, help_text='Text opravy') # def __init__(self,dictionary): # for k,v in dictionary.items(): # setattr(self,k,v) - def __str__(self): - return force_unicode(u'%s od %s: %s'%(self.status,self.autor,self.text)) + def __str__(self): + return force_unicode(u'%s od %s: %s'%(self.status,self.autor,self.text)) @reversion.register(ignore_duplicates=True) @python_2_unicode_compatible class Komentar(models.Model): - class Meta: - db_table = 'komentare' - verbose_name = u'Komentář k opravě' - verbose_name_plural = u'Komentáře k opravě' - ordering = ['cas'] - - #Interní ID - id = models.AutoField(primary_key = True) - - cas = models.DateTimeField(u'čas komentáře',default=timezone.now,help_text = 'Čas zadání komentáře') - - oprava = models.ForeignKey(Oprava) - autor = models.ForeignKey(Organizator, blank = True, - help_text = u'Autor komentáře', - null = True) - - text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře') - - def __str__(self): - return force_unicode(u'%s od %s: %s'%(self.cas,self.autor,self.text)) + class Meta: + db_table = 'komentare' + verbose_name = u'Komentář k opravě' + verbose_name_plural = u'Komentáře k opravě' + ordering = ['cas'] + + #Interní ID + id = models.AutoField(primary_key = True) + + cas = models.DateTimeField(u'čas komentáře',default=timezone.now,help_text = 'Čas zadání komentáře') + + oprava = models.ForeignKey(Oprava) + autor = models.ForeignKey(Organizator, blank = True, + help_text = u'Autor komentáře', + null = True) + + text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře') + + def __str__(self): + return force_unicode(u'%s od %s: %s'%(self.cas,self.autor,self.text)) diff --git a/korektury/urls.py b/korektury/urls.py index 2f131516..a1fe8469 100644 --- a/korektury/urls.py +++ b/korektury/urls.py @@ -6,7 +6,7 @@ from . import views staff_member_required = user_passes_test(lambda u: u.is_staff) urlpatterns = [ - url(r'^korektury/$', staff_member_required(views.KorekturyListView.as_view()), name='korektury-list'), - url(r'^korektury/(?P\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/$', staff_member_required(views.KorekturyListView.as_view()), name='korektury-list'), + url(r'^korektury/(?P\d+)/$', staff_member_required(views.KorekturyView.as_view()), name='korektury'), + url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'), ] diff --git a/korektury/views.py b/korektury/views.py index c8abbd9e..fe2577bb 100644 --- a/korektury/views.py +++ b/korektury/views.py @@ -15,186 +15,186 @@ import os import unicodedata class KorekturyHelpView(generic.TemplateView): - template_name = 'korektury/help.html' + template_name = 'korektury/help.html' class KorekturyListView(generic.ListView): - model = KorekturovanePDF - template_name = 'korektury/seznam.html' + model = KorekturovanePDF + template_name = 'korektury/seznam.html' ### Korektury class KorekturyView(generic.TemplateView): - model = Oprava - template_name = 'korektury/opraf.html' - form_class = OpravaForm - - def post(self, request, *args, **kwargs): - form = self.form_class(request.POST) - q = request.POST - scroll = q.get('scroll') - - # prirazeni autora podle prihlaseni - autor_user = request.user - # pokud existuje ucet (user), ale neni to organizator = 403 - autor = Organizator.objects.filter(user=autor_user).first() - if not autor: - return HttpResponseForbidden() - - if not scroll: - scroll = 0 - - action = q.get('action') - if (action == ''): # Přidej - x = int(q.get('x')) - y = int(q.get('y')) - text = q.get('txt') - strana = int(q.get('img-id')[4:]) - pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) - - op = Oprava(x=x,y=y, autor=autor, text=text, strana=strana,pdf = pdf) - op.save() - self.send_email_notification_komentar(op, autor, text) - elif (action == 'del'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - op.delete() - elif (action == 'update'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - text = q.get('txt') - op.autor = autor - op.text = text - op.save() - elif (action == 'undone'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - op.status = op.STATUS_K_OPRAVE - op.save() - elif (action == 'done'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - op.status = op.STATUS_OPRAVENO - op.save() - elif (action == 'ready'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - op.status = op.STATUS_K_ZANESENI - op.save() - elif (action == 'wontfix'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - op.status = op.STATUS_NENI_CHYBA - op.save() - elif (action == 'comment'): - id = int(q.get('id')) - op = Oprava.objects.get(id=id) - text = q.get('txt') - kom = Komentar(oprava=op,autor=autor,text=text) - kom.save() - self.send_email_notification_komentar(op, autor, text) - elif (action == 'update-comment'): - id = int(q.get('id')) - kom = Komentar.objects.get(id=id) - text = q.get('txt') - kom.text = text - kom.autor = autor - kom.save() - elif (action == 'del-comment'): - id = int(q.get('id')) - kom = Komentar.objects.get(id=id) - kom.delete() - elif (action == 'set-state'): - pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) - if (q.get('state') == 'adding'): - pdf.status = pdf.STATUS_PRIDAVANI - elif (q.get('state') == 'comitting'): - pdf.status = pdf.STATUS_ZANASENI - elif (q.get('state') == 'deprecated'): - pdf.status = pdf.STATUS_ZASTARALE - pdf.save() - context = self.get_context_data() - context['scroll'] = scroll - context['autor'] = autor - return render(request, 'korektury/opraf.html',context) - - def send_email_notification_komentar(self, oprava, autor, text): - ''' Rozesle e-mail pri pridani komentare, - ktery obsahuje text komentare. - ''' - - # parametry e-mailu - odkaz = "https://mam.mff.cuni.cz/korektury/{}/".format(oprava.pdf.pk) - from_email = 'korekturovatko@mam.mff.cuni.cz' - subject = 'Nová korektura od {} v {}'.format(autor, - oprava.pdf.nazev) - text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\ - \nodkaz do korekturovátka: {}\n\ - \nVaše korekturovátko\n".format(text, odkaz) - - # Prijemci e-mailu - emails = set() - # e-mail autora korektury - email = oprava.autor.user.email - if email: - emails.add(email) - - # nalezeni e-mailu na autory komentaru - for komentar in oprava.komentar_set.all(): - email_komentujiciho = komentar.autor.user.email - if email_komentujiciho: - emails.add(email_komentujiciho) - - # zodpovedny org - if oprava.pdf.org: - email_zobpovedny = oprava.pdf.org.user.email - if email_zobpovedny: - emails.add(email_zobpovedny) - - # odstran e-mail autora opravy - email = autor.user.email - if email: - emails.discard(email) - - if not settings.SEND_EMAIL_NOTIFICATIONS: - print("Poslal bych upozornění na tyto adresy: ", " ".join(emails)) - return - - send_mail(subject, text, from_email, list(emails)) - - def get_context_data(self, **kwargs): - context = super(KorekturyView,self).get_context_data(**kwargs) - pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf']) - context['pdf'] = pdf - context['img_prefix'] = pdf.get_prefix() - context['img_path'] = settings.KOREKTURY_IMG_DIR - context['img_indexes'] = range(pdf.stran) - context['form_oprava'] = OpravaForm() - opravy = Oprava.objects.filter(pdf=self.kwargs['pdf']) - zasluhy = {} - for o in opravy: - if o.autor in zasluhy: - zasluhy[o.autor]+=1 - else: - zasluhy[o.autor]=1 - o.komentare = o.komentar_set.all() - for k in o.komentare: - if k.autor in zasluhy: - zasluhy[k.autor] += 1 - else: - zasluhy[k.autor] = 1 - zasluhy = [ - {'autor': jmeno, 'pocet': pocet} - for (jmeno, pocet) in zasluhy.items() - ] - zasluhy.sort(key=lambda z: z['pocet'], reverse=True) - - strany = set(o.strana for o in opravy) - opravy_na_stranu = [{'strana': s, 'op_id': opravy.filter(strana=s)} for s in strany] - context['opravy_strany'] = opravy_na_stranu - - context['opravy'] = opravy - context['zasluhy'] = zasluhy - return context - - def form_valid(self,form): - return super(KorekturyView,self).form_valid(form) + model = Oprava + template_name = 'korektury/opraf.html' + form_class = OpravaForm + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST) + q = request.POST + scroll = q.get('scroll') + + # prirazeni autora podle prihlaseni + autor_user = request.user + # pokud existuje ucet (user), ale neni to organizator = 403 + autor = Organizator.objects.filter(user=autor_user).first() + if not autor: + return HttpResponseForbidden() + + if not scroll: + scroll = 0 + + action = q.get('action') + if (action == ''): # Přidej + x = int(q.get('x')) + y = int(q.get('y')) + text = q.get('txt') + strana = int(q.get('img-id')[4:]) + pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) + + op = Oprava(x=x,y=y, autor=autor, text=text, strana=strana,pdf = pdf) + op.save() + self.send_email_notification_komentar(op, autor, text) + elif (action == 'del'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + op.delete() + elif (action == 'update'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + text = q.get('txt') + op.autor = autor + op.text = text + op.save() + elif (action == 'undone'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + op.status = op.STATUS_K_OPRAVE + op.save() + elif (action == 'done'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + op.status = op.STATUS_OPRAVENO + op.save() + elif (action == 'ready'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + op.status = op.STATUS_K_ZANESENI + op.save() + elif (action == 'wontfix'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + op.status = op.STATUS_NENI_CHYBA + op.save() + elif (action == 'comment'): + id = int(q.get('id')) + op = Oprava.objects.get(id=id) + text = q.get('txt') + kom = Komentar(oprava=op,autor=autor,text=text) + kom.save() + self.send_email_notification_komentar(op, autor, text) + elif (action == 'update-comment'): + id = int(q.get('id')) + kom = Komentar.objects.get(id=id) + text = q.get('txt') + kom.text = text + kom.autor = autor + kom.save() + elif (action == 'del-comment'): + id = int(q.get('id')) + kom = Komentar.objects.get(id=id) + kom.delete() + elif (action == 'set-state'): + pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) + if (q.get('state') == 'adding'): + pdf.status = pdf.STATUS_PRIDAVANI + elif (q.get('state') == 'comitting'): + pdf.status = pdf.STATUS_ZANASENI + elif (q.get('state') == 'deprecated'): + pdf.status = pdf.STATUS_ZASTARALE + pdf.save() + context = self.get_context_data() + context['scroll'] = scroll + context['autor'] = autor + return render(request, 'korektury/opraf.html',context) + + def send_email_notification_komentar(self, oprava, autor, text): + ''' Rozesle e-mail pri pridani komentare, + ktery obsahuje text komentare. + ''' + + # parametry e-mailu + odkaz = "https://mam.mff.cuni.cz/korektury/{}/".format(oprava.pdf.pk) + from_email = 'korekturovatko@mam.mff.cuni.cz' + subject = 'Nová korektura od {} v {}'.format(autor, + oprava.pdf.nazev) + text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\ + \nodkaz do korekturovátka: {}\n\ + \nVaše korekturovátko\n".format(text, odkaz) + + # Prijemci e-mailu + emails = set() + # e-mail autora korektury + email = oprava.autor.user.email + if email: + emails.add(email) + + # nalezeni e-mailu na autory komentaru + for komentar in oprava.komentar_set.all(): + email_komentujiciho = komentar.autor.user.email + if email_komentujiciho: + emails.add(email_komentujiciho) + + # zodpovedny org + if oprava.pdf.org: + email_zobpovedny = oprava.pdf.org.user.email + if email_zobpovedny: + emails.add(email_zobpovedny) + + # odstran e-mail autora opravy + email = autor.user.email + if email: + emails.discard(email) + + if not settings.SEND_EMAIL_NOTIFICATIONS: + print("Poslal bych upozornění na tyto adresy: ", " ".join(emails)) + return + + send_mail(subject, text, from_email, list(emails)) + + def get_context_data(self, **kwargs): + context = super(KorekturyView,self).get_context_data(**kwargs) + pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf']) + context['pdf'] = pdf + context['img_prefix'] = pdf.get_prefix() + context['img_path'] = settings.KOREKTURY_IMG_DIR + context['img_indexes'] = range(pdf.stran) + context['form_oprava'] = OpravaForm() + opravy = Oprava.objects.filter(pdf=self.kwargs['pdf']) + zasluhy = {} + for o in opravy: + if o.autor in zasluhy: + zasluhy[o.autor]+=1 + else: + zasluhy[o.autor]=1 + o.komentare = o.komentar_set.all() + for k in o.komentare: + if k.autor in zasluhy: + zasluhy[k.autor] += 1 + else: + zasluhy[k.autor] = 1 + zasluhy = [ + {'autor': jmeno, 'pocet': pocet} + for (jmeno, pocet) in zasluhy.items() + ] + zasluhy.sort(key=lambda z: z['pocet'], reverse=True) + + strany = set(o.strana for o in opravy) + opravy_na_stranu = [{'strana': s, 'op_id': opravy.filter(strana=s)} for s in strany] + context['opravy_strany'] = opravy_na_stranu + + context['opravy'] = opravy + context['zasluhy'] = zasluhy + return context + + def form_valid(self,form): + return super(KorekturyView,self).form_valid(form) diff --git a/prednasky/admin.py b/prednasky/admin.py index 99504ddf..2688fffc 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -10,91 +10,91 @@ from seminar.models import Soustredeni class Seznam_PrednaskaInline(admin.TabularInline): - model = Prednaska.seznamy.through - extra = 0 - - def prednaska__nazev(self, obj): - return mark_safe( - u"{}".format( - obj.prednaska.id, - escape(obj.prednaska.nazev) - ) - ) - - def prednaska__popis(self, obj): - return mark_safe( - u"
{}
".format( - escape(obj.prednaska.popis) - ) - ) - - def prednaska__anotace(self, obj): - return obj.prednaska.anotace - - def prednaska__org(self, obj): - return obj.prednaska.org - - def prednaska__obor(self, obj): - return obj.prednaska.obor - - prednaska__nazev.short_description = u'Přednáška' - prednaska__popis.short_description = u'Popis pro orgy' - prednaska__anotace.short_description = u'Anotace' - prednaska__org.short_description = u'Org' - prednaska__obor.short_description = u'Obor' - - readonly_fields = [ - 'prednaska__nazev', - 'prednaska__obor', - 'prednaska__org', - 'prednaska__popis', - 'prednaska__anotace', - ] - exclude = ['prednaska'] - - def has_add_permission(self, req): return False + model = Prednaska.seznamy.through + extra = 0 + + def prednaska__nazev(self, obj): + return mark_safe( + u"{}".format( + obj.prednaska.id, + escape(obj.prednaska.nazev) + ) + ) + + def prednaska__popis(self, obj): + return mark_safe( + u"
{}
".format( + escape(obj.prednaska.popis) + ) + ) + + def prednaska__anotace(self, obj): + return obj.prednaska.anotace + + def prednaska__org(self, obj): + return obj.prednaska.org + + def prednaska__obor(self, obj): + return obj.prednaska.obor + + prednaska__nazev.short_description = u'Přednáška' + prednaska__popis.short_description = u'Popis pro orgy' + prednaska__anotace.short_description = u'Anotace' + prednaska__org.short_description = u'Org' + prednaska__obor.short_description = u'Obor' + + readonly_fields = [ + 'prednaska__nazev', + 'prednaska__obor', + 'prednaska__org', + 'prednaska__popis', + 'prednaska__anotace', + ] + exclude = ['prednaska'] + + def has_add_permission(self, req): return False class SeznamAdmin(VersionAdmin): - list_display = ['soustredeni', 'stav'] - inlines = [Seznam_PrednaskaInline] + list_display = ['soustredeni', 'stav'] + inlines = [Seznam_PrednaskaInline] admin.site.register(Seznam, SeznamAdmin) class PrednaskaAdmin(VersionAdmin): - list_display = ['nazev', 'org', 'obor'] - list_filter = ['org', 'obor'] - search_fields = [] - filter_horizontal = ('seznamy', ) - - actions = ['move_to_soustredeni'] - - def move_to_soustredeni(self, request, queryset): - sous = Soustredeni.objects.first() - seznam = Seznam.objects.filter(soustredeni=sous, stav=STAV_NAVRH) - if len(seznam) == 0: - self.message_user( - request, - u"Není definován seznam pro aktuální soustředění, " - u"nic se neprovedlo", - messages.ERROR - ) - return - seznam = seznam[0] - for prednaska in queryset: - prednaska.seznamy.add(seznam) - prednaska.save() - - self.message_user( - request, - u"Vybrané přednášky ({}) přidány jako návrhy na nejbližší " - u"soustředění".format(len(queryset)) - ) - - move_to_soustredeni.short_description = ( - u"Přidat přednášky do návrhu na nejbližší soustředění" - ) + list_display = ['nazev', 'org', 'obor'] + list_filter = ['org', 'obor'] + search_fields = [] + filter_horizontal = ('seznamy', ) + + actions = ['move_to_soustredeni'] + + def move_to_soustredeni(self, request, queryset): + sous = Soustredeni.objects.first() + seznam = Seznam.objects.filter(soustredeni=sous, stav=STAV_NAVRH) + if len(seznam) == 0: + self.message_user( + request, + u"Není definován seznam pro aktuální soustředění, " + u"nic se neprovedlo", + messages.ERROR + ) + return + seznam = seznam[0] + for prednaska in queryset: + prednaska.seznamy.add(seznam) + prednaska.save() + + self.message_user( + request, + u"Vybrané přednášky ({}) přidány jako návrhy na nejbližší " + u"soustředění".format(len(queryset)) + ) + + move_to_soustredeni.short_description = ( + u"Přidat přednášky do návrhu na nejbližší soustředění" + ) admin.site.register(Prednaska, PrednaskaAdmin) diff --git a/prednasky/forms.py b/prednasky/forms.py index 969232e3..071d068e 100644 --- a/prednasky/forms.py +++ b/prednasky/forms.py @@ -2,7 +2,7 @@ from django import forms class NewPrednaskyForm(forms.Form): - ucastnik = forms.CharField(label = 'Tvoje jméno', max_length = 100) + ucastnik = forms.CharField(label = 'Tvoje jméno', max_length = 100) diff --git a/prednasky/models.py b/prednasky/models.py index e1e57eef..1cda462f 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -29,15 +29,15 @@ class Seznam(models.Model): stav = models.IntegerField('Stav',choices=STAV_CHOICES,default = STAV_NAVRH) def __str__(self): - return force_unicode("Seznam {}přednášek na {}".format("návrhů " - if self.stav == STAV_NAVRH else "", self.soustredeni)) + return force_unicode("Seznam {}přednášek na {}".format("návrhů " + if self.stav == STAV_NAVRH else "", self.soustredeni)) CHOICES_OBTIZNOST = ( - (1, 'Lehká'), - (2, 'Střední'), - (3, 'Těžká'), - ) + (1, 'Lehká'), + (2, 'Střední'), + (3, 'Těžká'), + ) CHOICES_BODY = ( (-1, '-1'), @@ -82,5 +82,5 @@ class Hlasovani(models.Model): def __str__(self): return force_unicode("{} dal {} bodů {} v seznamu {}".format(self.ucastnik, - self.body, self.prednaska, self.seznam)) + self.body, self.prednaska, self.seznam)) diff --git a/prednasky/views.py b/prednasky/views.py index 05c63954..36239cfa 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -10,83 +10,83 @@ from seminar.models import Soustredeni from prednasky.forms import NewPrednaskyForm def newPrednaska(request): - # hlasovani se vztahuje k nejnovejsimu soustredeni - sous = Soustredeni.objects.first() - seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first() - print(seznam) - # obsluha formulare - if request.method == 'POST': - form = NewPrednaskyForm(request.POST, request.FILES) - if form.is_valid(): - jmeno = form.cleaned_data['ucastnik'] - for i in request.POST: - if i[0] == 'q': - hlasovani = Hlasovani() - print("q:"+i[1:]) - hlasovani.prednaska = Prednaska.objects.filter(pk = int(i[1:]))[0] - hlasovani.body = int(request.POST[i]) - hlasovani.ucastnik = jmeno - hlasovani.seznam = seznam - hlasovani.save() - - # presmerovani na prave vzniklou galerii - return HttpResponseRedirect('./hotovo') - - else: - form = NewPrednaskyForm() - - return render( - request, - 'prednasky/base.html', - {'form': form, 'prednasky': seznam} - ) + # hlasovani se vztahuje k nejnovejsimu soustredeni + sous = Soustredeni.objects.first() + seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first() + print(seznam) + # obsluha formulare + if request.method == 'POST': + form = NewPrednaskyForm(request.POST, request.FILES) + if form.is_valid(): + jmeno = form.cleaned_data['ucastnik'] + for i in request.POST: + if i[0] == 'q': + hlasovani = Hlasovani() + print("q:"+i[1:]) + hlasovani.prednaska = Prednaska.objects.filter(pk = int(i[1:]))[0] + hlasovani.body = int(request.POST[i]) + hlasovani.ucastnik = jmeno + hlasovani.seznam = seznam + hlasovani.save() + + # presmerovani na prave vzniklou galerii + return HttpResponseRedirect('./hotovo') + + else: + form = NewPrednaskyForm() + + return render( + request, + 'prednasky/base.html', + {'form': form, 'prednasky': seznam} + ) def Prednaska_hotovo(request): - return render(request, 'prednasky/hotovo.html') + return render(request, 'prednasky/hotovo.html') class MetaSeznamListView(generic.ListView): - model = Seznam - template_name = 'prednasky/metaseznam_prednasek.html' + model = Seznam + template_name = 'prednasky/metaseznam_prednasek.html' class SeznamListView(generic.ListView): - template_name = 'prednasky/seznam_prednasek.html' + template_name = 'prednasky/seznam_prednasek.html' - def get_queryset(self): - self.seznam = get_object_or_404(Seznam, id=self.kwargs["seznam"]) - prednasky = Prednaska.objects.filter(seznamy=self.seznam).order_by( - 'org__user__first_name', 'org__user__last_name' - ).annotate(body=Sum('hlasovani__body')) - return prednasky + def get_queryset(self): + self.seznam = get_object_or_404(Seznam, id=self.kwargs["seznam"]) + prednasky = Prednaska.objects.filter(seznamy=self.seznam).order_by( + 'org__user__first_name', 'org__user__last_name' + ).annotate(body=Sum('hlasovani__body')) + return prednasky def SeznamExportView(request, seznam): - 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 - # lidi? - hlasovani = Hlasovani.objects.filter(seznam=seznam) - prednasky = Prednaska.objects.filter(seznamy=seznam) - orgove = set(p.org for p in prednasky) - ucastnici = set(h.ucastnik for h in hlasovani) - - for p in prednasky: - p.body = [] - for u in ucastnici: - try: - p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body) - except ObjectDoesNotExist: - # účastník nehlasoval - p.body.append("?") - - for h in hlasovani: - h.ucastnik = hash(h.ucastnik) - - return render( - request, - 'prednasky/seznam_prednasek_export.txt', - {"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove}, - content_type="text/plain" - ) + 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 + # lidi? + hlasovani = Hlasovani.objects.filter(seznam=seznam) + prednasky = Prednaska.objects.filter(seznamy=seznam) + orgove = set(p.org for p in prednasky) + ucastnici = set(h.ucastnik for h in hlasovani) + + for p in prednasky: + p.body = [] + for u in ucastnici: + try: + p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body) + except ObjectDoesNotExist: + # účastník nehlasoval + p.body.append("?") + + for h in hlasovani: + h.ucastnik = hash(h.ucastnik) + + return render( + request, + 'prednasky/seznam_prednasek_export.txt', + {"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove}, + content_type="text/plain" + ) diff --git a/seminar/admin.py b/seminar/admin.py index 128c825e..024878d2 100644 --- a/seminar/admin.py +++ b/seminar/admin.py @@ -18,34 +18,34 @@ from autocomplete_light import shortcuts as autocomplete_light class UserModelChoiceField(forms.ModelChoiceField): - u"""Vlastní ModelChoiceField pro uživatele. Zobrazí kromě loginu i jméno. - """ - def label_from_instance(self, obj): - return u"{} ({})".format(obj.get_full_name(), obj.username) + u"""Vlastní ModelChoiceField pro uživatele. Zobrazí kromě loginu i jméno. + """ + def label_from_instance(self, obj): + return u"{} ({})".format(obj.get_full_name(), obj.username) def get_form_predvypln_autora(self, request, obj=None, *args, **kwargs): - u"""get_form fce pro Adminy. Předvyplňí přihlášeného uživatele jako autora. - """ - form = super(self.__class__, self).get_form(request, *args, **kwargs) - form.base_fields['autor'].initial = request.user.id - return form + u"""get_form fce pro Adminy. Předvyplňí přihlášeného uživatele jako autora. + """ + form = super(self.__class__, self).get_form(request, *args, **kwargs) + form.base_fields['autor'].initial = request.user.id + return form def make_set_action(atribut, hodnota, nazev): - u""" - Pomocnik pro rychle vytvareni hromadnych admin akci ktere jen nastavuji - atribut (dany jako string) na danou hodnotu. - nazev je krátký popis akce pro admin rozhraní.""" - def action_f(modeladmin, request, queryset): - for obj in queryset: - assert atribut in obj.__dict__ - obj.__setattr__(atribut, hodnota) - obj.save() - action_f.short_description = nazev - # Trik: je potřeba, aby se funkce různě (ale libovolně) jmenovaly, - # jinak je Django v seznamu akcí splácne do jedné - action_f.__name__ = 'action_f_%d_%d_%d' % (id(atribut), id(hodnota), id(nazev), ) - return action_f + u""" + Pomocnik pro rychle vytvareni hromadnych admin akci ktere jen nastavuji + atribut (dany jako string) na danou hodnotu. + nazev je krátký popis akce pro admin rozhraní.""" + def action_f(modeladmin, request, queryset): + for obj in queryset: + assert atribut in obj.__dict__ + obj.__setattr__(atribut, hodnota) + obj.save() + action_f.short_description = nazev + # Trik: je potřeba, aby se funkce různě (ale libovolně) jmenovaly, + # jinak je Django v seznamu akcí splácne do jedné + action_f.__name__ = 'action_f_%d_%d_%d' % (id(atribut), id(hodnota), id(nazev), ) + return action_f ### Globální nastavení @@ -56,202 +56,202 @@ admin.site.register(Nastaveni, SingletonModelAdmin) ### UTILS (pro verbose_name a help_text) def field_labels(model, fieldname): - f = [i for i in model._meta.fields if i.name == fieldname][0] - return {'label': f.verbose_name.capitalize(), 'help_text': f.help_text, } + f = [i for i in model._meta.fields if i.name == fieldname][0] + return {'label': f.verbose_name.capitalize(), 'help_text': f.help_text, } def create_modeladmin(modeladmin, model, name = None, verbose_name = None, verbose_name_plural = None): - - class Meta: - proxy = True - app_label = model._meta.app_label - Meta.verbose_name = verbose_name - Meta.verbose_name_plural = verbose_name_plural - - attrs = {'__module__': '', 'Meta': Meta} - newmodel = type(name, (model,), attrs) - admin.site.register(newmodel, modeladmin) - return modeladmin + + class Meta: + proxy = True + app_label = model._meta.app_label + Meta.verbose_name = verbose_name + Meta.verbose_name_plural = verbose_name_plural + + attrs = {'__module__': '', 'Meta': Meta} + newmodel = type(name, (model,), attrs) + admin.site.register(newmodel, modeladmin) + return modeladmin ### INLINES class ResitelInline(admin.TabularInline): - model = Resitel - fields = ['osoba_jmeno', 'osoba_prijmeni', 'skola', 'osoba_mesto', 'rok_maturity', ] - readonly_fields = ['osoba_jmeno', 'osoba_prijmeni', 'skola', 'osoba_mesto', 'rok_maturity', ] - extra = 0 - view_on_site = False + model = Resitel + fields = ['osoba_jmeno', 'osoba_prijmeni', 'skola', 'osoba_mesto', 'rok_maturity', ] + readonly_fields = ['osoba_jmeno', 'osoba_prijmeni', 'skola', 'osoba_mesto', 'rok_maturity', ] + extra = 0 + view_on_site = False - def osoba_jmeno(self, obj): - return obj.osoba.jmeno + def osoba_jmeno(self, obj): + return obj.osoba.jmeno - def osoba_prijmeni(self, obj): - return obj.osoba.prijmeni + def osoba_prijmeni(self, obj): + return obj.osoba.prijmeni - def osoba_mesto(self, obj): - return obj.osoba.mesto + def osoba_mesto(self, obj): + return obj.osoba.mesto - def has_add_permission(self, req): return False + def has_add_permission(self, req): return False class CisloInline(admin.TabularInline): - model = Cislo - fields = ['cislo', - 'datum_vydani', 'datum_deadline', 'datum_deadline_soustredeni', - 'verejne_db', 'poznamka'] - readonly_fields = ['cislo'] - extra = 0 - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } - view_on_site = Cislo.verejne_url + model = Cislo + fields = ['cislo', + 'datum_vydani', 'datum_deadline', 'datum_deadline_soustredeni', + 'verejne_db', 'poznamka'] + readonly_fields = ['cislo'] + extra = 0 + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + view_on_site = Cislo.verejne_url - def has_add_permission(self, req): return False + def has_add_permission(self, req): return False class PrilohaReseniInline(admin.StackedInline): - model = PrilohaReseni - fields = ['vytvoreno', 'soubor', 'poznamka'] - readonly_fields = ['vytvoreno'] - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } - view_on_site = False + model = PrilohaReseni + fields = ['vytvoreno', 'soubor', 'poznamka'] + readonly_fields = ['vytvoreno'] + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + view_on_site = False - extra = 0 + extra = 0 class ProblemInline(admin.TabularInline): - model = Problem - fields = ['kod', 'nazev', 'autor', 'garant', 'opravovatele', 'stav'] - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } - view_on_site = Problem.verejne_url + model = Problem + fields = ['kod', 'nazev', 'autor', 'garant', 'opravovatele', 'stav'] + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + view_on_site = Problem.verejne_url - extra = 0 + extra = 0 class ReseniKProblemuInline(admin.TabularInline): - form = autocomplete_light.modelform_factory(Reseni, autocomplete_fields=['resitele'], fields=['resitele']) - model = Reseni - fields = ['resitele', 'forma', 'body', 'cislo_body', 'timestamp', 'poznamka'] - readonly_fields = ['timestamp'] - extra = 0 - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } - view_on_site = False - - def get_queryset(self, request): - qs = super(ReseniKProblemuInline, self).get_queryset(request) - return qs.select_related('problem', 'cislo_body', 'resitele') - - # Potenciální DB HOG (cislo_body se dotazovalo na cisla pri kazdem zobrazeni jejich selectu ...) - def formfield_for_dbfield(self, db_field, **kwargs): - formfield = super(ReseniKProblemuInline, self).formfield_for_dbfield(db_field, **kwargs) - if db_field.name == 'cislo_body': - # dirty trick so queryset is evaluated and cached in .choices - formfield.choices = formfield.choices - return formfield + form = autocomplete_light.modelform_factory(Reseni, autocomplete_fields=['resitele'], fields=['resitele']) + model = Reseni + fields = ['resitele', 'forma', 'body', 'cislo_body', 'timestamp', 'poznamka'] + readonly_fields = ['timestamp'] + extra = 0 + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + view_on_site = False + + def get_queryset(self, request): + qs = super(ReseniKProblemuInline, self).get_queryset(request) + return qs.select_related('problem', 'cislo_body', 'resitele') + + # Potenciální DB HOG (cislo_body se dotazovalo na cisla pri kazdem zobrazeni jejich selectu ...) + def formfield_for_dbfield(self, db_field, **kwargs): + formfield = super(ReseniKProblemuInline, self).formfield_for_dbfield(db_field, **kwargs) + if db_field.name == 'cislo_body': + # dirty trick so queryset is evaluated and cached in .choices + formfield.choices = formfield.choices + return formfield class ReseniKResiteliInline(admin.TabularInline): - model = Reseni - fields = ['problem', 'forma', 'body', 'cislo_body', 'timestamp', 'poznamka'] - readonly_fields = ['cas_doruceni', 'problem'] - extra = 0 - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } - view_on_site = False - - def has_add_permission(self, req): return False - - def get_queryset(self, request): - qs = super(ReseniKResiteliInline, self).get_queryset(request) - return qs.select_related('problem', 'cislo_body', 'resitel') - - # Potenciální DB HOG (cislo_body se dotazovalo na cisla pri kazdem zobrazeni jejich selectu ...) - def formfield_for_dbfield(self, db_field, **kwargs): - formfield = super(ReseniKResiteliInline, self).formfield_for_dbfield(db_field, **kwargs) - if db_field.name == 'cislo_body': - # dirty trick so queryset is evaluated and cached in .choices - formfield.choices = formfield.choices - return formfield + model = Reseni + fields = ['problem', 'forma', 'body', 'cislo_body', 'timestamp', 'poznamka'] + readonly_fields = ['cas_doruceni', 'problem'] + extra = 0 + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + view_on_site = False + + def has_add_permission(self, req): return False + + def get_queryset(self, request): + qs = super(ReseniKResiteliInline, self).get_queryset(request) + return qs.select_related('problem', 'cislo_body', 'resitel') + + # Potenciální DB HOG (cislo_body se dotazovalo na cisla pri kazdem zobrazeni jejich selectu ...) + def formfield_for_dbfield(self, db_field, **kwargs): + formfield = super(ReseniKResiteliInline, self).formfield_for_dbfield(db_field, **kwargs) + if db_field.name == 'cislo_body': + # dirty trick so queryset is evaluated and cached in .choices + formfield.choices = formfield.choices + return formfield class Soustredeni_UcastniciInline(admin.TabularInline): - form = autocomplete_light.modelform_factory(Soustredeni_Ucastnici, autocomplete_fields=['resitel'], fields=['resitel']) - model = Soustredeni_Ucastnici - fields = ['resitel', 'poznamka', ] - extra = 0 - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } + form = autocomplete_light.modelform_factory(Soustredeni_Ucastnici, autocomplete_fields=['resitel'], fields=['resitel']) + model = Soustredeni_Ucastnici + fields = ['resitel', 'poznamka', ] + extra = 0 + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } - def get_queryset(self, request): - qs = super(Soustredeni_UcastniciInline, self).get_queryset(request) - return qs.select_related('resitel', 'soustredeni') + def get_queryset(self, request): + qs = super(Soustredeni_UcastniciInline, self).get_queryset(request) + return qs.select_related('resitel', 'soustredeni') class Soustredeni_OrganizatoriInline(admin.TabularInline): - form = autocomplete_light.modelform_factory(Soustredeni_Organizatori, autocomplete_fields=['organizator'], fields=['organizator'],) - model = Soustredeni_Organizatori - fields = ['organizator', 'poznamka', ] - extra = 0 - formfield_overrides = { - models.TextField: {'widget': forms.TextInput}, - } - - def get_queryset(self, request): - qs = super(Soustredeni_OrganizatoriInline, self).get_queryset(request) - return qs.select_related('organizator', 'soustredeni') + form = autocomplete_light.modelform_factory(Soustredeni_Organizatori, autocomplete_fields=['organizator'], fields=['organizator'],) + model = Soustredeni_Organizatori + fields = ['organizator', 'poznamka', ] + extra = 0 + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + + def get_queryset(self, request): + qs = super(Soustredeni_OrganizatoriInline, self).get_queryset(request) + return qs.select_related('organizator', 'soustredeni') ### Resitel class ResitelAdmin(VersionAdmin): - form = autocomplete_light.modelform_factory(Resitel, autocomplete_fields=['skola'], fields=['skola']) - fk_name = 'osoba' - fieldsets = [ - (None, {'fields': ['jmeno', 'prijmeni', 'user']}), - (u'Škola', {'fields': ['skola', 'rok_maturity']}), - (u'Seminář', {'fields': ['datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_prihlaseni', 'zasilat']}), - (u'Osobní údaje', {'fields': ['pohlavi_muz', 'datum_narozeni', 'email', 'telefon']}), - (u'Adresa', {'fields': ['ulice', 'mesto', 'psc', 'stat']}), - ] - list_display = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__user', 'osoba__pohlavi_muz', 'skola', 'rok_maturity', 'pocet_reseni'] - list_filter = ['osoba__pohlavi_muz', 'rok_maturity', 'zasilat'] - search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__ulice', 'osoba__mesto', 'osoba__email'] + form = autocomplete_light.modelform_factory(Resitel, autocomplete_fields=['skola'], fields=['skola']) + fk_name = 'osoba' + fieldsets = [ + (None, {'fields': ['jmeno', 'prijmeni', 'user']}), + (u'Škola', {'fields': ['skola', 'rok_maturity']}), + (u'Seminář', {'fields': ['datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_prihlaseni', 'zasilat']}), + (u'Osobní údaje', {'fields': ['pohlavi_muz', 'datum_narozeni', 'email', 'telefon']}), + (u'Adresa', {'fields': ['ulice', 'mesto', 'psc', 'stat']}), + ] + list_display = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__user', 'osoba__pohlavi_muz', 'skola', 'rok_maturity', 'pocet_reseni'] + list_filter = ['osoba__pohlavi_muz', 'rok_maturity', 'zasilat'] + search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__ulice', 'osoba__mesto', 'osoba__email'] # inlines = [ReseniKResiteliInline] # FIXME: - view_on_site = False + view_on_site = False - def osoba__jmeno(self, obj): - return obj.osoba.jmeno - - def osoba__prijmeni(self, obj): - return obj.osoba.prijmeni - - def osoba__user(self, obj): - return obj.osoba.user + def osoba__jmeno(self, obj): + return obj.osoba.jmeno + + def osoba__prijmeni(self, obj): + return obj.osoba.prijmeni + + def osoba__user(self, obj): + return obj.osoba.user - def osoba__pohlavi_muz(self, obj): - return obj.osoba.pohlavi_muz + def osoba__pohlavi_muz(self, obj): + return obj.osoba.pohlavi_muz - def osoba__ulice(self, obj): - return obj.osoba.ulice + def osoba__ulice(self, obj): + return obj.osoba.ulice - def osoba__mesto(self, obj): - return obj.osoba.mesto + def osoba__mesto(self, obj): + return obj.osoba.mesto - def osoba__email(self, obj): - return obj.osoba.email + def osoba__email(self, obj): + return obj.osoba.email - def get_queryset(self, request): - qs = super(ResitelAdmin, self).get_queryset(request) - return qs.select_related('skola', 'user').annotate(pocet_reseni=Count('reseni')) + def get_queryset(self, request): + qs = super(ResitelAdmin, self).get_queryset(request) + return qs.select_related('skola', 'user').annotate(pocet_reseni=Count('reseni')) - def pocet_reseni(self, obj): - return obj.pocet_reseni + def pocet_reseni(self, obj): + return obj.pocet_reseni admin.site.register(Resitel, ResitelAdmin) @@ -260,17 +260,17 @@ admin.site.register(Resitel, ResitelAdmin) ### Skola class SkolaAdmin(VersionAdmin): - fieldsets = [ - (None, {'fields': ['nazev', 'kratky_nazev', 'je_zs', 'je_ss']}), - (u'Interní ID', {'fields': ['aesop_id', 'izo'], 'classes': ['collapse']}), - (u'Adresa', {'fields': ['ulice', 'mesto', 'psc', 'stat']}), - (None, {'fields': ['poznamka']}), - ] - list_display = ['nazev', 'aesop_id', 'mesto', 'ulice', 'stat', 'je_zs', 'je_ss'] - list_filter = ['stat', 'je_zs', 'je_ss'] - search_fields = ['nazev', 'mesto', 'ulice'] - inlines = [ResitelInline] - view_on_site = False + fieldsets = [ + (None, {'fields': ['nazev', 'kratky_nazev', 'je_zs', 'je_ss']}), + (u'Interní ID', {'fields': ['aesop_id', 'izo'], 'classes': ['collapse']}), + (u'Adresa', {'fields': ['ulice', 'mesto', 'psc', 'stat']}), + (None, {'fields': ['poznamka']}), + ] + list_display = ['nazev', 'aesop_id', 'mesto', 'ulice', 'stat', 'je_zs', 'je_ss'] + list_filter = ['stat', 'je_zs', 'je_ss'] + search_fields = ['nazev', 'mesto', 'ulice'] + inlines = [ResitelInline] + view_on_site = False admin.site.register(Skola, SkolaAdmin) @@ -278,59 +278,59 @@ admin.site.register(Skola, SkolaAdmin) ### Cislo class CisloAdmin(VersionAdmin): - fieldsets = [ - (None, { - 'fields': [ - 'cislo', - 'rocnik', - 'verejne_db', - 'verejna_vysledkovka', - 'faze', - 'poznamka', - 'pdf' - ] - }), - ('Data', {'fields': ['datum_vydani', 'datum_deadline', - 'datum_deadline_soustredeni']}), - ] - list_display = [ - 'kod', - 'rocnik', - 'cislo', - 'datum_vydani', - 'datum_deadline', - 'verejne', - 'verejna_vysledkovka' - ] - list_filter = ['rocnik'] - view_on_site = Cislo.verejne_url - actions = [ - make_set_action('verejne_db', True, 'Zveřejnit číslo'), - make_set_action('verejne_db', False, 'Skrýt (zneveřejnit) číslo'), - make_set_action('verejna_vysledkovka', True, 'Zveřejnit výsledkovku'), - make_set_action('verejna_vysledkovka', False, 'Skrýt (zneveřejnit) výsledkovku'), - ] - #inlines = [ProblemInline] - - def get_queryset(self, request): - qs = super(CisloAdmin, self).get_queryset(request) - return qs.select_related('rocnik') + fieldsets = [ + (None, { + 'fields': [ + 'cislo', + 'rocnik', + 'verejne_db', + 'verejna_vysledkovka', + 'faze', + 'poznamka', + 'pdf' + ] + }), + ('Data', {'fields': ['datum_vydani', 'datum_deadline', + 'datum_deadline_soustredeni']}), + ] + list_display = [ + 'kod', + 'rocnik', + 'cislo', + 'datum_vydani', + 'datum_deadline', + 'verejne', + 'verejna_vysledkovka' + ] + list_filter = ['rocnik'] + view_on_site = Cislo.verejne_url + actions = [ + make_set_action('verejne_db', True, 'Zveřejnit číslo'), + make_set_action('verejne_db', False, 'Skrýt (zneveřejnit) číslo'), + make_set_action('verejna_vysledkovka', True, 'Zveřejnit výsledkovku'), + make_set_action('verejna_vysledkovka', False, 'Skrýt (zneveřejnit) výsledkovku'), + ] + #inlines = [ProblemInline] + + def get_queryset(self, request): + qs = super(CisloAdmin, self).get_queryset(request) + return qs.select_related('rocnik') admin.site.register(Cislo, CisloAdmin) ### Rocnik class RocnikAdmin(VersionAdmin): - fieldsets = [ - (None, {'fields': ['rocnik', 'prvni_rok', 'exportovat']}), - ] - list_display = ['rocnik', 'prvni_rok', 'exportovat', 'verejne'] - inlines = [CisloInline] - view_on_site = Rocnik.verejne_url - actions = [ - make_set_action('exportovat', True, u'Nastavit pro AESOP export'), - make_set_action('exportovat', False, u'Skrýt pro AESOP export'), - ] + fieldsets = [ + (None, {'fields': ['rocnik', 'prvni_rok', 'exportovat']}), + ] + list_display = ['rocnik', 'prvni_rok', 'exportovat', 'verejne'] + inlines = [CisloInline] + view_on_site = Rocnik.verejne_url + actions = [ + make_set_action('exportovat', True, u'Nastavit pro AESOP export'), + make_set_action('exportovat', False, u'Skrýt pro AESOP export'), + ] admin.site.register(Rocnik, RocnikAdmin) @@ -353,21 +353,21 @@ admin.site.register(Rocnik, RocnikAdmin) ### Reseni class ReseniAdmin(VersionAdmin): - #form = autocomplete_light.modelform_factory(Reseni, autocomplete_fields=['problem', 'resitele'], fields=['problem', 'resitele']) - fieldsets = [ - (None, {'fields': ['problem__set', 'resitele__set', 'forma', 'cas_doruceni']}), - (u'Poznámky', {'fields': ['poznamka']}), - ] - readonly_fields = ['cas_doruceni'] - list_display = [ResitelInline, 'forma', 'cas_doruceni'] - list_filter = ['cas_doruceni', 'forma'] - search_fields = [] - inlines = [PrilohaReseniInline] # FIXME: odmazáno ProblemInline,ResitelInline - view_on_site = False - - def get_queryset(self, request): - qs = super(ReseniAdmin, self).get_queryset(request) - return qs.select_related('resitele', 'problem', 'cislo_body') + #form = autocomplete_light.modelform_factory(Reseni, autocomplete_fields=['problem', 'resitele'], fields=['problem', 'resitele']) + fieldsets = [ + (None, {'fields': ['problem__set', 'resitele__set', 'forma', 'cas_doruceni']}), + (u'Poznámky', {'fields': ['poznamka']}), + ] + readonly_fields = ['cas_doruceni'] + list_display = [ResitelInline, 'forma', 'cas_doruceni'] + list_filter = ['cas_doruceni', 'forma'] + search_fields = [] + inlines = [PrilohaReseniInline] # FIXME: odmazáno ProblemInline,ResitelInline + view_on_site = False + + def get_queryset(self, request): + qs = super(ReseniAdmin, self).get_queryset(request) + return qs.select_related('resitele', 'problem', 'cislo_body') admin.site.register(Reseni, ReseniAdmin) @@ -375,52 +375,52 @@ admin.site.register(Reseni, ReseniAdmin) ### Pohádka class PohadkaAdminForm(forms.ModelForm): - class Meta: - model = Pohadka - exclude = [] - autor = UserModelChoiceField(User.objects.filter(is_staff=True)) - uloha = forms.ModelChoiceField( - Uloha.objects.all() - ) + class Meta: + model = Pohadka + exclude = [] + autor = UserModelChoiceField(User.objects.filter(is_staff=True)) + uloha = forms.ModelChoiceField( + Uloha.objects.all() + ) - def __init__(self, *args, **kwargs): - super(PohadkaAdminForm, self).__init__(*args, **kwargs) - instance = getattr(self, 'instance', None) + def __init__(self, *args, **kwargs): + super(PohadkaAdminForm, self).__init__(*args, **kwargs) + instance = getattr(self, 'instance', None) - # viz ProblemAdminForm.__init__ - if instance and instance.pk: - if instance.uloha and instance.uloha.cislo_zadani: - if instance.uloha.cislo_zadani.faze != 'admin': - self.fields['text'].widget.attrs['readonly'] = True + # viz ProblemAdminForm.__init__ + if instance and instance.pk: + if instance.uloha and instance.uloha.cislo_zadani: + if instance.uloha.cislo_zadani.faze != 'admin': + self.fields['text'].widget.attrs['readonly'] = True class PohadkaAdmin(VersionAdmin): - form = PohadkaAdminForm - view_on_site = False + form = PohadkaAdminForm + view_on_site = False - def get_kod_ulohy(self, obj): - return obj.uloha.kod_v_rocniku() - get_kod_ulohy.short_description = u'Kód úlohy' + def get_kod_ulohy(self, obj): + return obj.uloha.kod_v_rocniku() + get_kod_ulohy.short_description = u'Kód úlohy' - def get_rocnik(self, obj): - return obj.uloha.cislo_zadani.rocnik.rocnik - get_rocnik.short_description = u'Ročník' + def get_rocnik(self, obj): + return obj.uloha.cislo_zadani.rocnik.rocnik + get_rocnik.short_description = u'Ročník' - list_display = [ - '__str__', - 'get_rocnik', - 'get_kod_ulohy', - 'uloha', - 'autor' - ] + list_display = [ + '__str__', + 'get_rocnik', + 'get_kod_ulohy', + 'uloha', + 'autor' + ] - get_form = get_form_predvypln_autora + get_form = get_form_predvypln_autora class PohadkaKProblemuInline(admin.TabularInline): - form = PohadkaAdminForm - model = Pohadka - exclude = [] - extra = 0 + form = PohadkaAdminForm + model = Pohadka + exclude = [] + extra = 0 admin.site.register(Pohadka, PohadkaAdmin) @@ -431,78 +431,78 @@ from autocomplete_light.contrib.taggit_field import TaggitField, TaggitWidget class ProblemAdminForm(forms.ModelForm): - #text_zadani = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_zadani')) - #text_reseni = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_reseni')) - #text_org = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_org')) - zamereni = TaggitField(widget=TaggitWidget('TagAutocomplete'), required=False) - autor = UserModelChoiceField(User.objects.filter(is_staff=True)) - opravovatel = UserModelChoiceField(User.objects.filter(is_staff=True), required=False) - class Meta: - model = Problem - exclude = ['nadproblem'] - - def __init__(self, *args, **kwargs): - super(ProblemAdminForm, self).__init__(*args, **kwargs) - instance = getattr(self, 'instance', None) - - # Nedovol měnit název a zadání resp. řešení, pokud je cislo_zadani - # resp. cislo_reseni mimo fázi admin. - # - # Nastavení readonly fields sice vypadá lépe (nevygeneruje input tag), - # ale při ukládání změny vypíše admin nespecifikovanou chybu, která je - # způsobena zřejmě tím, že se neodešle žádná hodnota pro povinné pole - # nazev. Navíc by se smazalo nepovinné pole zadání. - # - # Toto řešení je z http://stackoverflow.com/a/325038/4786205. - # - # TODO Django 1.9: použít field s atributem disabled? - if instance and instance.pk: - if instance.cislo_zadani and instance.cislo_zadani.faze != 'admin': - # CKEditor neumí readonly ... - self.fields['text_zadani'] = forms.CharField( - widget=forms.Textarea, - required=False - ) - for f in ['nazev', 'text_zadani', 'body']: - self.fields[f].widget.attrs['readonly'] = True - if instance.cislo_reseni and instance.cislo_reseni.faze != 'admin': - self.fields['text_reseni'] = forms.CharField( - widget=forms.Textarea, - required=False - ) - self.fields['text_reseni'].widget.attrs['readonly'] = True + #text_zadani = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_zadani')) + #text_reseni = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_reseni')) + #text_org = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_org')) + zamereni = TaggitField(widget=TaggitWidget('TagAutocomplete'), required=False) + autor = UserModelChoiceField(User.objects.filter(is_staff=True)) + opravovatel = UserModelChoiceField(User.objects.filter(is_staff=True), required=False) + class Meta: + model = Problem + exclude = ['nadproblem'] + + def __init__(self, *args, **kwargs): + super(ProblemAdminForm, self).__init__(*args, **kwargs) + instance = getattr(self, 'instance', None) + + # Nedovol měnit název a zadání resp. řešení, pokud je cislo_zadani + # resp. cislo_reseni mimo fázi admin. + # + # Nastavení readonly fields sice vypadá lépe (nevygeneruje input tag), + # ale při ukládání změny vypíše admin nespecifikovanou chybu, která je + # způsobena zřejmě tím, že se neodešle žádná hodnota pro povinné pole + # nazev. Navíc by se smazalo nepovinné pole zadání. + # + # Toto řešení je z http://stackoverflow.com/a/325038/4786205. + # + # TODO Django 1.9: použít field s atributem disabled? + if instance and instance.pk: + if instance.cislo_zadani and instance.cislo_zadani.faze != 'admin': + # CKEditor neumí readonly ... + self.fields['text_zadani'] = forms.CharField( + widget=forms.Textarea, + required=False + ) + for f in ['nazev', 'text_zadani', 'body']: + self.fields[f].widget.attrs['readonly'] = True + if instance.cislo_reseni and instance.cislo_reseni.faze != 'admin': + self.fields['text_reseni'] = forms.CharField( + widget=forms.Textarea, + required=False + ) + self.fields['text_reseni'].widget.attrs['readonly'] = True class ProblemAdmin(VersionAdmin): - form = ProblemAdminForm - fieldsets = [ - (None, {'fields': ['nazev', 'typ', 'stav', 'autor', 'zamereni', 'body', 'timestamp', 'import_dakos_id']}), - (u'Vydání', {'fields': ['kod', 'cislo_reseni', 'opravovatel',]}), - (None, {'fields': ['text_zadani', 'text_reseni', 'text_org',]}), - ] - list_select_related = True - search_fields = ['nazev', 'text_zadani', 'text_reseni', 'text_org'] - view_on_site = Problem.verejne_url - ordering = ['-timestamp'] - - readonly_fields = ['timestamp', 'import_dakos_id'] - - def get_queryset(self, request): - qs = super(ProblemAdmin, self).get_queryset(request) - return qs.select_related('autor', 'opravovatel', 'cislo_reseni') - - def pocet_reseni(self, obj): - return obj.pocet_reseni + form = ProblemAdminForm + fieldsets = [ + (None, {'fields': ['nazev', 'typ', 'stav', 'autor', 'zamereni', 'body', 'timestamp', 'import_dakos_id']}), + (u'Vydání', {'fields': ['kod', 'cislo_reseni', 'opravovatel',]}), + (None, {'fields': ['text_zadani', 'text_reseni', 'text_org',]}), + ] + list_select_related = True + search_fields = ['nazev', 'text_zadani', 'text_reseni', 'text_org'] + view_on_site = Problem.verejne_url + ordering = ['-timestamp'] + + readonly_fields = ['timestamp', 'import_dakos_id'] + + def get_queryset(self, request): + qs = super(ProblemAdmin, self).get_queryset(request) + return qs.select_related('autor', 'opravovatel', 'cislo_reseni') + + def pocet_reseni(self, obj): + return obj.pocet_reseni class ProblemNavrhAdmin(ProblemAdmin): - list_display = ['nazev', 'typ', 'stav', 'autor', 'timestamp'] - list_filter = ['typ', 'zamereni', 'timestamp', 'stav'] + list_display = ['nazev', 'typ', 'stav', 'autor', 'timestamp'] + list_filter = ['typ', 'zamereni', 'timestamp', 'stav'] - def get_queryset(self, request): - qs = super(ProblemNavrhAdmin, self).get_queryset(request) - return qs.filter(stav__in=[Problem.STAV_NAVRH, Problem.STAV_SMAZANY]) + def get_queryset(self, request): + qs = super(ProblemNavrhAdmin, self).get_queryset(request) + return qs.filter(stav__in=[Problem.STAV_NAVRH, Problem.STAV_SMAZANY]) - get_form = get_form_predvypln_autora + get_form = get_form_predvypln_autora #FIXME @@ -510,54 +510,54 @@ class ProblemNavrhAdmin(ProblemAdmin): class ProblemZadanyAdmin(ProblemAdmin): - list_display = [ - 'nazev', - 'typ', - 'cislo_zadani_link', - 'cislo_reseni_link', - 'autor', - 'opravovatel', - 'kod', - 'verejne' - ] - list_filter = [ - 'typ', 'zamereni', 'cislo_zadani__cislo', 'cislo_zadani__rocnik' - ] - - def cislo_zadani_link(self, obj): - if not obj.cislo_zadani: - return "" - return mark_safe('{}'.format( - reverse("admin:seminar_cislo_change", args=(obj.cislo_zadani.pk,)), - obj.cislo_zadani - )) - cislo_zadani_link.short_description = u'Číslo zadání' - - # TODO pokud se budou odkazy v adminu více používat, možná by se hodilo je - # nějak zjednodušit, např. tímto? - # https://github.com/gitaarik/django-admin-relation-links - def cislo_reseni_link(self, obj): - if not obj.cislo_reseni: - return "" - return mark_safe('{}'.format( - reverse("admin:seminar_cislo_change", args=(obj.cislo_reseni.pk,)), - obj.cislo_reseni - )) - cislo_reseni_link.short_description = u'Číslo řešení' - - def get_inline_instances(self, request, obj=None): - if obj and obj.typ == Problem.TYP_ULOHA: - inlines = [ReseniKProblemuInline, PohadkaKProblemuInline] - else: - inlines = [ReseniKProblemuInline] - - return [inline(self.model, self.admin_site) for inline in inlines] - - def get_queryset(self, request): - qs = super(ProblemZadanyAdmin, self).get_queryset(request) - return qs.filter(stav=Problem.STAV_ZADANY) - - get_form = get_form_predvypln_autora + list_display = [ + 'nazev', + 'typ', + 'cislo_zadani_link', + 'cislo_reseni_link', + 'autor', + 'opravovatel', + 'kod', + 'verejne' + ] + list_filter = [ + 'typ', 'zamereni', 'cislo_zadani__cislo', 'cislo_zadani__rocnik' + ] + + def cislo_zadani_link(self, obj): + if not obj.cislo_zadani: + return "" + return mark_safe('{}'.format( + reverse("admin:seminar_cislo_change", args=(obj.cislo_zadani.pk,)), + obj.cislo_zadani + )) + cislo_zadani_link.short_description = u'Číslo zadání' + + # TODO pokud se budou odkazy v adminu více používat, možná by se hodilo je + # nějak zjednodušit, např. tímto? + # https://github.com/gitaarik/django-admin-relation-links + def cislo_reseni_link(self, obj): + if not obj.cislo_reseni: + return "" + return mark_safe('{}'.format( + reverse("admin:seminar_cislo_change", args=(obj.cislo_reseni.pk,)), + obj.cislo_reseni + )) + cislo_reseni_link.short_description = u'Číslo řešení' + + def get_inline_instances(self, request, obj=None): + if obj and obj.typ == Problem.TYP_ULOHA: + inlines = [ReseniKProblemuInline, PohadkaKProblemuInline] + else: + inlines = [ReseniKProblemuInline] + + return [inline(self.model, self.admin_site) for inline in inlines] + + def get_queryset(self, request): + qs = super(ProblemZadanyAdmin, self).get_queryset(request) + return qs.filter(stav=Problem.STAV_ZADANY) + + get_form = get_form_predvypln_autora #FIXME #create_modeladmin(ProblemZadanyAdmin, Problem, 'ProblemZadany', verbose_name=u'Problém (zadaný)', verbose_name_plural=u'Problémy (zadané)') @@ -585,40 +585,40 @@ class ProblemZadanyAdmin(ProblemAdmin): ### Soustredeni class SoustredeniAdminForm(forms.ModelForm): - text = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Soustredeni, 'text')) - class Meta: - model = Soustredeni - exclude = [] + text = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Soustredeni, 'text')) + class Meta: + model = Soustredeni + exclude = [] class SoustredeniAdmin(VersionAdmin): - form = SoustredeniAdminForm - fieldsets = [ - (None, {'fields': ['rocnik', 'misto', 'typ', 'verejne_db', 'exportovat', 'text']}), - ('Data', {'fields': ['datum_zacatku', 'datum_konce']}), - ] - list_display = ['rocnik', 'misto', 'datum_zacatku', 'typ', 'exportovat', 'verejne'] - inlines = [Soustredeni_UcastniciInline, Soustredeni_OrganizatoriInline] - list_filter = ['typ', 'rocnik'] - view_on_site = Soustredeni.verejne_url - actions = [ - make_set_action('verejne_db', True, u'Zveřejnit soustředění'), - make_set_action('verejne_db', False, u'Skrýt (zneveřejnit) soustředění'), - make_set_action('exportovat', True, u'Nastavit pro AESOP export'), - make_set_action('exportovat', False, u'Skrýt pro AESOP export'), - ] + form = SoustredeniAdminForm + fieldsets = [ + (None, {'fields': ['rocnik', 'misto', 'typ', 'verejne_db', 'exportovat', 'text']}), + ('Data', {'fields': ['datum_zacatku', 'datum_konce']}), + ] + list_display = ['rocnik', 'misto', 'datum_zacatku', 'typ', 'exportovat', 'verejne'] + inlines = [Soustredeni_UcastniciInline, Soustredeni_OrganizatoriInline] + list_filter = ['typ', 'rocnik'] + view_on_site = Soustredeni.verejne_url + actions = [ + make_set_action('verejne_db', True, u'Zveřejnit soustředění'), + make_set_action('verejne_db', False, u'Skrýt (zneveřejnit) soustředění'), + make_set_action('exportovat', True, u'Nastavit pro AESOP export'), + make_set_action('exportovat', False, u'Skrýt pro AESOP export'), + ] admin.site.register(Soustredeni, SoustredeniAdmin) ### Konfery class KonferaAdminForm(forms.ModelForm): - class Meta: - model=Konfera - exclude = [] + class Meta: + model=Konfera + exclude = [] class KonferaAdmin(VersionAdmin): - form = KonferaAdminForm - list_filter = ['soustredeni'] - list_display = ['nazev','soustredeni','organizator','typ_prezentace'] + form = KonferaAdminForm + list_filter = ['soustredeni'] + list_display = ['nazev','soustredeni','organizator','typ_prezentace'] # inlines = [Konfera_UcastniciInline] admin.site.register(Konfera,KonferaAdmin) @@ -626,35 +626,35 @@ admin.site.register(Konfera,KonferaAdmin) ### Novinky class NovinkyAdminForm(forms.ModelForm): - text = forms.CharField(widget=CKEditorUploadingWidget(), required=False, - **field_labels(Novinky, 'text')) - autor = UserModelChoiceField(User.objects.filter(is_staff=True)) + text = forms.CharField(widget=CKEditorUploadingWidget(), required=False, + **field_labels(Novinky, 'text')) + autor = UserModelChoiceField(User.objects.filter(is_staff=True)) - class Meta: - model = Novinky - exclude = [] + class Meta: + model = Novinky + exclude = [] def zverejnit_novinky(modeladmin, request, queryset): - ''' zverejni vybrane novinky ''' - for novinka in queryset: - novinka.zverejneno = True - novinka.save() - zverejnit_novinky.short_description = 'Zveřejnit vybané novinky' + ''' zverejni vybrane novinky ''' + for novinka in queryset: + novinka.zverejneno = True + novinka.save() + zverejnit_novinky.short_description = 'Zveřejnit vybané novinky' def zneverejnit_novinky(modeladmin, request, queryset): - ''' zneverejni vybrane novinky''' - for novinka in queryset: - novinka.zverejneno = False - novinka.save() - zneverejnit_novinky.short_description = 'Zneveřejnit vybrané novinky' + ''' zneverejni vybrane novinky''' + for novinka in queryset: + novinka.zverejneno = False + novinka.save() + zneverejnit_novinky.short_description = 'Zneveřejnit vybrané novinky' class NovinkyAdmin(VersionAdmin): - form = NovinkyAdminForm - list_display = ['datum', 'autor', 'text', 'zverejneno', 'obrazek'] - actions = [zverejnit_novinky, zneverejnit_novinky] + form = NovinkyAdminForm + list_display = ['datum', 'autor', 'text', 'zverejneno', 'obrazek'] + actions = [zverejnit_novinky, zneverejnit_novinky] - get_form = get_form_predvypln_autora + get_form = get_form_predvypln_autora admin.site.register(Novinky, NovinkyAdmin) @@ -662,40 +662,40 @@ admin.site.register(Novinky, NovinkyAdmin) ### Organizator def jmeno_organizatora(obj): - ''' vraci jmeno organizatora ''' - jmeno = obj.user.first_name - if obj.prezdivka: - jmeno = jmeno + ' "' + obj.prezdivka + '"' - jmeno = jmeno + ' ' + obj.user.last_name - if jmeno == ' ': # zobrazeni bezejmennych orgu - return 'org' - return jmeno + ''' vraci jmeno organizatora ''' + jmeno = obj.user.first_name + if obj.prezdivka: + jmeno = jmeno + ' "' + obj.prezdivka + '"' + jmeno = jmeno + ' ' + obj.user.last_name + if jmeno == ' ': # zobrazeni bezejmennych orgu + return 'org' + return jmeno jmeno_organizatora.short_description = 'Jméno organizátora' def je_organizator_aktivni(obj): - ''' zjisti, zda-li je organizator aktivni ''' - return obj.user.is_active + ''' zjisti, zda-li je organizator aktivni ''' + return obj.user.is_active je_organizator_aktivni.short_description = 'Aktivní' je_organizator_aktivni.boolean = True def zaktivovat_organizatory(modeladmin, request, queryset): - ''' vybrane organizatory oznaci jako aktivni ''' - for org in queryset: - org.user.is_active = True - org.user.save() + ''' vybrane organizatory oznaci jako aktivni ''' + for org in queryset: + org.user.is_active = True + org.user.save() zaktivovat_organizatory.short_description = 'Zaktivovat organizátory' def deaktivovat_organizatory(modeladmin, request, queryset): - ''' deaktivuje vybrane organizatory ''' - for org in queryset: - org.user.is_active = False - org.user.save() + ''' deaktivuje vybrane organizatory ''' + for org in queryset: + org.user.is_active = False + org.user.save() deaktivovat_organizatory.short_description = 'Deaktivovat organizátory' @admin.register(Organizator) class OrganizatorAdmin(VersionAdmin): - list_filter = ['organizuje_do'] - list_display = [jmeno_organizatora, je_organizator_aktivni,] - actions = [zaktivovat_organizatory, deaktivovat_organizatory,] + list_filter = ['organizuje_do'] + list_display = [jmeno_organizatora, je_organizator_aktivni,] + actions = [zaktivovat_organizatory, deaktivovat_organizatory,] diff --git a/seminar/forms.py b/seminar/forms.py index fe150c55..693e36df 100644 --- a/seminar/forms.py +++ b/seminar/forms.py @@ -1,6 +1,6 @@ from django import forms class NameForm(forms.Form): - your_name = forms.CharField(label='Your name', max_length=100) + your_name = forms.CharField(label='Your name', max_length=100) diff --git a/seminar/models.py b/seminar/models.py index 10ee5f77..bbeba051 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -65,7 +65,7 @@ class Osoba(SeminarModelBase): # User, pokud má na webu účet user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, - verbose_name='uživatel') + verbose_name='uživatel') # 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) diff --git a/seminar/tests.py b/seminar/tests.py index 80bbfaa3..a5e74f01 100644 --- a/seminar/tests.py +++ b/seminar/tests.py @@ -14,81 +14,81 @@ from seminar import ovvpfile from seminar import utils class SeminarBasicTests(TestCase): - def setUp(self): - create_test_data(size=2) - self.client = Client() - - def tearDown(self): - call_command('flush', noinput=True, verbosity=0, interactive=False) - self.cleint = None - - def test_rocniky(self): - r19 = Rocnik.objects.get(rocnik=21) - self.assertEqual(r19.roman(), 'XXI') - - def test_render_cislo_e2e(self): - cs = Cislo.objects.all() - for c in cs[:4]: - url = c.verejne_url() - r = self.client.get(url) - assert r.status_code == 200 - assert len(r.content) >= 100 - # TODO: Validate cntent as HTML - - def test_render_problem_e2e(self): - ps = Problem.objects.all() - for p in ps[:4]: - url = p.verejne_url() - r = self.client.get(url) - assert r.status_code == 200 - assert len(r.content) >= 100 - # TODO: Validate cntent as HTML - - def test_export_e2e(self): - i_url = '/aesop-export/index.csv' - i_r = self.client.get(i_url) - assert i_r.status_code == 200 - ls = i_r.content.strip().split('\n') - - for u in [ls[0], ls[-1]]: - ex_r = self.client.get('/aesop-export/' + u) - assert ex_r.status_code == 200 - assert len(ex_r.content) >= 100 - o = ovvpfile.parse(ex_r.content) - assert o.headers['version'] == '1' - - def test_admin_url(self): - for m in [Skola, Resitel, Rocnik, Cislo, Problem, Reseni, Nastaveni]: - o = m.objects.first() - url = o.admin_url() - assert url - view = resolve(url) - assert view - - def test_verejne_url(self): - for m in [Rocnik, Cislo, Problem]: - p = Problem.objects.first() - url = p.verejne_url() - assert url - view = resolve(url) - assert view - - def test_ovvpfile(self): - filetext = "H1\ta\nH2\tb\tc\n\nx\ty\tz\n0\t1\t2\n3\t4\t5\n" - o = ovvpfile.parse(filetext) - assert len(o.headers) == 2 - assert o.headers['H2'] == 'b\tc' - - assert o.columns == ['x','y','z'] - assert len(o.rows) == 2 - assert o.rows[0]['z'] == '2' - - t = o.to_string() - assert t == filetext - - def test_roman(self): - for i in [0, 1, 23, 2015, 1999, 42, 4, 400, 78, 4321, 8765, 999]: - r = utils.roman(i) - fr = utils.from_roman(r) - assert fr == i + def setUp(self): + create_test_data(size=2) + self.client = Client() + + def tearDown(self): + call_command('flush', noinput=True, verbosity=0, interactive=False) + self.cleint = None + + def test_rocniky(self): + r19 = Rocnik.objects.get(rocnik=21) + self.assertEqual(r19.roman(), 'XXI') + + def test_render_cislo_e2e(self): + cs = Cislo.objects.all() + for c in cs[:4]: + url = c.verejne_url() + r = self.client.get(url) + assert r.status_code == 200 + assert len(r.content) >= 100 + # TODO: Validate cntent as HTML + + def test_render_problem_e2e(self): + ps = Problem.objects.all() + for p in ps[:4]: + url = p.verejne_url() + r = self.client.get(url) + assert r.status_code == 200 + assert len(r.content) >= 100 + # TODO: Validate cntent as HTML + + def test_export_e2e(self): + i_url = '/aesop-export/index.csv' + i_r = self.client.get(i_url) + assert i_r.status_code == 200 + ls = i_r.content.strip().split('\n') + + for u in [ls[0], ls[-1]]: + ex_r = self.client.get('/aesop-export/' + u) + assert ex_r.status_code == 200 + assert len(ex_r.content) >= 100 + o = ovvpfile.parse(ex_r.content) + assert o.headers['version'] == '1' + + def test_admin_url(self): + for m in [Skola, Resitel, Rocnik, Cislo, Problem, Reseni, Nastaveni]: + o = m.objects.first() + url = o.admin_url() + assert url + view = resolve(url) + assert view + + def test_verejne_url(self): + for m in [Rocnik, Cislo, Problem]: + p = Problem.objects.first() + url = p.verejne_url() + assert url + view = resolve(url) + assert view + + def test_ovvpfile(self): + filetext = "H1\ta\nH2\tb\tc\n\nx\ty\tz\n0\t1\t2\n3\t4\t5\n" + o = ovvpfile.parse(filetext) + assert len(o.headers) == 2 + assert o.headers['H2'] == 'b\tc' + + assert o.columns == ['x','y','z'] + assert len(o.rows) == 2 + assert o.rows[0]['z'] == '2' + + t = o.to_string() + assert t == filetext + + def test_roman(self): + for i in [0, 1, 23, 2015, 1999, 42, 4, 400, 78, 4321, 8765, 999]: + r = utils.roman(i) + fr = utils.from_roman(r) + assert fr == i diff --git a/seminar/testutils.py b/seminar/testutils.py index 9e88e08a..ecc6b216 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -15,135 +15,135 @@ User = django.contrib.auth.get_user_model() @transaction.atomic def create_test_data(size = 6, rnd = None): - assert size >= 1 - # pevna pseudo-nahodnost - rnd = rnd or random.Random(x=42) - - # static URL stranky - # FIXME: nakopirovat sem vsechny z produkcni databaze - s = Site.objects.filter(name="example.com") - f = FlatPage.objects.create(url="/", title="Seminář M&M", content = "

Vítejte na stránce semináře MaM!

") - f.sites.add(s[0]) - f.save() - - # users - admin = User.objects.create_superuser(username='admin', email='', password='admin') - - usernames = ['anet', 'bara', 'cyril', 'david', 'eva', 'filip'] - orgs = [] - for org in usernames[:size]: - o = User.objects.create_user(username=org, password=org) - o.first_name = org.capitalize() - o.save() - orgs.append(o) - - # skoly - skoly = [] - skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='101 00', ulice='Krátká 5', - nazev='První ZŠ', je_zs=True, je_ss=False)) - skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='101 00', ulice='Krátká 5', - nazev='První SŠ', je_zs=False, je_ss=True)) - skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='102 00', ulice='Dlouhá 5', - nazev='Druhá SŠ', je_zs=False, je_ss=True)) - skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='103 00', ulice='Široká 3', - nazev='Třetí SŠ a ZŠ', je_zs=True, je_ss=True)) - skoly.append(Skola.objects.create(mesto = 'Ostrava', stat='CZ', psc='700 00', ulic='Hluboká 42', - nazev='Hutní gympl', je_zs=False, je_ss=True)) - skoly.append(Skola.objects.create(mesto = 'Humenné', stat='SK', psc='012 34', ulice='Pltká 1', - nazev='Sredná škuola', je_zs=False, je_ss=True)) - #FIXME pridat kontaktni osobu alespon nekde - skoly.append(zlinska = Skola.objects.create(mesto = 'Zlín', stat='CZ', psc='76001', - ulice='náměstí T.G. Masaryka 2734-9', nazev='Gymnázium a Střední jazyková škola s právem SJZ', - kratky_nazev="GaSJŠspSJZ", je_zs=True, je_ss=True)) - - # osoby - jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel'] - jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', 'Marta Iva', 'Shu Shan'] - prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', 'Pokora', - 'Koch', 'Szegedy', 'Rudý', "von Neumann", "d'Este"] - prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', 'Mrkvová', - 'Suchá', 'Lovelace', 'Holcová', 'Rui'] - prezdivka = ['Kaki', 'Hurdur', 'Maracuja', 'Bobbo', None, None, None, None, None, None, None, - 'Riki', 'Sapa', None, '', '---', 'Koko'] - domain = ['example.com', 'dolujeme.eu', 'mff.cuni.cz', 'strcprstskrzkrk.cz', 'british.co.uk', - 'splachni.to', 'haha.org'] - seznam_ulic = ['Krátká', 'Vlhká', 'Jungmanova', '17. listopadu', '4. října', 'Roztocká', - 'Forstova', 'Generála Františka Janouška', 'Náměstí Války', 'Svratecké náměstí', - 'Zelená lhota', 'Z Plynu', 'K Jezeru', 'U Kocourkova', 'Uštěpačná', 'Ostrorepská', - 'Zubří'] - seznam_mest = ['Praha', 'Brno', 'Ostrava', 'Horní Jelení', 'Dolní Zábrdovice', 'Prdelkov', - 'Stará myslivna', 'Kocourkov', 'Šalingrad', 'Medvědí hora', 'Basilej', - 'Unterschiedlich', 'Old York', 'Lancastershire', 'Vóloďháza'] - - osoby = [] - for i in range(3 * size): - pohlavi = rnd.randint(0,1) - jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) - prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) - prezdivka = rnd.choice(prezdivka) - email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)]) - telefon = [rnd.choice([k for k in range(10)]) for i in range(10)] - narozeni = datetime.date(rnd.randint(1980, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) - ulic = rnd.choice([seznam_ulic]) - cp = rnd.int(1, 99) - ulice = " ".join(ulic, cp) - mesto = rnd.choice([seznam_mest]) - psc = [rnd.choice([k for k in range(10)]) for i in range(6)] - osoby.append(Osoba.objects.create(jmeno = jmeno, prijmeni = prijmeni, prezdivka = prezdivka, - pohlavi_muz = pohlavi, email = email, telefon = telefon, datum_narozeni = narozeni, - ulice = ulice, mesto = mesto, psc = psc, - datum_registrace = datetime.date(rnd.randint(2019, 2029), rnd.randint(1, 12), - rnd.randint(1, 28)))) - #TODO pridat foto male a velke. Jak? - - # resitele a organizatori - last_rocnik = 25 - resitele = [] - 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 + assert size >= 1 + # pevna pseudo-nahodnost + rnd = rnd or random.Random(x=42) + + # static URL stranky + # FIXME: nakopirovat sem vsechny z produkcni databaze + s = Site.objects.filter(name="example.com") + f = FlatPage.objects.create(url="/", title="Seminář M&M", content = "

Vítejte na stránce semináře MaM!

") + f.sites.add(s[0]) + f.save() + + # users + admin = User.objects.create_superuser(username='admin', email='', password='admin') + + usernames = ['anet', 'bara', 'cyril', 'david', 'eva', 'filip'] + orgs = [] + for org in usernames[:size]: + o = User.objects.create_user(username=org, password=org) + o.first_name = org.capitalize() + o.save() + orgs.append(o) + + # skoly + skoly = [] + skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='101 00', ulice='Krátká 5', + nazev='První ZŠ', je_zs=True, je_ss=False)) + skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='101 00', ulice='Krátká 5', + nazev='První SŠ', je_zs=False, je_ss=True)) + skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='102 00', ulice='Dlouhá 5', + nazev='Druhá SŠ', je_zs=False, je_ss=True)) + skoly.append(Skola.objects.create(mesto = 'Praha', stat='CZ', psc='103 00', ulice='Široká 3', + nazev='Třetí SŠ a ZŠ', je_zs=True, je_ss=True)) + skoly.append(Skola.objects.create(mesto = 'Ostrava', stat='CZ', psc='700 00', ulic='Hluboká 42', + nazev='Hutní gympl', je_zs=False, je_ss=True)) + skoly.append(Skola.objects.create(mesto = 'Humenné', stat='SK', psc='012 34', ulice='Pltká 1', + nazev='Sredná škuola', je_zs=False, je_ss=True)) + #FIXME pridat kontaktni osobu alespon nekde + skoly.append(zlinska = Skola.objects.create(mesto = 'Zlín', stat='CZ', psc='76001', + ulice='náměstí T.G. Masaryka 2734-9', nazev='Gymnázium a Střední jazyková škola s právem SJZ', + kratky_nazev="GaSJŠspSJZ", je_zs=True, je_ss=True)) + + # osoby + jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel'] + jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', 'Marta Iva', 'Shu Shan'] + prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', 'Pokora', + 'Koch', 'Szegedy', 'Rudý', "von Neumann", "d'Este"] + prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', 'Mrkvová', + 'Suchá', 'Lovelace', 'Holcová', 'Rui'] + prezdivka = ['Kaki', 'Hurdur', 'Maracuja', 'Bobbo', None, None, None, None, None, None, None, + 'Riki', 'Sapa', None, '', '---', 'Koko'] + domain = ['example.com', 'dolujeme.eu', 'mff.cuni.cz', 'strcprstskrzkrk.cz', 'british.co.uk', + 'splachni.to', 'haha.org'] + seznam_ulic = ['Krátká', 'Vlhká', 'Jungmanova', '17. listopadu', '4. října', 'Roztocká', + 'Forstova', 'Generála Františka Janouška', 'Náměstí Války', 'Svratecké náměstí', + 'Zelená lhota', 'Z Plynu', 'K Jezeru', 'U Kocourkova', 'Uštěpačná', 'Ostrorepská', + 'Zubří'] + seznam_mest = ['Praha', 'Brno', 'Ostrava', 'Horní Jelení', 'Dolní Zábrdovice', 'Prdelkov', + 'Stará myslivna', 'Kocourkov', 'Šalingrad', 'Medvědí hora', 'Basilej', + 'Unterschiedlich', 'Old York', 'Lancastershire', 'Vóloďháza'] + + osoby = [] + for i in range(3 * size): + pohlavi = rnd.randint(0,1) + jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) + prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) + prezdivka = rnd.choice(prezdivka) + email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)]) + telefon = [rnd.choice([k for k in range(10)]) for i in range(10)] + narozeni = datetime.date(rnd.randint(1980, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) + ulic = rnd.choice([seznam_ulic]) + cp = rnd.int(1, 99) + ulice = " ".join(ulic, cp) + mesto = rnd.choice([seznam_mest]) + psc = [rnd.choice([k for k in range(10)]) for i in range(6)] + osoby.append(Osoba.objects.create(jmeno = jmeno, prijmeni = prijmeni, prezdivka = prezdivka, + pohlavi_muz = pohlavi, email = email, telefon = telefon, datum_narozeni = narozeni, + ulice = ulice, mesto = mesto, psc = psc, + datum_registrace = datetime.date(rnd.randint(2019, 2029), rnd.randint(1, 12), + rnd.randint(1, 28)))) + #TODO pridat foto male a velke. Jak? + + # resitele a organizatori + last_rocnik = 25 + resitele = [] + 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): @@ -162,16 +162,16 @@ def create_test_data(size = 6, rnd = None): # 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: 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() + # 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]) + nastaveni = Nastaveni.objects.create(aktualni_rocnik = Rocnik.objects.last(), + aktualni_cislo = Cislo.objects.all()[1]) diff --git a/seminar/tools.py b/seminar/tools.py index eb83332f..2f8f8666 100644 --- a/seminar/tools.py +++ b/seminar/tools.py @@ -5,78 +5,78 @@ from .models import Resitel from reversion import revisions as reversion 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 both set and equal=True, warn if not equal. - If both set and smaller=True, use the smaller one. - With pretend=True does not modify r1. - """ - a1 = r1.__getattribute__(prop) - a2 = r2.__getattribute__(prop) - if not a1: - if not pretend: - r1.__setattr__(prop, a2) - elif a2: - if equal and a1 != a2: - log.warn(u"merge: Ruzna %s: %s VS %s", prop, a1, a2) - if smaller: - if not pretend: - r1.__setattr__(prop, min(a1, a2)) + If r1.`prop` unset, use r1.`prop`=r2.`prop`. + If both set and equal=True, warn if not equal. + If both set and smaller=True, use the smaller one. + With pretend=True does not modify r1. + """ + a1 = r1.__getattribute__(prop) + a2 = r2.__getattribute__(prop) + if not a1: + if not pretend: + r1.__setattr__(prop, a2) + elif a2: + if equal and a1 != a2: + log.warn(u"merge: Ruzna %s: %s VS %s", prop, a1, a2) + if smaller: + if not pretend: + r1.__setattr__(prop, min(a1, a2)) 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. - """ - # Ma relace: skola - # Je v relaci: user, reseni, soustredeni_ucastnici, vysledky_base(VIEW) + Selze pro uzivatele s user!=NULL. S pretend=True nezmeni databazi. + """ + # Ma relace: skola + # 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 rmerge.user - assert rbase != rmerge + assert not rbase.user + assert not rmerge.user + assert rbase != rmerge - if (rbase.jmeno != rmerge.jmeno) or (rbase.prijmeni != rmerge.prijmeni): - log.error(u"merge: Ruzna jmena: %s VS %s", rbase, rmerge) - if rbase.rok_maturity != rmerge.rok_maturity: - log.error(u"merge: Ruzne roky maturity: %s VS %s", rbase.rok_maturity, rmerge.rok_maturity) + if (rbase.jmeno != rmerge.jmeno) or (rbase.prijmeni != rmerge.prijmeni): + log.error(u"merge: Ruzna jmena: %s VS %s", rbase, rmerge) + if 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(): - reversion.set_comment('Merge duplicitnich Resitelu: %r <- %r' % (rbase.pk, rmerge.pk)) + with reversion.create_revision(): + reversion.set_comment('Merge duplicitnich Resitelu: %r <- %r' % (rbase.pk, rmerge.pk)) - merge_props(rbase, rmerge, 'skola', pretend=pretend) - merge_props(rbase, rmerge, 'datum_narozeni', 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_udaje', pretend=pretend, smaller=True, equal=False) - merge_props(rbase, rmerge, 'email', pretend=pretend) - if rmerge.import_mamoper_id and not pretend: - rbase.import_mamoper_id += ' ' + rmerge.import_mamoper_id - if rmerge.poznamka and not pretend: - rbase.poznamka += ' ' + rmerge.poznamka - merge_props(rbase, rmerge, 'mesto', pretend=pretend) - merge_props(rbase, rmerge, 'pohlavi_muz', pretend=pretend) - merge_props(rbase, rmerge, 'psc', pretend=pretend) - merge_props(rbase, rmerge, 'stat', pretend=pretend) - merge_props(rbase, rmerge, 'telefon', pretend=pretend) - merge_props(rbase, rmerge, 'ulice', pretend=pretend) - merge_props(rbase, rmerge, 'zasilat', pretend=pretend) + merge_props(rbase, rmerge, 'skola', pretend=pretend) + merge_props(rbase, rmerge, 'datum_narozeni', 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_udaje', pretend=pretend, smaller=True, equal=False) + merge_props(rbase, rmerge, 'email', pretend=pretend) + if rmerge.import_mamoper_id and not pretend: + rbase.import_mamoper_id += ' ' + rmerge.import_mamoper_id + if rmerge.poznamka and not pretend: + rbase.poznamka += ' ' + rmerge.poznamka + merge_props(rbase, rmerge, 'mesto', pretend=pretend) + merge_props(rbase, rmerge, 'pohlavi_muz', pretend=pretend) + merge_props(rbase, rmerge, 'psc', pretend=pretend) + merge_props(rbase, rmerge, 'stat', pretend=pretend) + merge_props(rbase, rmerge, 'telefon', pretend=pretend) + merge_props(rbase, rmerge, 'ulice', pretend=pretend) + merge_props(rbase, rmerge, 'zasilat', pretend=pretend) - for res in rmerge.reseni.all(): - if not pretend: - res.resitel = rbase - res.save() + for res in rmerge.reseni.all(): + if not pretend: + res.resitel = rbase + res.save() - for uc in rmerge.soustredeni_ucastnici_set.all(): - if not pretend: - uc.resitel = rbase - uc.save() + for uc in rmerge.soustredeni_ucastnici_set.all(): + if not pretend: + uc.resitel = rbase + uc.save() - if not pretend: - rmerge.delete() - rbase.save() - + if not pretend: + rmerge.delete() + rbase.save() + diff --git a/seminar/urls.py b/seminar/urls.py index a224fa0c..aa41c386 100644 --- a/seminar/urls.py +++ b/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) urlpatterns = [ - # REDIRECTy - url(r'^jak-resit/$', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), + # REDIRECTy + url(r'^jak-resit/$', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), - # 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'), + # 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'), - # Archiv - url(r'^archiv/cisla/$', views.ArchivView.as_view()), - url(r'^archiv/temata/$', views.ArchivTemataView.as_view()), + # Archiv + url(r'^archiv/cisla/$', views.ArchivView.as_view()), + url(r'^archiv/temata/$', views.ArchivTemataView.as_view()), - url(r'^rocnik/(?P\d+)/$', views.RocnikView.as_view(), name='seminar_rocnik'), - #url(r'^cislo/(?P\d+)\.(?P[0-9-]+)/$', views.CisloView.as_view(), name='seminar_cislo'), - url(r'^problem/(?P\d+)/$', views.ProblemView.as_view(), name='seminar_problem'), - #url(r'^problem/(?P\d+)/(?P\d+)/$', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), + url(r'^rocnik/(?P\d+)/$', views.RocnikView.as_view(), name='seminar_rocnik'), + #url(r'^cislo/(?P\d+)\.(?P[0-9-]+)/$', views.CisloView.as_view(), name='seminar_cislo'), + url(r'^problem/(?P\d+)/$', views.ProblemView.as_view(), name='seminar_problem'), + #url(r'^problem/(?P\d+)/(?P\d+)/$', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), - # Soustredeni - url( - r'^soustredeni/probehlo/$', - views.SoustredeniListView.as_view(), - name='seminar_seznam_soustredeni' - ), - url( - r'^soustredeni/probehlo/(?P\d+)/$', - views.SoustredeniView.as_view(), - name='seminar_soustredeni' - ), - url( - r'^soustredeni/(?P\d+)/seznam_ucastniku$', - staff_member_required(views.SoustredeniUcastniciView.as_view()), - name='soustredeni_ucastnici' - ), - url( - r'^soustredeni/(?P\d+)/maily_ucastniku$', - staff_member_required(views.SoustredeniMailyUcastnikuView.as_view()), - name='maily_ucastniku' - ), - url( - r'^soustredeni/(?P\d+)/stvrzenky/(?P\d+)$', - staff_member_required(views.soustredeniStvrzenkyExportView), - name='soustredeni_stvrzenky' - ), - url( - r'^soustredeni/(?P\d+)/export_ucastniku$', - staff_member_required(views.soustredeniUcastniciExportView), - name='soustredeni_ucastnici_export' - ), - url( - r'^soustredeni/(?P\d+)/fotogalerie/', - include('galerie.urls') - ), + # Soustredeni + url( + r'^soustredeni/probehlo/$', + views.SoustredeniListView.as_view(), + name='seminar_seznam_soustredeni' + ), + url( + r'^soustredeni/probehlo/(?P\d+)/$', + views.SoustredeniView.as_view(), + name='seminar_soustredeni' + ), + url( + r'^soustredeni/(?P\d+)/seznam_ucastniku$', + staff_member_required(views.SoustredeniUcastniciView.as_view()), + name='soustredeni_ucastnici' + ), + url( + r'^soustredeni/(?P\d+)/maily_ucastniku$', + staff_member_required(views.SoustredeniMailyUcastnikuView.as_view()), + name='maily_ucastniku' + ), + url( + r'^soustredeni/(?P\d+)/stvrzenky/(?P\d+)$', + staff_member_required(views.soustredeniStvrzenkyExportView), + name='soustredeni_stvrzenky' + ), + url( + r'^soustredeni/(?P\d+)/export_ucastniku$', + staff_member_required(views.soustredeniUcastniciExportView), + name='soustredeni_ucastnici_export' + ), + url( + r'^soustredeni/(?P\d+)/fotogalerie/', + include('galerie.urls') + ), - # Zadani - url(r'^zadani/aktualni/$', views.AktualniZadaniView, name='seminar_aktualni_zadani'), - url(r'^zadani/temata/$', views.ZadaniTemataView, name='seminar_temata'), - #url(r'^zadani/vysledkova-listina/$', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), - url(r'^$', views.TitulniStranaView.as_view(), name='titulni_strana'), - url(r'^stare-novinky/$', views.StareNovinkyView.as_view(), name='stare_novinky'), + # Zadani + url(r'^zadani/aktualni/$', views.AktualniZadaniView, name='seminar_aktualni_zadani'), + url(r'^zadani/temata/$', views.ZadaniTemataView, name='seminar_temata'), + #url(r'^zadani/vysledkova-listina/$', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), + url(r'^$', views.TitulniStranaView.as_view(), name='titulni_strana'), + url(r'^stare-novinky/$', views.StareNovinkyView.as_view(), name='stare_novinky'), - # Clanky - url(r'^clanky/resitel/$', views.ClankyResitelView.as_view(), name='clanky_resitel'), - #url(r'^clanky/org/$', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'), + # Clanky + url(r'^clanky/resitel/$', views.ClankyResitelView.as_view(), name='clanky_resitel'), + #url(r'^clanky/org/$', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'), - # Aesop - url(r'^aesop-export/mam-rocnik-(?P\d+)\.csv$', export.ExportRocnikView.as_view(), name='seminar_export_rocnik'), - url(r'^aesop-export/mam-sous-(?P[\d-]+)\.csv$', export.ExportSousView.as_view(), name='seminar_export_sous'), - url(r'^aesop-export/index.csv$', export.ExportIndexView.as_view(), name='seminar_export_index'), + # Aesop + url(r'^aesop-export/mam-rocnik-(?P\d+)\.csv$', export.ExportRocnikView.as_view(), name='seminar_export_rocnik'), + url(r'^aesop-export/mam-sous-(?P[\d-]+)\.csv$', export.ExportSousView.as_view(), name='seminar_export_sous'), + url(r'^aesop-export/index.csv$', export.ExportIndexView.as_view(), name='seminar_export_index'), - # Stranky viditelne pouze pro orgy: - #url( - # r'^rocnik/(?P\d+)/vysledkovka.tex$', - # staff_member_required(views.RocnikVysledkovkaView.as_view()), - # name='seminar_cislo_vysledkovka' - #), - #url(r'^cislo/(?P\d+).(?P[0-9-]+)/vysledkovka.tex$', - # staff_member_required(views.CisloVysledkovkaView.as_view()), name='seminar_cislo_vysledkovka'), - url(r'^cislo/(?P\d+).(?P[0-9-]+)/obalky.pdf$', - staff_member_required(views.cisloObalkyView), name='seminar_cislo_obalky'), + # Stranky viditelne pouze pro orgy: + #url( + # r'^rocnik/(?P\d+)/vysledkovka.tex$', + # staff_member_required(views.RocnikVysledkovkaView.as_view()), + # name='seminar_cislo_vysledkovka' + #), + #url(r'^cislo/(?P\d+).(?P[0-9-]+)/vysledkovka.tex$', + # staff_member_required(views.CisloVysledkovkaView.as_view()), name='seminar_cislo_vysledkovka'), + url(r'^cislo/(?P\d+).(?P[0-9-]+)/obalky.pdf$', + staff_member_required(views.cisloObalkyView), name='seminar_cislo_obalky'), - #url(r'^cislo/(?P\d+).(?P[0-9-]+)/tituly.tex$', - # staff_member_required(views.TitulyView), name='seminar_cislo_titul'), - url(r'^stav$', - staff_member_required(views.StavDatabazeView), name='stav_databaze'), - url(r'^cislo/(?P\d+).(?P[0-9-]+)/obalkovani$', - staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'), - url(r'^cislo/(?P\d+).(?P[0-9-]+)/tex-download.json$', - staff_member_required(views.texDownloadView), name='seminar_tex_download'), - url(r'^soustredeni/(?P\d+)/obalky.pdf', - staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), + #url(r'^cislo/(?P\d+).(?P[0-9-]+)/tituly.tex$', + # staff_member_required(views.TitulyView), name='seminar_cislo_titul'), + url(r'^stav$', + staff_member_required(views.StavDatabazeView), name='stav_databaze'), + url(r'^cislo/(?P\d+).(?P[0-9-]+)/obalkovani$', + staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'), + url(r'^cislo/(?P\d+).(?P[0-9-]+)/tex-download.json$', + staff_member_required(views.texDownloadView), name='seminar_tex_download'), + url(r'^soustredeni/(?P\d+)/obalky.pdf', + staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), - url(r'^tex-upload/login/$', views.LoginView, name='seminar_login'), - url( - r'^tex-upload/$', - staff_member_required(views.texUploadView), - name='seminar_tex_upload' - ), - url(r'^prihlaska/$',views.get_name), + url(r'^tex-upload/login/$', views.LoginView, name='seminar_login'), + url( + r'^tex-upload/$', + staff_member_required(views.texUploadView), + name='seminar_tex_upload' + ), + url(r'^prihlaska/$',views.get_name), - # Ceka na autocomplete v3 - # url(r'^autocomplete/organizatori/$', - # staff_member_required(views.OrganizatorAutocomplete.as_view()), - # name='seminar_autocomplete_organizator') + # Ceka na autocomplete v3 + # url(r'^autocomplete/organizatori/$', + # staff_member_required(views.OrganizatorAutocomplete.as_view()), + # name='seminar_autocomplete_organizator') ] diff --git a/seminar/utils.py b/seminar/utils.py index f08e1f51..75092384 100644 --- a/seminar/utils.py +++ b/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) def histogram(seznam): - d = {} - for i in seznam: - if i not in d: - d[i] = 0 - d[i] += 1 - return d + d = {} + for i in seznam: + if i not in d: + d[i] = 0 + d[i] += 1 + return d 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): - res = "" - for i, n in roman_numerals: - res += n * (num // i) - num %= i - return res + res = "" + for i, n in roman_numerals: + res += n * (num // i) + num %= i + return res def from_roman(rom): - if not rom: - return 0 - for i, n in roman_numerals: - if rom.upper().startswith(n): - return i + from_roman(rom[len(n):]) - raise Exception('Invalid roman numeral: "%s"', rom) + if not rom: + return 0 + for i, n in roman_numerals: + if rom.upper().startswith(n): + return i + from_roman(rom[len(n):]) + raise Exception('Invalid roman numeral: "%s"', rom) def seznam_problemu(): - from .models import Problem, Resitel, Rocnik, Reseni, Cislo - problemy = [] - - # Pomocna fce k formatovani problemovych hlasek - def prb(cls, msg, objs=None): - s = u'%s: %s' % (cls.__name__, msg) - if objs: - s += u' [' - for o in objs: - try: - url = o.admin_url() - except: - url = None - if url: - s += u'%s, ' % (url, o.pk, ) - else: - s += u'%s, ' % (o.pk, ) - s = s[:-2] + u']' - problemy.append(s) - - # Duplicita jmen - jmena = {} - for r in Resitel.objects.all(): - j = r.plne_jmeno() - if j not in jmena: - jmena[j] = [] - jmena[j].append(r) - for j in jmena: - if len(jmena[j]) > 1: - prb(Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) - - # Data maturity a narození - for r in Resitel.objects.all(): - if not r.rok_maturity: - 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): - 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): - prb(Resitel, u'Podezřelé datum narození', [r]) + from .models import Problem, Resitel, Rocnik, Reseni, Cislo + problemy = [] + + # Pomocna fce k formatovani problemovych hlasek + def prb(cls, msg, objs=None): + s = u'%s: %s' % (cls.__name__, msg) + if objs: + s += u' [' + for o in objs: + try: + url = o.admin_url() + except: + url = None + if url: + s += u'%s, ' % (url, o.pk, ) + else: + s += u'%s, ' % (o.pk, ) + s = s[:-2] + u']' + problemy.append(s) + + # Duplicita jmen + jmena = {} + for r in Resitel.objects.all(): + j = r.plne_jmeno() + if j not in jmena: + jmena[j] = [] + jmena[j].append(r) + for j in jmena: + if len(jmena[j]) > 1: + prb(Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) + + # Data maturity a narození + for r in Resitel.objects.all(): + if not r.rok_maturity: + 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): + 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): + prb(Resitel, u'Podezřelé datum narození', [r]) # if not r.email: # prb(Resitel, u'Neznámý email', [r]) - return problemy + return problemy diff --git a/seminar/views.py b/seminar/views.py index d208e673..fce826d8 100644 --- a/seminar/views.py +++ b/seminar/views.py @@ -33,39 +33,39 @@ import csv def verejna_temata(rocnik): - """Vrací queryset zveřejněných témat v daném ročníku. - """ - return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod') + """Vrací queryset zveřejněných témat v daném ročníku. + """ + return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod') def AktualniZadaniView(request): - nastaveni = get_object_or_404(Nastaveni) - verejne = nastaveni.aktualni_cislo.verejne() - problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany') - ulohy = problemy.filter(typ = 'uloha').order_by('kod') - serialy = problemy.filter(typ = 'serial').order_by('kod') - jednorazove_problemy = [ulohy, serialy] - return render(request, 'seminar/zadani/AktualniZadani.html', - {'nastaveni': nastaveni, - 'jednorazove_problemy': jednorazove_problemy, - 'temata': verejna_temata(nastaveni.aktualni_rocnik), - 'verejne': verejne, - }, - ) + nastaveni = get_object_or_404(Nastaveni) + verejne = nastaveni.aktualni_cislo.verejne() + problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany') + ulohy = problemy.filter(typ = 'uloha').order_by('kod') + serialy = problemy.filter(typ = 'serial').order_by('kod') + jednorazove_problemy = [ulohy, serialy] + return render(request, 'seminar/zadani/AktualniZadani.html', + {'nastaveni': nastaveni, + 'jednorazove_problemy': jednorazove_problemy, + 'temata': verejna_temata(nastaveni.aktualni_rocnik), + 'verejne': verejne, + }, + ) def ZadaniTemataView(request): - nastaveni = get_object_or_404(Nastaveni) - temata = verejna_temata(nastaveni.aktualni_rocnik) - for t in temata: - if request.user.is_staff: - t.prispevky = t.prispevek_set.filter(problem=t) - else: - t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) - return render(request, 'seminar/zadani/Temata.html', - { - 'temata': temata, - } - ) + nastaveni = get_object_or_404(Nastaveni) + temata = verejna_temata(nastaveni.aktualni_rocnik) + for t in temata: + if request.user.is_staff: + t.prispevky = t.prispevek_set.filter(problem=t) + else: + t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) + return render(request, 'seminar/zadani/Temata.html', + { + 'temata': temata, + } + ) #def ZadaniAktualniVysledkovkaView(request): @@ -96,181 +96,181 @@ def ZadaniTemataView(request): ### Titulni strana class TitulniStranaView(generic.ListView): - model = Novinky - template_name='seminar/titulnistrana.html' - queryset = Novinky.objects.order_by('-datum')[:5] - - def get_context_data(self, **kwargs): - context = super(TitulniStranaView, self).get_context_data(**kwargs) - nastaveni = get_object_or_404(Nastaveni) - - # zjisteni spravneho terminu - if nastaveni.aktualni_cislo.datum_deadline_soustredeni: - cas_deadline_soustredeni = nastaveni.aktualni_cislo.\ - datum_deadline_soustredeni - if (datetime.now().date() <= cas_deadline_soustredeni): - cas_deadline = cas_deadline_soustredeni - deadline_soustredeni = True - else: - cas_deadline = nastaveni.aktualni_cislo.datum_deadline - deadline_soustredeni = False - else: - cas_deadline = nastaveni.aktualni_cislo.datum_deadline - deadline_soustredeni = False - - # Pokud neni zverejnene cislo nezverejnuj odpocet - if nastaveni.aktualni_cislo.verejne(): - # pokus se zjistit termin odeslani a pokud neni zadany, - # nezverejnuj odpocet - context['deadline_soustredeni'] = deadline_soustredeni - try: - context['dead'] = datetime.combine(cas_deadline, - datetime.max.time()) - context['ted'] = datetime.now() - except: - context['dead'] = None - else: - context['dead'] = None - context['deadline_soustredeni'] = deadline_soustredeni - return context + model = Novinky + template_name='seminar/titulnistrana.html' + queryset = Novinky.objects.order_by('-datum')[:5] + + def get_context_data(self, **kwargs): + context = super(TitulniStranaView, self).get_context_data(**kwargs) + nastaveni = get_object_or_404(Nastaveni) + + # zjisteni spravneho terminu + if nastaveni.aktualni_cislo.datum_deadline_soustredeni: + cas_deadline_soustredeni = nastaveni.aktualni_cislo.\ + datum_deadline_soustredeni + if (datetime.now().date() <= cas_deadline_soustredeni): + cas_deadline = cas_deadline_soustredeni + deadline_soustredeni = True + else: + cas_deadline = nastaveni.aktualni_cislo.datum_deadline + deadline_soustredeni = False + else: + cas_deadline = nastaveni.aktualni_cislo.datum_deadline + deadline_soustredeni = False + + # Pokud neni zverejnene cislo nezverejnuj odpocet + if nastaveni.aktualni_cislo.verejne(): + # pokus se zjistit termin odeslani a pokud neni zadany, + # nezverejnuj odpocet + context['deadline_soustredeni'] = deadline_soustredeni + try: + context['dead'] = datetime.combine(cas_deadline, + datetime.max.time()) + context['ted'] = datetime.now() + except: + context['dead'] = None + else: + context['dead'] = None + context['deadline_soustredeni'] = deadline_soustredeni + return context class StareNovinkyView(generic.ListView): - model = Novinky - template_name = 'seminar/stare_novinky.html' - queryset = Novinky.objects.filter(zverejneno=True).order_by('-datum') + model = Novinky + template_name = 'seminar/stare_novinky.html' + queryset = Novinky.objects.filter(zverejneno=True).order_by('-datum') ### Co je M&M # Organizatori def aktivniOrganizatori(datum=date.today()): - return Organizator.objects.exclude( - organizuje_do__isnull=False, - organizuje_do__lt=datum - ).order_by('user__first_name') + return Organizator.objects.exclude( + organizuje_do__isnull=False, + organizuje_do__lt=datum + ).order_by('user__first_name') class CojemamOrganizatoriView(generic.ListView): - model = Organizator - template_name = 'seminar/cojemam/organizatori.html' - queryset = aktivniOrganizatori() + model = Organizator + template_name = 'seminar/cojemam/organizatori.html' + queryset = aktivniOrganizatori() - def get_context_data(self, **kwargs): - context = super(CojemamOrganizatoriView, self).get_context_data(**kwargs) - context['aktivni'] = True - return context + def get_context_data(self, **kwargs): + context = super(CojemamOrganizatoriView, self).get_context_data(**kwargs) + context['aktivni'] = True + return context class CojemamOrganizatoriStariView(generic.ListView): - model = Organizator - template_name = 'seminar/cojemam/organizatori.html' - queryset = Organizator.objects.exclude( - id__in=aktivniOrganizatori()).order_by('-organizuje_do') + model = Organizator + template_name = 'seminar/cojemam/organizatori.html' + queryset = Organizator.objects.exclude( + id__in=aktivniOrganizatori()).order_by('-organizuje_do') ### Archiv class ArchivView(generic.ListView): - model = Rocnik - template_name='seminar/archiv/cisla.html' - - def get_context_data(self, **kwargs): - context = super(ArchivView, self).get_context_data(**kwargs) - - vyska = 297 # px - sirka = 210 # px - - cisla = Cislo.objects.filter(verejne_db=True)[:10] - - png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") - - # seznam [(url obrázku, číslo)] - urls = [] - - for i, c in enumerate(cisla): - if not c.pdf: - continue - filename = os.path.split(c.pdf.file.name)[1].split(".")[0] - png_filename = "{}-{}px.png".format(filename, vyska) - - # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej - png_path = op.join(png_dir, png_filename) - if not op.exists(png_path) or \ - op.getmtime(png_path) < op.getmtime(c.pdf.path): - - subprocess.call([ - "convert", - "-density", "180x180", - "-geometry", "{}x{}".format(vyska, vyska), - "-background", "white", - "-flatten", - "-rotate", str(90 * i), - "{}[0]".format(c.pdf.path), # titulní strana - png_path - ]) - - urls.append( - (op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c) - ) - vyska, sirka = sirka, vyska / 2 - - tags = [] - - def spirala(urls, tags, idx): - """Rekurzivně prochází urls a generuje strom elementů do tags""" - if idx >= len(urls): - return - - img_url, cislo = urls[idx] - tags.append( - "
" - .format( - 50 if idx % 4 == 2 else 0, - 50 if idx % 4 == 1 else 0, - 50 if idx % 2 == 1 else 100, - 50 if idx > 0 and idx % 2 == 0 else 100 - ) - ) - tags.append("".format( - cislo.verejne_url(), cislo.kod() - )) - tags.append( - "" - .format( - img_url, - 50 if idx % 4 == 3 else 0, - 50 if idx % 4 == 2 else 0, - 50 if idx % 2 == 0 else 100, - 50 if idx % 2 == 1 else 100 - ) - ) - tags.append("") - spirala(urls, tags, idx + 1) - tags.append("
") - spirala(urls, tags, 0) - - context["nahledy"] = "\n".join(tags) - return context + model = Rocnik + template_name='seminar/archiv/cisla.html' + + def get_context_data(self, **kwargs): + context = super(ArchivView, self).get_context_data(**kwargs) + + vyska = 297 # px + sirka = 210 # px + + cisla = Cislo.objects.filter(verejne_db=True)[:10] + + png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") + + # seznam [(url obrázku, číslo)] + urls = [] + + for i, c in enumerate(cisla): + if not c.pdf: + continue + filename = os.path.split(c.pdf.file.name)[1].split(".")[0] + png_filename = "{}-{}px.png".format(filename, vyska) + + # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej + png_path = op.join(png_dir, png_filename) + if not op.exists(png_path) or \ + op.getmtime(png_path) < op.getmtime(c.pdf.path): + + subprocess.call([ + "convert", + "-density", "180x180", + "-geometry", "{}x{}".format(vyska, vyska), + "-background", "white", + "-flatten", + "-rotate", str(90 * i), + "{}[0]".format(c.pdf.path), # titulní strana + png_path + ]) + + urls.append( + (op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c) + ) + vyska, sirka = sirka, vyska / 2 + + tags = [] + + def spirala(urls, tags, idx): + """Rekurzivně prochází urls a generuje strom elementů do tags""" + if idx >= len(urls): + return + + img_url, cislo = urls[idx] + tags.append( + "
" + .format( + 50 if idx % 4 == 2 else 0, + 50 if idx % 4 == 1 else 0, + 50 if idx % 2 == 1 else 100, + 50 if idx > 0 and idx % 2 == 0 else 100 + ) + ) + tags.append("".format( + cislo.verejne_url(), cislo.kod() + )) + tags.append( + "" + .format( + img_url, + 50 if idx % 4 == 3 else 0, + 50 if idx % 4 == 2 else 0, + 50 if idx % 2 == 0 else 100, + 50 if idx % 2 == 1 else 100 + ) + ) + tags.append("") + spirala(urls, tags, idx + 1) + tags.append("
") + spirala(urls, tags, 0) + + context["nahledy"] = "\n".join(tags) + return context def sloupec_s_poradim(vysledky): - # počet řešitelů ve výsledkovce nad aktuálním - lepsich_resitelu = 0 - - poradi_l = [] - # projdeme skupiny řešitelů se stejným počtem bodů - for skupina in (list(x) for _, x in groupby(vysledky, lambda x: x.body)): - - # připravíme si obsahy buněk ve sloupci pořadí pro skupinu - if len(skupina) == 1: - poradi_l += ["{}.".format(lepsich_resitelu + 1)] - # je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního - else: - poradi_l += [u"{}.–{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1) - lepsich_resitelu += len(skupina) + # počet řešitelů ve výsledkovce nad aktuálním + lepsich_resitelu = 0 + + poradi_l = [] + # projdeme skupiny řešitelů se stejným počtem bodů + for skupina in (list(x) for _, x in groupby(vysledky, lambda x: x.body)): + + # připravíme si obsahy buněk ve sloupci pořadí pro skupinu + if len(skupina) == 1: + poradi_l += ["{}.".format(lepsich_resitelu + 1)] + # je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního + else: + poradi_l += [u"{}.–{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1) + lepsich_resitelu += len(skupina) #pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu - return poradi_l + return poradi_l #def vysledkovka_rocniku(rocnik, jen_verejne=True): @@ -332,50 +332,50 @@ def sloupec_s_poradim(vysledky): class RocnikView(generic.DetailView): - model = Rocnik - template_name = 'seminar/archiv/rocnik.html' + model = Rocnik + template_name = 'seminar/archiv/rocnik.html' - # Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) - def get_object(self, queryset=None): - if queryset is None: - queryset = self.get_queryset() - rocnik_arg = self.kwargs.get('rocnik') - queryset = queryset.filter(rocnik=rocnik_arg) + # Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) + def get_object(self, queryset=None): + if queryset is None: + queryset = self.get_queryset() + rocnik_arg = self.kwargs.get('rocnik') + queryset = queryset.filter(rocnik=rocnik_arg) - try: - obj = queryset.get() - except queryset.model.DoesNotExist: - raise Http404(_("No %(verbose_name)s found matching the query") % - {'verbose_name': queryset.model._meta.verbose_name}) - return obj + try: + obj = queryset.get() + except queryset.model.DoesNotExist: + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) + return obj - def get_context_data(self, **kwargs): - context = super(RocnikView, self).get_context_data(**kwargs) + def get_context_data(self, **kwargs): + context = super(RocnikView, self).get_context_data(**kwargs) - #context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) - #context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) - context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) + #context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) + #context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) + context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) - return context + return context class ProblemView(generic.DetailView): - model = Problem + model = Problem - def _je_clanek(self, problem): - return problem.typ in [Problem.TYP_ORG_CLANEK, Problem.TYP_RES_CLANEK] + def _je_clanek(self, problem): + return problem.typ in [Problem.TYP_ORG_CLANEK, Problem.TYP_RES_CLANEK] - def get_template_names(self, **kwargs): - context = super(ProblemView, self).get_context_data(**kwargs) - return ['seminar/archiv/problem_' + ('clanek.html' if self._je_clanek(context['problem']) else 'uloha_tema.html')] + def get_template_names(self, **kwargs): + context = super(ProblemView, self).get_context_data(**kwargs) + return ['seminar/archiv/problem_' + ('clanek.html' if self._je_clanek(context['problem']) else 'uloha_tema.html')] - def get_context_data(self, **kwargs): - context = super(ProblemView, self).get_context_data(**kwargs) - if not context['problem'].verejne() and not self.request.user.is_staff: - raise PermissionDenied() - if context['problem'].typ == Problem.TYP_RES_CLANEK: - context['reseni'] = Reseni.objects.filter(problem=context['problem']).select_related('resitel').order_by('resitel__prijmeni') - return context + def get_context_data(self, **kwargs): + context = super(ProblemView, self).get_context_data(**kwargs) + if not context['problem'].verejne() and not self.request.user.is_staff: + raise PermissionDenied() + if context['problem'].typ == Problem.TYP_RES_CLANEK: + context['reseni'] = Reseni.objects.filter(problem=context['problem']).select_related('resitel').order_by('resitel__prijmeni') + return context ## Prispevek byl zrusen, mozna ale bude podobny nahled na neco jineho. #class PrispevekView(generic.DetailView): @@ -420,7 +420,7 @@ class ProblemView(generic.DetailView): class RadekVysledkovky(object): - pass + pass #class CisloView(generic.DetailView): # model = Cislo @@ -502,9 +502,9 @@ class RadekVysledkovky(object): # return context class ArchivTemataView(generic.ListView): - model = Problem - template_name = 'seminar/archiv/temata.html' - queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') + model = Problem + template_name = 'seminar/archiv/temata.html' + queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') ### Generovani vysledkovky @@ -526,14 +526,14 @@ class ArchivTemataView(generic.ListView): ### Generovani obalek class CisloObalkyStruct: - rocnik = None - cisla = None + rocnik = None + cisla = None # Vraci QuerySet aktualnich resitelu = nekdy neco poslali, ale jeste neodmaturovali def aktualniResitele(rocnik): - letos = Rocnik.objects.get(rocnik = rocnik) - return Resitel.objects.filter(rok_maturity__gt = letos.prvni_rok) + letos = Rocnik.objects.get(rocnik = rocnik) + return Resitel.objects.filter(rok_maturity__gt = letos.prvni_rok) # # ALERT: pokud nekdo nema vypleny rok maturity, tak neni aktualni, protoze Karel Tesar a jini # return Resitel.objects.filter(Q(rok_maturity__gt = letos.prvni_rok)|Q(rok_maturity = None)) @@ -542,62 +542,62 @@ def aktualniResitele(rocnik): # (pokud je aktualni cislo mensi nez 3, pak (letos || loni) neco poslali # jinak letos neco poslali) def aktivniResitele(rocnik,cislo): - letos = CisloObalkyStruct() - loni = CisloObalkyStruct() + letos = CisloObalkyStruct() + loni = CisloObalkyStruct() - aktualni_resitele = aktualniResitele(rocnik) + aktualni_resitele = aktualniResitele(rocnik) - letos.rocnik = Rocnik.objects.get(rocnik = rocnik) - loni.rocnik = Rocnik.objects.get(rocnik = int(rocnik)-1) - letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo) - loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik) - if int(cislo) > 3: - problemy = Problem.objects.filter(cislo_zadani__in = letos.cisla) - else: - problemy = Problem.objects.filter(Q(cislo_zadani__in = letos.cisla)|Q(cislo_zadani__in = loni.cisla)) - resitele = aktualni_resitele.filter(reseni__in = Reseni.objects.filter(problem__in=problemy)).distinct() - return resitele + letos.rocnik = Rocnik.objects.get(rocnik = rocnik) + loni.rocnik = Rocnik.objects.get(rocnik = int(rocnik)-1) + letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo) + loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik) + if int(cislo) > 3: + problemy = Problem.objects.filter(cislo_zadani__in = letos.cisla) + else: + problemy = Problem.objects.filter(Q(cislo_zadani__in = letos.cisla)|Q(cislo_zadani__in = loni.cisla)) + resitele = aktualni_resitele.filter(reseni__in = Reseni.objects.filter(problem__in=problemy)).distinct() + return resitele def cisloObalkyView(request,rocnik,cislo): - return obalkyView(request,aktivniResitele(rocnik,cislo)) + return obalkyView(request,aktivniResitele(rocnik,cislo)) def obalkyView(request,resitele): - tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content + tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content - tempdir = tempfile.mkdtemp() - with open(tempdir+"/obalky.tex","w") as texfile: - texfile.write(tex) - shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.eps'),tempdir) - subprocess.call(["pdflatex","obalky.tex"],cwd = tempdir) + tempdir = tempfile.mkdtemp() + with open(tempdir+"/obalky.tex","w") as texfile: + texfile.write(tex) + shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.eps'),tempdir) + subprocess.call(["pdflatex","obalky.tex"],cwd = tempdir) - with open(tempdir+"/obalky.pdf","rb") as pdffile: - response = HttpResponse(pdffile.read(),content_type='application/pdf') - shutil.rmtree(tempdir) - return response + with open(tempdir+"/obalky.pdf","rb") as pdffile: + response = HttpResponse(pdffile.read(),content_type='application/pdf') + shutil.rmtree(tempdir) + return response def obalkovaniView(request, rocnik, cislo): - rocnik = Rocnik.objects.get(rocnik=rocnik) - cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) - - reseni = ( - Reseni.objects.filter(cislo_body=cislo) - .order_by( - 'resitel__prijmeni', - 'resitel__jmeno', - 'problem__typ', - 'problem__kod' - ) - ) - - problemy = sorted(set(r.problem for r in reseni), key=lambda p: (p.typ, p.kod)) - return render( - request, - 'seminar/archiv/cislo_obalkovani.html', - {'cislo': cislo, 'problemy': problemy, 'reseni': reseni} - ) + rocnik = Rocnik.objects.get(rocnik=rocnik) + cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) + + reseni = ( + Reseni.objects.filter(cislo_body=cislo) + .order_by( + 'resitel__prijmeni', + 'resitel__jmeno', + 'problem__typ', + 'problem__kod' + ) + ) + + problemy = sorted(set(r.problem for r in reseni), key=lambda p: (p.typ, p.kod)) + return render( + request, + 'seminar/archiv/cislo_obalkovani.html', + {'cislo': cislo, 'problemy': problemy, 'reseni': reseni} + ) ### Tituly @@ -630,85 +630,85 @@ def obalkovaniView(request, rocnik, cislo): ### Soustredeni class SoustredeniListView(generic.ListView): - model = Soustredeni - template_name = 'seminar/soustredeni/seznam_soustredeni.html' + model = Soustredeni + template_name = 'seminar/soustredeni/seznam_soustredeni.html' class SoustredeniView(generic.DetailView): - model = Soustredeni - template_name = 'seminar/archiv/soustredeni.html' + model = Soustredeni + template_name = 'seminar/archiv/soustredeni.html' def soustredeniObalkyView(request,soustredeni): - soustredeni = get_object_or_404(Soustredeni,id = soustredeni) - return obalkyView(request,soustredeni.ucastnici.all()) + soustredeni = get_object_or_404(Soustredeni,id = soustredeni) + return obalkyView(request,soustredeni.ucastnici.all()) class SoustredeniUcastniciBaseView(generic.ListView): - model = Soustredeni_Ucastnici + model = Soustredeni_Ucastnici - def get_queryset(self): - soustredeni = get_object_or_404( - Soustredeni, - pk=self.kwargs["soustredeni"] - ) - return Soustredeni_Ucastnici.objects.filter( - soustredeni=soustredeni).select_related('resitel') + def get_queryset(self): + soustredeni = get_object_or_404( + Soustredeni, + pk=self.kwargs["soustredeni"] + ) + return Soustredeni_Ucastnici.objects.filter( + soustredeni=soustredeni).select_related('resitel') class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView): - """Seznam e-mailů řešitelů oddělených čárkami""" - model = Soustredeni_Ucastnici - template_name = 'seminar/soustredeni/maily_ucastniku.txt' + """Seznam e-mailů řešitelů oddělených čárkami""" + model = Soustredeni_Ucastnici + template_name = 'seminar/soustredeni/maily_ucastniku.txt' class SoustredeniUcastniciView(SoustredeniUcastniciBaseView): - """HTML tabulka účastníků pro tisk""" - model = Soustredeni_Ucastnici - template_name = 'seminar/soustredeni/seznam_ucastniku.html' + """HTML tabulka účastníků pro tisk""" + model = Soustredeni_Ucastnici + template_name = 'seminar/soustredeni/seznam_ucastniku.html' def soustredeniStvrzenkyExportView(request,soustredeni,first_num): - first_num = int(first_num) - soustredeni = get_object_or_404(Soustredeni,id = soustredeni) - ucastnici = Resitel.objects.filter(soustredeni=soustredeni) - for (idx,u) in enumerate(ucastnici): - u.cislo_stvrzenky = first_num+idx; - tex = render(request,'seminar/soustredeni/ucastnici.tex', {'ucastnici': ucastnici, 'datum':soustredeni.datum_zacatku }).content - - tempdir = tempfile.mkdtemp() - with open(tempdir+"/ucastnici.tex","w") as texfile: - # Pokud TeX chce ISO Latin, tak se da encode nastavit - texfile.write(tex.decode("utf-8").encode("utf-8")) - shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/stvrzenka.sty'),tempdir) - shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/stvrzenky.tex'),tempdir) - subprocess.call(["cslatex","stvrzenky.tex"],cwd = tempdir) - subprocess.call(["dvipdf","stvrzenky.dvi"],cwd = tempdir) - - with open(tempdir+"/stvrzenky.pdf","rb") as pdffile: - response = HttpResponse(pdffile.read(),content_type='application/pdf') - shutil.rmtree(tempdir) - return response + first_num = int(first_num) + soustredeni = get_object_or_404(Soustredeni,id = soustredeni) + ucastnici = Resitel.objects.filter(soustredeni=soustredeni) + for (idx,u) in enumerate(ucastnici): + u.cislo_stvrzenky = first_num+idx; + tex = render(request,'seminar/soustredeni/ucastnici.tex', {'ucastnici': ucastnici, 'datum':soustredeni.datum_zacatku }).content + + tempdir = tempfile.mkdtemp() + with open(tempdir+"/ucastnici.tex","w") as texfile: + # Pokud TeX chce ISO Latin, tak se da encode nastavit + texfile.write(tex.decode("utf-8").encode("utf-8")) + shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/stvrzenka.sty'),tempdir) + shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/stvrzenky.tex'),tempdir) + subprocess.call(["cslatex","stvrzenky.tex"],cwd = tempdir) + subprocess.call(["dvipdf","stvrzenky.dvi"],cwd = tempdir) + + with open(tempdir+"/stvrzenky.pdf","rb") as pdffile: + response = HttpResponse(pdffile.read(),content_type='application/pdf') + shutil.rmtree(tempdir) + return response def soustredeniUcastniciExportView(request,soustredeni): - soustredeni = get_object_or_404(Soustredeni,id = soustredeni) - ucastnici = Resitel.objects.filter(soustredeni=soustredeni) - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"' + soustredeni = get_object_or_404(Soustredeni,id = soustredeni) + ucastnici = Resitel.objects.filter(soustredeni=soustredeni) + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"' - writer = UnicodeWriter(response) - writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"]) - for u in ucastnici: - writer.writerow([u.jmeno, u.prijmeni, str(u.rok_maturity), u.telefon, u.email, u.ulice, u.mesto, u.psc, u.stat.name]) - return response + writer = UnicodeWriter(response) + writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"]) + for u in ucastnici: + writer.writerow([u.jmeno, u.prijmeni, str(u.rok_maturity), u.telefon, u.email, u.ulice, u.mesto, u.psc, u.stat.name]) + return response ### Články # FIXME: clanky jsou vsechny, pokud budou i neresitelske, tak se take zobrazi class ClankyResitelView(generic.ListView): - model = Problem - template_name = 'seminar/clanky/resitelske_clanky.html' - queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') + model = Problem + template_name = 'seminar/clanky/resitelske_clanky.html' + queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') # FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit #class ClankyOrganizatorView(generic.ListView): @@ -721,241 +721,241 @@ class ClankyResitelView(generic.ListView): def StavDatabazeView(request): # nastaveni = Nastaveni.objects.get() - problemy = utils.seznam_problemu() - muzi = Resitel.objects.filter(pohlavi_muz=True) - zeny = Resitel.objects.filter(pohlavi_muz=False) - return render(request, 'seminar/stav_databaze.html', - { + problemy = utils.seznam_problemu() + muzi = Resitel.objects.filter(pohlavi_muz=True) + zeny = Resitel.objects.filter(pohlavi_muz=False) + return render(request, 'seminar/stav_databaze.html', + { # 'nastaveni': nastaveni, - 'problemy': problemy, + 'problemy': problemy, - 'resitele': Resitel.objects.all(), - 'muzi': muzi, - 'zeny': zeny, - 'jmena_muzu': utils.histogram([r.jmeno for r in muzi]), - 'jmena_zen': utils.histogram([r.jmeno for r in zeny]), - }) + 'resitele': Resitel.objects.all(), + 'muzi': muzi, + 'zeny': zeny, + 'jmena_muzu': utils.histogram([r.jmeno for r in muzi]), + 'jmena_zen': utils.histogram([r.jmeno for r in zeny]), + }) @ensure_csrf_cookie def LoginView(request): - """Pro přihlášení při nahrávání z texu""" - q = request.POST - # nastavení cookie csrftoken - if not q: - return JsonResponse({"ok": 1}) - - if "username" in q: - username = q["username"] - password = q["password"] - user = authenticate(username=username, password=password) - if user is not None and user.is_staff: - login(request, user) - return JsonResponse({"ok": 1}) - else: - return JsonResponse({"error": "Neplatné uživatelské jméno nebo heslo"}) + """Pro přihlášení při nahrávání z texu""" + q = request.POST + # nastavení cookie csrftoken + if not q: + return JsonResponse({"ok": 1}) + + if "username" in q: + username = q["username"] + password = q["password"] + user = authenticate(username=username, password=password) + if user is not None and user.is_staff: + login(request, user) + return JsonResponse({"ok": 1}) + else: + return JsonResponse({"error": "Neplatné uživatelské jméno nebo heslo"}) @ensure_csrf_cookie def texUploadView(request): - def uloz_soubory(files, rocnik, cislo): - for filename, f in files: - path = os.path.join( - settings.MEDIA_ROOT, - settings.CISLO_IMG_DIR, - rocnik, - cislo, - filename - ) - - adresar = os.path.dirname(path) - if not os.path.exists(adresar): - os.makedirs(adresar) - - with open(path, "wb+") as fout: - for chunk in f.chunks(): - fout.write(chunk) - - q = request.POST - # nastavení cookie csrftoken - if not q: - return JsonResponse({"ok": 1}) - - # Odchytíme všechny výjimky a traceback pošleme v odpovědi - try: - meta = json.loads(q["meta"]) - html = q["html"] - - if meta["typ"] in ["uloha", "serial", "reseni", "tema"]: - - # Uložíme soubory - if meta["typ"] != "reseni": - c = meta["cislo"] - else: - # Řešení má nastavené číslo svojí úlohy, ale obrázky jsou - # ukládány do čísla, kde řešení vyšlo - c = meta["cislo_reseni"] - - # Zjistíme typ ukládaného problému - typy = { - "uloha": Problem.TYP_ULOHA, - "serial": Problem.TYP_SERIAL, - "reseni": Problem.TYP_ULOHA, - "tema": Problem.TYP_TEMA, - } - problem_typ = typy[meta["typ"]] - - # Pokud už problém existuje, vytáhneme jej z db a upravíme - # Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené - - # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> - # číslo/ročník se musí založit ručně v adminu - rocnik = Rocnik.objects.get(rocnik=meta["rocnik"]) - cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"]) - - existujici = Problem.objects.filter( - typ=problem_typ, - stav=Problem.STAV_ZADANY, - cislo_zadani=cislo, - kod=meta["kod"] - ) - - problem = None - if existujici: - problem = existujici[0] - elif "vytvor" in q: - # vytvoříme nový - problem = Problem( - typ=problem_typ, - stav=Problem.STAV_ZADANY, - kod=meta["kod"], - cislo_zadani=cislo - ) - else: - return JsonResponse({ - "error": "Problém neexistuje: {} {}.{} kód {}".format( - meta["typ"], meta["rocnik"], meta["cislo"], meta["kod"] - ) - }) - - uloz_soubory(request.FILES.items(), meta["rocnik"], c) - - if meta["typ"] == "reseni": - problem.text_reseni = html - - # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> - # číslo/ročník se musí založit ručně v adminu - problem.cislo_reseni = Cislo.objects.get( - rocnik=rocnik, - cislo=meta["cislo_reseni"] - ) - # při nahrávání řešení už původní zadání atd. neměníme - else: - problem.text_zadani = html - problem.nazev = meta["nazev"] - if meta["typ"] != "tema": - problem.body = meta["body"] - - problem.save() - cislo.faze = cislo.FAZE_NAHRANO - cislo.save() - - # Vrátíme id dané úlohy, aby se k ní dala případně připojit pohádka - return JsonResponse({"db_id": problem.id}) - - elif meta["typ"] == "pohadka": - uloha = Problem.objects.get(typ=Problem.TYP_ULOHA, pk=meta["uloha"]) - - # Pokud už příslušná pohádka existuje, jen ji upravíme - existujici = Pohadka.objects.filter(uloha=uloha, pred=meta["pred"]) - pohadka = None - if existujici: - pohadka = existujici[0] - else: - pohadka = Pohadka(uloha=uloha, pred=meta["pred"]) - pohadka.text = q["html"] - pohadka.save() - - return JsonResponse({"db_id": pohadka.id}) - - except Exception as e: - # Pošleme zpátky traceback, ať uživatel ví, v čem je problém - tb = "".join(traceback.format_exception(type(e), e, sys.exc_info()[2])) - return JsonResponse({"error": tb}) + def uloz_soubory(files, rocnik, cislo): + for filename, f in files: + path = os.path.join( + settings.MEDIA_ROOT, + settings.CISLO_IMG_DIR, + rocnik, + cislo, + filename + ) + + adresar = os.path.dirname(path) + if not os.path.exists(adresar): + os.makedirs(adresar) + + with open(path, "wb+") as fout: + for chunk in f.chunks(): + fout.write(chunk) + + q = request.POST + # nastavení cookie csrftoken + if not q: + return JsonResponse({"ok": 1}) + + # Odchytíme všechny výjimky a traceback pošleme v odpovědi + try: + meta = json.loads(q["meta"]) + html = q["html"] + + if meta["typ"] in ["uloha", "serial", "reseni", "tema"]: + + # Uložíme soubory + if meta["typ"] != "reseni": + c = meta["cislo"] + else: + # Řešení má nastavené číslo svojí úlohy, ale obrázky jsou + # ukládány do čísla, kde řešení vyšlo + c = meta["cislo_reseni"] + + # Zjistíme typ ukládaného problému + typy = { + "uloha": Problem.TYP_ULOHA, + "serial": Problem.TYP_SERIAL, + "reseni": Problem.TYP_ULOHA, + "tema": Problem.TYP_TEMA, + } + problem_typ = typy[meta["typ"]] + + # Pokud už problém existuje, vytáhneme jej z db a upravíme + # Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené + + # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> + # číslo/ročník se musí založit ručně v adminu + rocnik = Rocnik.objects.get(rocnik=meta["rocnik"]) + cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"]) + + existujici = Problem.objects.filter( + typ=problem_typ, + stav=Problem.STAV_ZADANY, + cislo_zadani=cislo, + kod=meta["kod"] + ) + + problem = None + if existujici: + problem = existujici[0] + elif "vytvor" in q: + # vytvoříme nový + problem = Problem( + typ=problem_typ, + stav=Problem.STAV_ZADANY, + kod=meta["kod"], + cislo_zadani=cislo + ) + else: + return JsonResponse({ + "error": "Problém neexistuje: {} {}.{} kód {}".format( + meta["typ"], meta["rocnik"], meta["cislo"], meta["kod"] + ) + }) + + uloz_soubory(request.FILES.items(), meta["rocnik"], c) + + if meta["typ"] == "reseni": + problem.text_reseni = html + + # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> + # číslo/ročník se musí založit ručně v adminu + problem.cislo_reseni = Cislo.objects.get( + rocnik=rocnik, + cislo=meta["cislo_reseni"] + ) + # při nahrávání řešení už původní zadání atd. neměníme + else: + problem.text_zadani = html + problem.nazev = meta["nazev"] + if meta["typ"] != "tema": + problem.body = meta["body"] + + problem.save() + cislo.faze = cislo.FAZE_NAHRANO + cislo.save() + + # Vrátíme id dané úlohy, aby se k ní dala případně připojit pohádka + return JsonResponse({"db_id": problem.id}) + + elif meta["typ"] == "pohadka": + uloha = Problem.objects.get(typ=Problem.TYP_ULOHA, pk=meta["uloha"]) + + # Pokud už příslušná pohádka existuje, jen ji upravíme + existujici = Pohadka.objects.filter(uloha=uloha, pred=meta["pred"]) + pohadka = None + if existujici: + pohadka = existujici[0] + else: + pohadka = Pohadka(uloha=uloha, pred=meta["pred"]) + pohadka.text = q["html"] + pohadka.save() + + return JsonResponse({"db_id": pohadka.id}) + + except Exception as e: + # Pošleme zpátky traceback, ať uživatel ví, v čem je problém + tb = "".join(traceback.format_exception(type(e), e, sys.exc_info()[2])) + return JsonResponse({"error": tb}) def texDownloadView(request, rocnik, cislo): - """View posílající JSON se zadanými a řešenými problémy pro založení čísla - """ - cislo = Cislo.objects.get(rocnik__rocnik=rocnik, cislo=cislo) - if cislo.faze == cislo.FAZE_NAHRANO: - # obsah byl nahrán z TeXu na web, už je příliš složitý - return JsonResponse( - {"error": "Obsah čísla už byl nahrán z TeXu na web."} - ) - - zadane = Problem.objects.filter( - cislo_zadani=cislo, - stav=Problem.STAV_ZADANY - ) - resene = Problem.objects.filter( - cislo_reseni=cislo, - stav=Problem.STAV_ZADANY, - typ=Problem.TYP_ULOHA - ) - pred_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=True) - po_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=False) - - response = { - "zadane": [ - { - "nazev": p.nazev, - "typ": p.typ, - "kod": p.kod, - "body": p.body, - "zadani": p.text_zadani, - "pred_pohadky": [x.text for x in pred_pohadky.filter(uloha=p)], - "po_pohadky": [x.text for x in po_pohadky.filter(uloha=p)], - } for p in zadane - ], - "resene": [ - { - "nazev": p.nazev, - "typ": p.typ, - "kod": p.kod, - "body": p.body, - "zadani": p.text_zadani, - "reseni": p.text_reseni, - "cislo_zadani": p.cislo_zadani.cislo, - } for p in resene - ], - } - - cislo.faze = Cislo.FAZE_TEX - cislo.save() - return JsonResponse(response) + """View posílající JSON se zadanými a řešenými problémy pro založení čísla + """ + cislo = Cislo.objects.get(rocnik__rocnik=rocnik, cislo=cislo) + if cislo.faze == cislo.FAZE_NAHRANO: + # obsah byl nahrán z TeXu na web, už je příliš složitý + return JsonResponse( + {"error": "Obsah čísla už byl nahrán z TeXu na web."} + ) + + zadane = Problem.objects.filter( + cislo_zadani=cislo, + stav=Problem.STAV_ZADANY + ) + resene = Problem.objects.filter( + cislo_reseni=cislo, + stav=Problem.STAV_ZADANY, + typ=Problem.TYP_ULOHA + ) + pred_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=True) + po_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=False) + + response = { + "zadane": [ + { + "nazev": p.nazev, + "typ": p.typ, + "kod": p.kod, + "body": p.body, + "zadani": p.text_zadani, + "pred_pohadky": [x.text for x in pred_pohadky.filter(uloha=p)], + "po_pohadky": [x.text for x in po_pohadky.filter(uloha=p)], + } for p in zadane + ], + "resene": [ + { + "nazev": p.nazev, + "typ": p.typ, + "kod": p.kod, + "body": p.body, + "zadani": p.text_zadani, + "reseni": p.text_reseni, + "cislo_zadani": p.cislo_zadani.cislo, + } for p in resene + ], + } + + cislo.faze = Cislo.FAZE_TEX + cislo.save() + return JsonResponse(response) ## Formulare def get_name(request): - # if this is a POST request we need to process the form data - if request.method == 'POST': - # create a form instance and populate it with data from the request: - form = NameForm(request.POST) - # check whether it's valid: - if form.is_valid(): - # process the data in form.cleaned_data as required - # ... - # redirect to a new URL: - return HttpResponseRedirect('/thanks/') - - # if a GET (or any other method) we'll create a blank form - else: - form = NameForm() - - return render(request, 'seminar/prihlaska.html', {'form': form}) + # if this is a POST request we need to process the form data + if request.method == 'POST': + # create a form instance and populate it with data from the request: + form = NameForm(request.POST) + # check whether it's valid: + if form.is_valid(): + # process the data in form.cleaned_data as required + # ... + # redirect to a new URL: + return HttpResponseRedirect('/thanks/') + + # if a GET (or any other method) we'll create a blank form + else: + form = NameForm() + + return render(request, 'seminar/prihlaska.html', {'form': form}) # Ceka na autocomplete v3 # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView):