Browse Source

Merge branch 'master' into stable

remotes/origin/temata v1.4
Bc. Petr Pecha 9 years ago
parent
commit
ff7f52c85d
  1. 15
      galerie/admin.py
  2. 49
      galerie/autocomplete_light_registry.py
  3. 22
      galerie/migrations/0006_django_imagekit.py
  4. 22
      galerie/migrations/0007_obrazek_odstranen_datum.py
  5. 124
      galerie/models.py
  6. 19
      galerie/templates/galerie/GalerieNahled.html
  7. 4
      galerie/views.py
  8. 12
      mamweb/settings_common.py
  9. 1
      mamweb/settings_prod.py
  10. 1
      mamweb/settings_test.py
  11. 28
      mamweb/static/css/mamweb.css
  12. 3
      requirements.txt
  13. 2
      seminar/admin.py
  14. 25
      seminar/migrations/0035_django_imagekit.py
  15. 76
      seminar/models.py
  16. 10
      seminar/templates/seminar/archiv/cislo.html
  17. 32
      seminar/templates/seminar/archiv/cislo_vysledkovka.tex
  18. 21
      seminar/templates/seminar/novinky.html
  19. 19
      seminar/templates/seminar/soustredeni/seznam_soustredeni.html
  20. 18
      seminar/templates/seminar/titulnistrana.html
  21. 2
      seminar/templates/seminar/zadani/AktualniVysledkovka.html
  22. 15
      seminar/templates/seminar/zadani/AktualniZadani.html
  23. 63
      seminar/views.py

15
galerie/admin.py

@ -3,6 +3,9 @@
from galerie.models import Obrazek, Galerie from galerie.models import Obrazek, Galerie
from django.contrib import admin from django.contrib import admin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django import forms
from django.db import models
import autocomplete_light
# akction # akction
@ -28,16 +31,24 @@ def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset):
class GalerieInline(admin.TabularInline): class GalerieInline(admin.TabularInline):
model = Obrazek 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): class ObrazekAdmin(admin.ModelAdmin):
list_display = ('obrazek_velky', 'nazev', 'popis') list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag')
class GalerieAdmin(admin.ModelAdmin): class GalerieAdmin(admin.ModelAdmin):
form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek'])
model = Galerie model = Galerie
fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi')
list_display = ('nazev', 'pk', 'datum_zmeny', 'zobrazit', 'soustredeni') list_display = ('nazev', 'pk', 'poradi', 'datum_zmeny', 'zobrazit', 'soustredeni')
inlines = [GalerieInline] inlines = [GalerieInline]
actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] 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(Obrazek, ObrazekAdmin)
admin.site.register(Galerie, GalerieAdmin) admin.site.register(Galerie, GalerieAdmin)

49
galerie/autocomplete_light_registry.py

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import autocomplete_light
from models import Obrazek, Galerie
from views import cesta_od_korene
class ObrazekAutocomplete(autocomplete_light.AutocompleteModelBase):
model = Obrazek
search_fields = ['nazev', 'popis']
split_words = True
limit_choices = 15
attrs = {
# This will set the input placeholder attribute:
'placeholder': u'Obrázek',
# This will set the yourlabs.Autocomplete.minimumCharacters
# options, the naming conversion is handled by jQuery
'data-autocomplete-minimum-characters': 1,
}
choice_html_format = '''
<span class="block" data-value="{}">
<span class="block">
{}
<span class="block">{}</span>
</span>
</span>
'''
def choice_label(self, obrazek):
cesta = "/".join(g.nazev for g in cesta_od_korene(obrazek.galerie))
popis = "{}<br>".format(obrazek.popis) if obrazek.popis else ""
return '{}<br>{}{}'.format(obrazek.nazev, popis, cesta)
def choice_html(self, obrazek):
"""Vrátí kus html i s obrázkem, které se pak ukazuje v nabídce"""
return self.choice_html_format.format(self.choice_value(obrazek),
obrazek.obrazek_maly_tag(), self.choice_label(obrazek))
widget_attrs={
'data-widget-maximum-values': 15,
'class': 'modern-style',
}
autocomplete_light.register(ObrazekAutocomplete)

22
galerie/migrations/0006_django_imagekit.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('galerie', '0005_obrazek_ordering_datum'),
]
operations = [
migrations.RemoveField(
model_name='obrazek',
name='obrazek_maly',
),
migrations.RemoveField(
model_name='obrazek',
name='obrazek_stredni',
),
]

22
galerie/migrations/0007_obrazek_odstranen_datum.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('galerie', '0006_django_imagekit'),
]
operations = [
migrations.AlterModelOptions(
name='obrazek',
options={'ordering': ['nazev'], 'verbose_name': 'Obr\xe1zek', 'verbose_name_plural': 'Obr\xe1zky'},
),
migrations.RemoveField(
model_name='obrazek',
name='datum',
),
]

124
galerie/models.py

@ -1,17 +1,12 @@
# coding: utf-8 # coding: utf-8
from django.db import models from django.db import models
import seminar.models #from django.db.models import Q
from django.db.models import Q
from django.utils import timezone
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFit, Transpose
from PIL import Image
from PIL.ExifTags import TAGS
import os import os
from cStringIO import StringIO
from django.core.files.base import ContentFile
from datetime import datetime
from seminar.models import Soustredeni from seminar.models import Soustredeni
@ -24,120 +19,49 @@ VIDITELNOST = (
(NIKDY, 'Nikdy'), (NIKDY, 'Nikdy'),
) )
def get_exif(fn): # tyhle funkce jsou tady jen kvůli starým migracím, které se na ně odkazují
ret = {} # až se ty migrace někdy squashnou, tak by mělo být možné funkce smazat
info = fn._getexif() def obrazek_filename_maly():
for tag, value in info.items(): pass
decoded = TAGS.get(tag, tag) def obrazek_filename_stredni():
ret[decoded] = value pass
return ret def obrazek_filename_velky():
pass
def flip_horizontal(im): return im.transpose(Image.FLIP_LEFT_RIGHT)
def flip_vertical(im): return im.transpose(Image.FLIP_TOP_BOTTOM)
def rotate_180(im): return im.transpose(Image.ROTATE_180)
def rotate_90(im): return im.transpose(Image.ROTATE_90)
def rotate_270(im): return im.transpose(Image.ROTATE_270)
def transpose(im): return rotate_90(flip_horizontal(im))
def transverse(im): return rotate_90(flip_vertical(im))
orientation_funcs = [None,
lambda x: x,
flip_horizontal,
rotate_180,
flip_vertical,
transpose,
rotate_270,
transverse,
rotate_90
]
def obrazek_filename(self, filename): def obrazek_filename(self, filename):
return obrazek_filename_obecny(self, filename, "velky")
def obrazek_filename_stredni(self, filename):
return obrazek_filename_obecny(self, filename, "stredni")
def obrazek_filename_maly(self, filename):
return obrazek_filename_obecny(self, filename, "maly")
def obrazek_filename_obecny(self, filename, typ):
gal = self.galerie gal = self.galerie
cislo_gal = force_unicode(gal.pk) cislo_gal = force_unicode(gal.pk)
cesta = "" cesta = ""
while(not gal.soustredeni): while(not gal.soustredeni):
gal = gal.galerie_up gal = gal.galerie_up
return os.path.join('Galerie', "soustredeni_" + force_unicode(gal.soustredeni.pk), "galerie_" + cislo_gal, typ, force_unicode(self.nazev)) return os.path.join('Galerie', "soustredeni_" + force_unicode(gal.soustredeni.pk), "galerie_" + cislo_gal, "velky", force_unicode(self.nazev))
class Obrazek(models.Model): class Obrazek(models.Model):
obrazek_velky = models.ImageField(upload_to=obrazek_filename, obrazek_velky = models.ImageField(upload_to=obrazek_filename,
help_text = "Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.") help_text = "Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.")
obrazek_stredni = models.ImageField(upload_to=obrazek_filename_stredni, null = True, editable = False) obrazek_stredni = ImageSpecField(source='obrazek_velky',
obrazek_maly = models.ImageField(upload_to=obrazek_filename_maly, null = True, editable = False) 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) nazev = models.CharField('Název', max_length=50, blank = True, null = True)
popis = models.TextField('Popis', blank = True, null = True) popis = models.TextField('Popis', blank = True, null = True)
datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add = True) datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add = True)
datum = models.DateTimeField('Datum pořízení fotografie', blank = True, null = True)
galerie = models.ForeignKey('Galerie', blank=True, null=True) galerie = models.ForeignKey('Galerie', blank=True, null=True)
poradi = models.IntegerField('Pořadí', blank = True, null = True) poradi = models.IntegerField('Pořadí', blank = True, null = True)
def __unicode__(self): def __unicode__(self):
return self.nazev + " -- " + unicode(self.obrazek_velky.name) + " (" + str(self.datum) + ")" return self.nazev + " -- " + unicode(self.obrazek_velky.name)
class Meta: class Meta:
verbose_name = 'Obrázek' verbose_name = 'Obrázek'
verbose_name_plural = 'Obrázky' verbose_name_plural = 'Obrázky'
ordering = ['datum'] ordering = ['nazev']
def save(self):
original = Image.open(self.obrazek_velky)
# vycteni EXIFu
exif = get_exif(original)
# otoceni podle EXIFu
if exif['Orientation']:
f = orientation_funcs[exif['Orientation']]
original_otoceny = f(original)
original_otoceny.format = original.format
original = original_otoceny
# datum podle EXIfu
if exif['DateTimeOriginal']:
datum_string = ":".join(exif['DateTimeOriginal'].split(' ')).split(":")
datum_int = []
for retezec in datum_string:
datum_int.append(int(retezec))
self.datum = datetime(datum_int[0], datum_int[1], datum_int[2],
datum_int[3], datum_int[4], datum_int[5])
jmeno = os.path.basename(self.obrazek_velky.file.name)
if not self.obrazek_stredni:
Obrazek._vyrobMiniaturu(original, jmeno, 1024, self.obrazek_stredni)
if not self.obrazek_maly:
Obrazek._vyrobMiniaturu(original, jmeno, 200, self.obrazek_maly)
super(Obrazek, self).save()
@staticmethod
def _vyrobMiniaturu(original, jmeno, maximum, field):
zmensenina = Obrazek._zmensiObrazek(original, maximum)
f = StringIO()
try:
zmensenina.save(f, format=original.format)
data = ContentFile(f.getvalue())
finally:
f.close()
field.save(jmeno, data, save = False)
@staticmethod def obrazek_maly_tag(self):
def _zmensiObrazek(original, maximum): return u'<img src="{}">'.format(self.obrazek_maly.url)
"""Preskaluje obrazek tak, aby byl zachovan pomer stran a zadny rozmer obrazek_maly_tag.short_description = "Náhled"
nepresahoval maxRozmer. Pokud zadny rozmer nepresahuje maxRozmer, tak obrazek_maly_tag.allow_tags = True
vrati puvodni obrazek (tj. nedojde ke zvetseni obrazku)."""
novaVelikost = Obrazek._zmensiVelikost(original.size, maximum)
return original.resize(novaVelikost, Image.ANTIALIAS)
@staticmethod
def _zmensiVelikost(velikost, maximum):
maximum = float(maximum)
w, h = velikost
soucasneMaximum = max(w, h)
if soucasneMaximum <= maximum:
return velikost
pomer = maximum/soucasneMaximum
return (int(w * pomer), int(h * pomer))
class Galerie(models.Model): class Galerie(models.Model):

19
galerie/templates/galerie/GalerieNahled.html

@ -6,6 +6,10 @@ Galerie {{galerie.nazev}}
{% block content %} {% block content %}
{% if galerie.zobrazit > 0 %}
<div class="mam-org-only">
{% endif %}
<h2> <h2>
{% for g in cesta %} {% for g in cesta %}
{% if not forloop.last %} {% if not forloop.last %}
@ -19,7 +23,7 @@ Galerie {{galerie.nazev}}
{% if not obrazky %} {% if not obrazky %}
<div class="galerie_hlavicka"> <div class="galerie_hlavicka">
{% if galerie.titulni_obrazek %} {% if galerie.titulni_obrazek %}
<img src="{{ galerie.titulni_obrazek.obrazek_stredni.url }}" style="border: 1px solid black;"> <img src="{{ galerie.titulni_obrazek.obrazek_stredni.url }}" class="titulni_obrazek">
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
@ -45,8 +49,8 @@ Galerie {{galerie.nazev}}
{% if galerie.titulni_obrazek %} {% if galerie.titulni_obrazek %}
{% with galerie.titulni_obrazek.obrazek_maly as obrazek %} {% with galerie.titulni_obrazek.obrazek_maly as obrazek %}
<img src="{{ obrazek.url }}" <img src="{{ obrazek.url }}"
width={% widthratio obrazek.width 200 167 %} width="{% widthratio obrazek.width 167 obrazek.width %}"
height={% widthratio obrazek.height 200 167 %} /> height="{% widthratio obrazek.width 167 obrazek.height %}" />
{% endwith %} {% endwith %}
{% endif %} {% endif %}
<div> <div>
@ -76,8 +80,8 @@ Galerie {{galerie.nazev}}
{% for obrazek in obrazky %} {% for obrazek in obrazky %}
<a title="Zobrazit tuto fotografii" href="./{{obrazek.pk}}#nahoru" class="galerie_nahled"><span class="vystredeno"></span><img <a title="Zobrazit tuto fotografii" href="./{{obrazek.pk}}#nahoru" class="galerie_nahled"><span class="vystredeno"></span><img
src="{{obrazek.obrazek_maly.url}}" src="{{obrazek.obrazek_maly.url}}"
width={% widthratio obrazek.obrazek_maly.width 200 167 %} width="{{ obrazek.obrazek_maly.width }}"
height={% widthratio obrazek.obrazek_maly.height 200 167 %} /> height="{{ obrazek.obrazek_maly.height }}" />
</a> </a>
{% endfor %} {% endfor %}
<br> <br>
@ -103,4 +107,9 @@ Galerie {{galerie.nazev}}
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if galerie.zobrazit > 0 %}
</div> {# mam-org-only #}
{% endif %}
{% endblock content %} {% endblock content %}

4
galerie/views.py

@ -38,7 +38,7 @@ def nahled(request, pk, soustredeni):
if not request.user.is_staff: if not request.user.is_staff:
podgalerie = podgalerie.filter(zobrazit__lt=1) podgalerie = podgalerie.filter(zobrazit__lt=1)
obrazky = Obrazek.objects.filter(galerie = galerie).order_by('datum') obrazky = Obrazek.objects.filter(galerie = galerie)
preview = zobrazit(galerie, request) preview = zobrazit(galerie, request)
sourozenci = [] sourozenci = []
@ -82,7 +82,7 @@ def detail(request, pk, fotka, soustredeni):
galerie = get_object_or_404(Galerie, pk=pk) galerie = get_object_or_404(Galerie, pk=pk)
preview = zobrazit(galerie, request) preview = zobrazit(galerie, request)
obrazek = get_object_or_404(Obrazek, pk=fotka) obrazek = get_object_or_404(Obrazek, pk=fotka)
obrazky = galerie.obrazek_set.all().order_by('datum') obrazky = galerie.obrazek_set.all()
# vytvoreni a obslouzeni formulare # vytvoreni a obslouzeni formulare
if request.method == 'POST': if request.method == 'POST':

12
mamweb/settings_common.py

@ -111,6 +111,8 @@ INSTALLED_APPS = (
'django.contrib.flatpages', 'django.contrib.flatpages',
'django.contrib.humanize', 'django.contrib.humanize',
'imagekit',
# MaMweb # MaMweb
'mamweb', 'mamweb',
'seminar', 'seminar',
@ -155,6 +157,7 @@ CKEDITOR_IMAGE_BACKEND = 'pillow'
#CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js' #CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'
CKEDITOR_CONFIGS = { CKEDITOR_CONFIGS = {
'default': { 'default': {
'entities': False,
'toolbar': [ 'toolbar': [
['Source', 'ShowBlocks', '-', 'Maximize'], ['Source', 'ShowBlocks', '-', 'Maximize'],
['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'], ['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'],
@ -233,12 +236,3 @@ LOGGING = {
# MaM specific # MaM specific
SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni') SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni')
CKEDITOR_CONFIGS = {
'default': {
'entities': False
}
}

1
mamweb/settings_prod.py

@ -50,6 +50,7 @@ ADMINS = [
('Petr Pecha', 'nejlepsitextovyeditorjevim@gmail.com'), ('Petr Pecha', 'nejlepsitextovyeditorjevim@gmail.com'),
('Tomas Gavenciak', 'gavento@gmail.com'), ('Tomas Gavenciak', 'gavento@gmail.com'),
('Matěj Kocián', 'matej.kocian@gmail.com'), ('Matěj Kocián', 'matej.kocian@gmail.com'),
('M&M ERRORs', 'mam-errors@atrey.karlin.mff.cuni.cz'),
] ]

1
mamweb/settings_test.py

@ -50,6 +50,7 @@ import os
SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz' SERVER_EMAIL = 'mamweb-test-errors@mam.mff.cuni.cz'
ADMINS = [ ADMINS = [
('Petr Pecha', 'nejlepsitextovyeditorjevim@gmail.com'), ('Petr Pecha', 'nejlepsitextovyeditorjevim@gmail.com'),
('M&M ERRORs', 'mam-errors@atrey.karlin.mff.cuni.cz'),
] ]

28
mamweb/static/css/mamweb.css

@ -18,6 +18,15 @@ body {
border: orange 2px dashed; border: orange 2px dashed;
} }
.mam-org-only .mam-org-only {
border: 0px;
}
li.mam-org-only {
padding: 3px 0px;
margin: -2px 0px;
}
table .border-r { table .border-r {
border-right: solid 1px; border-right: solid 1px;
} }
@ -437,7 +446,7 @@ div.zadani_azad_termin {
float: none; float: none;
width: 70%; width: 70%;
margin-left: auto; margin-left: auto;
margin-right: auto margin-right: auto;
} }
} }
@ -446,7 +455,8 @@ div.zadani_azad_termin {
/* galerie */ /* galerie */
/* velká fotka */ /* velká fotka */
.galerie .obrazek { /* zmenšování spolu s oknem prohlížeče */
.galerie .obrazek, .titulni_obrazek {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
width: auto\9; /* ie8 */ width: auto\9; /* ie8 */
@ -482,7 +492,6 @@ div.zadani_azad_termin {
.galerie { .galerie {
position: relative; position: relative;
text-align: center; text-align: center;
/*width: 100%;*/
margin: 20px auto 0 auto; margin: 20px auto 0 auto;
} }
@ -503,6 +512,11 @@ div.zadani_azad_termin {
text-align: center; text-align: center;
} }
/* titulní obrázek hlavní galerie soustředění */
.titulni_obrazek {
border: 1px solid black;
}
.galerie_nahledy{ .galerie_nahledy{
/*margin: 1em 0;*/ /*margin: 1em 0;*/
margin: 0 auto 30px auto; margin: 0 auto 30px auto;
@ -649,3 +663,11 @@ div.zadani_azad_termin {
float: right; float: right;
max-width: 42%; max-width: 42%;
} }
.novinka_obrazek img {
margin-bottom: 15px;
}
div.novinka_obrazek {
width: 100%;
}

3
requirements.txt

@ -23,6 +23,7 @@ django-flat-theme==0.9.3
django-taggit==0.17 django-taggit==0.17
django-autocomplete-light==2.2.6 django-autocomplete-light==2.2.6
django-crispy-forms==1.4.0 django-crispy-forms==1.4.0
django-imagekit==3.2.7
# Comments # Comments
akismet==0.2.0 akismet==0.2.0
@ -32,7 +33,7 @@ django-contrib-comments==1.6.1
# debug tools/extensions # debug tools/extensions
django-debug-toolbar==1.3.2 django-debug-toolbar==1.4
django-extensions==1.5.6 django-extensions==1.5.6
sqlparse==0.1.16 sqlparse==0.1.16
Werkzeug==0.10.4 Werkzeug==0.10.4

2
seminar/admin.py

@ -334,7 +334,7 @@ create_modeladmin(ProblemNavrhAdmin, Problem, 'ProblemNavrh', verbose_name=u'Pro
class ProblemZadanyAdmin(ProblemAdmin): class ProblemZadanyAdmin(ProblemAdmin):
list_display = ['nazev', 'typ', 'autor', 'opravovatel', 'kod', 'cislo_zadani', 'pocet_reseni', 'verejne'] list_display = ['nazev', 'typ', 'autor', 'opravovatel', 'kod', 'cislo_zadani', 'pocet_reseni', 'verejne']
list_filter = ['typ', 'cislo_zadani__cislo', 'cislo_zadani__rocnik'] list_filter = ['typ', 'zamereni', 'cislo_zadani__cislo', 'cislo_zadani__rocnik']
inlines = [ReseniKProblemuInline] inlines = [ReseniKProblemuInline]
def get_queryset(self, request): def get_queryset(self, request):

25
seminar/migrations/0035_django_imagekit.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import imagekit.models.fields
class Migration(migrations.Migration):
dependencies = [
('seminar', '0034_reseni_forma_default_email'),
]
operations = [
migrations.RemoveField(
model_name='organizator',
name='foto_male',
),
migrations.AlterField(
model_name='organizator',
name='foto',
field=imagekit.models.fields.ProcessedImageField(help_text=b'Vlo\xc5\xbe fotografii organiz\xc3\xa1tora o libovoln\xc3\xa9 velikosti', upload_to=b'image_organizatori/velke/%Y/', null=True, verbose_name=b'Fotografie organiz\xc3\xa1tora', blank=True),
preserve_default=True,
),
]

76
seminar/models.py

@ -11,6 +11,8 @@ from django.utils.encoding import force_unicode
from django.utils.text import slugify from django.utils.text import slugify
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.cache import cache from django.core.cache import cache
from imagekit.models import ImageSpecField, ProcessedImageField
from imagekit.processors import ResizeToFit, Transpose
from PIL import Image from PIL import Image
import os import os
@ -183,6 +185,9 @@ class Resitel(SeminarModelBase):
def plne_jmeno(self): def plne_jmeno(self):
return force_unicode(u'%s %s' % (self.jmeno, self.prijmeni)) return force_unicode(u'%s %s' % (self.jmeno, self.prijmeni))
def inicial_krestni(self):
return force_unicode(u'%s.' % (self.jmeno[0]))
def __str__(self): def __str__(self):
return force_unicode(self.plne_jmeno()) return force_unicode(self.plne_jmeno())
@ -755,6 +760,12 @@ class Novinky(models.Model):
text = models.TextField('Text novinky', blank=True, null=True) text = models.TextField('Text novinky', blank=True, null=True)
obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/', obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/',
null=True, blank=True) null=True, blank=True)
obrazek_maly = ImageSpecField(source='obrazek',
processors=[
ResizeToFit(350, 200, upscale=False)
],
options={'quality': 95})
autor = models.ForeignKey(settings.AUTH_USER_MODEL, autor = models.ForeignKey(settings.AUTH_USER_MODEL,
verbose_name='Autor novinky') verbose_name='Autor novinky')
zverejneno = models.BooleanField('Zveřejněno', default="False") zverejneno = models.BooleanField('Zveřejněno', default="False")
@ -785,11 +796,19 @@ class Organizator(models.Model):
"'Přednáší na MFF'") "'Přednáší na MFF'")
strucny_popis_organizatora = models.TextField('Stručný popis organizátora', strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
null = True, blank = True) null = True, blank = True)
foto = models.ImageField('Fotografie organizátora', foto = ProcessedImageField(verbose_name='Fotografie organizátora',
upload_to='image_organizatori/velke/%Y/', null = True, blank = True, upload_to='image_organizatori/velke/%Y/', null = True, blank = True,
help_text = 'Vlož fotografii organizátora o libovolné velikosti') help_text = 'Vlož fotografii organizátora o libovolné velikosti',
foto_male = models.ImageField(upload_to='image_organizatori/male/%Y/', processors=[
null = True, blank = True, editable = False) Transpose(Transpose.AUTO),
ResizeToFit(500, 500, upscale=False)
],
options={'quality': 95})
foto_male = ImageSpecField(source='foto',
processors=[
ResizeToFit(200, 200, upscale=False)
],
options={'quality': 95})
def __str__(self): def __str__(self):
return str(self.user) return str(self.user)
@ -797,52 +816,3 @@ class Organizator(models.Model):
class Meta: class Meta:
verbose_name = 'Organizátor' verbose_name = 'Organizátor'
verbose_name_plural = 'Organizátoři' verbose_name_plural = 'Organizátoři'
def save(self):
# v databázi uložený záznam o organizátorovi
puvodni = None
# pokud už organizátor v databázi existuje, nastav puvodni
if self.id is not None:
puvodni = Organizator.objects.get(id=self.id)
# pokud nahráváme fotku
if self.foto:
# a je jiná než ta stará
if not puvodni or puvodni.foto != self.foto:
# uložíme ji
original = Image.open(self.foto)
jmeno = os.path.basename(self.foto.file.name)
Organizator._vyrobMiniaturu(original, jmeno, 500, self.foto)
Organizator._vyrobMiniaturu(original, jmeno, 200, self.foto_male)
super(Organizator, self).save()
@staticmethod
def _vyrobMiniaturu(original, jmeno, maximum, field):
zmensenina = Organizator._zmensiObrazek(original, maximum)
f = StringIO()
try:
zmensenina.save(f, format=original.format)
data = ContentFile(f.getvalue())
finally:
f.close()
field.save(jmeno, data, save = False)
@staticmethod
def _zmensiObrazek(original, maximum):
"""Preskaluje obrazek tak, aby byl zachovan pomer stran
a zadny rozmer nepresahoval maxRozmer. Pokud zadny rozmer
nepresahuje maxRozmer, tak vrati puvodni obrazek
(tj. nedojde ke zvetseni obrazku)."""
novaVelikost = Organizator._zmensiVelikost(original.size, maximum)
return original.resize(novaVelikost, Image.ANTIALIAS)
@staticmethod
def _zmensiVelikost(velikost, maximum):
maximum = float(maximum)
w, h = velikost
soucasneMaximum = max(w, h)
if soucasneMaximum <= maximum:
return velikost
pomer = maximum/soucasneMaximum
return (int(w * pomer), int(h * pomer))

10
seminar/templates/seminar/archiv/cislo.html

@ -27,6 +27,16 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% if user.is_staff %}
<div class="mam-org-only">
<h3> Orgovské odkazy </h3>
<ul>
<li><a href="obalky.pdf">Obálky (PDF)</a></li>
<li><a href="tituly.tex">Tituly (TeX)</a></li>
</ul>
</div>
{% endif %}
{% if cislo.verejna_vysledkovka %} {% if cislo.verejna_vysledkovka %}
<h3>Výsledkovka</h3> <h3>Výsledkovka</h3>
{% else %} {% else %}

32
seminar/templates/seminar/archiv/cislo_vysledkovka.tex

@ -1,33 +1,9 @@
\begin{longtable}{r|l|c|l|c \setlength{\tabcolsep}{3pt}
{% for p in problemy %} \begin{longtable}{|r|l|c|r|{% for p in problemy %}c@{\hskip.5em}{% endfor %}|r|r|}\hline
@\hskip.5em}c {% endfor %} & & & & \multicolumn{ {{ problemy|length}} }{c|}{\textbf{Úlohy}} & & \\\textbf{Poř.}& \textbf{Jméno}& \textbf{R.}& \raisebox{0.7mm}{$\sum_{-1}$}& {% for p in problemy %}{% if p.typ == "uloha" %}\textbf{r{{p.kod}}}&{% elif p.typ = "tema" %}\textbf{t{{p.kod}}}&{% else %}\textbf{ {{p.kod}} }&{% endif %}{% endfor %}\raisebox{0.7mm}{$\sum_0$}&\raisebox{0.7mm}{$\sum_1$}\\\hline
|c|r|r}
\hline
& & & & \multicolumn{ {{ problemy|length }} }{c|}{\textbf{Úlohy}} & & \\
\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} &
{% for p in problemy %}
{% if p.TYP_ULOHA %}
\textbf{ r{{ p.kod }} } &
{% else %}
\textbf{ t{{ p.kod }} } &
{% endif %}
{% endfor %}
\raisebox{0.7mm}{$\sum_0$} &
\raisebox{0.7mm}{$\sum_1$} \\
\hline
\endhead \endhead
\hline \hline
\endfoot \endfoot
{% for rv in vysledkovka %}{{rv.poradi}}&{% if rv.titul %}\titul{ {{ rv.titul}}}{% endif %}{{rv.resitel.inicial_krestni}}{{rv.resitel.prijmeni}}&{{rv.resitel.rocnik|default:""}}&{{rv.body_celkem_odjakziva}}&{% for b in rv.body_ulohy %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_celkem_rocnik|default:0}}\\
{% for rv in vysledkovka %}
{{ rv.poradi }} &
{% if rv.resitel.titul %}
\titul{ {{ rv.titul }} }
{% endif %}
{{ rv.resitel.plne_jmeno }} & {{ rv.resitel.rocnik |default:"" }} & {{ rv.body_minule }}
{% for b in rv.body %}
{{ b }} &
{% endfor %}
{{ rv.body_celkem_rocnik |default:0 }} & {{ rv.body_celkem_minule }} \\
{% endfor %} {% endfor %}
\end{longtable} \end{longtable}

21
seminar/templates/seminar/novinky.html

@ -1,21 +1,24 @@
{% for novinka in object_list %} {% for novinka in object_list %}
{# pripravene div-y na stylovani#} {# pripravene div-y na stylovani#}
<div> <div>
{% if not novinka.zverejneno and user.is_staff %}
<div class="mam-org-only">
{% endif %}
{% if novinka.zverejneno or user.is_staff %}
{# datum #} {# datum #}
<div><b>{{novinka.datum}}</b></div> <div><b>{{novinka.datum}}</b></div>
{# text #} {# text #}
{{ novinka.text | safe }} {{ novinka.text | safe }}
{# obrazek #} {# obrazek #}
{% if novinka.obrazek %} {% if novinka.obrazek %}
<div> <div class='novinka_obrazek'>
<img src='{{novinka.obrazek.url}}' {% if novinka.obrazek.height > novinka.obrazek_maly.height %}
height=' <a href='{{ novinka.obrazek.url }}'>
{% if novinka.obrazek.height > 200 %} {# vyska obrazku natvrdo #} <img src='{{ novinka.obrazek.url }}' height='{{ novinka.obrazek_maly.height }}'>
200 </a>
{% else %} {% else %}
{{novinka.obrazek.height}} <img src='{{ novinka.obrazek.url }}' height='{{ novinka.obrazek_maly.height }}'>
{% endif%} {% endif%}
'>
</div> </div>
{% endif %} {% endif %}
{# autor #} {# autor #}
@ -26,6 +29,10 @@
{{novinka.autor.last_name}} {{novinka.autor.last_name}}
</p> </p>
</div> </div>
{% endif %}
{% if not novinka.zverejneno and user.is_staff %}
</div>
{% endif %}
</div> </div>
{% endfor%} {% endfor%}

19
seminar/templates/seminar/soustredeni/seznam_soustredeni.html

@ -21,13 +21,10 @@
{# Projdi vsechna soustredeni #} {# Projdi vsechna soustredeni #}
{% for soustredeni in object_list %} {% for soustredeni in object_list %}
{# Kdyz je verejne -> zobraz #} {# Kdyz je verejne -> zobraz #}
{% if soustredeni.verejne_db or user.is_authenticated %} {% if soustredeni.verejne_db or user.is_staff %}
{% if not soustredeni.verejne_db and user.is_authenticated %} {% if not soustredeni.verejne_db and user.is_staff %}
Groups of user: {{user.groups.all}} <br> <div class="mam-org-only">
<!-- TODO pri prihlasovani ucastniku dodelat prava <!--Groups of user: {{user.groups.all}} <br>-->
jen na group org ve view -->
Toto soustředění není veřejné, vidíte ho jen proto,
že jste přihlášení. <br>
{% endif %} {% endif %}
{# misto soustredeni TODO upravit#} {# misto soustredeni TODO upravit#}
<h2> <h2>
@ -42,7 +39,7 @@
{% if soustredeni.galerie_set.all %} {% if soustredeni.galerie_set.all %}
{% for galerie in soustredeni.galerie_set.all %} {% for galerie in soustredeni.galerie_set.all %}
{% if galerie.zobrazit == 0 or user.is_staff %} {% if galerie.zobrazit == 0 or user.is_staff %}
<li> <li {% if galerie.zobrazit > 0 and user.is_staff %}class="mam-org-only"{% endif %}>
<a href="../{{soustredeni.pk}}/fotogalerie/{{galerie.pk}}">Fotogalerie</a> <a href="../{{soustredeni.pk}}/fotogalerie/{{galerie.pk}}">Fotogalerie</a>
{# TODO kdyz je titulni obrazek, tak asi i titulni obrazek #} {# TODO kdyz je titulni obrazek, tak asi i titulni obrazek #}
</li> </li>
@ -52,7 +49,8 @@
</ul> </ul>
{% if user.is_staff %} {% if user.is_staff %}
<div class="mam-org-only"> <div class="mam-org-only">
<a href="../{{soustredeni.pk}}/fotogalerie/0/new/">Vytvořit novou fotogalerii</a> <a href="../{{soustredeni.pk}}/fotogalerie/0/new/">Vytvořit novou fotogalerii</a><br>
<a href="../{{soustredeni.pk}}/obalky.pdf">Vygenerovat obálky pro účastníky</a>
</div> </div>
{% endif %} {% endif %}
@ -72,6 +70,9 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% if not soustredeni.verejne_db and user.is_staff %}
</div> {# class="mam-org-only" #}
{% endif %}
{% endif %} {% endif %}
{% empty %} {% empty %}
Žádná soustředění zatím neproběhla! Žádná soustředění zatím neproběhla!

18
seminar/templates/seminar/titulnistrana.html

@ -20,25 +20,11 @@ M&amp;M je korespondeční seminář. Několikrát do roka zdarma vydáváme ča
<div class="novinky"> <div class="novinky">
{% if dead %} {% if dead %}
<div class="odpocet"> <div class="odpocet">
<p><b>Do konce odeslání řešení zbývá:<br> <p><b>Do konce <a href="https://mam.mff.cuni.cz/zadani/aktualni/">odeslání řešení</a> zbývá:<br>
<big>{{ted|timesince:dead}}</big></b></p> <big>{{ted|timesince:dead}}</big></b></p>
<!--
{{cas_do_konce_dni}} dní
{% if cas_do_konce_dni < 5 %}
{{cas_do_konce_hodin}} hodin
{% if cas_do_konce_hodin < 5 %}
{{cas_do_konce_minut}} minut
{% if cas_do_konce_minut < 5 %}
{{cas_do_konce_sekund}}
{% endif %}
{% endif %}
{% endif %}
-->
</div> </div>
{% endif %} {% endif %}
{# Novinky #} {# Novinky #}
<h2>Novinky</h2> <h2>Novinky</h2>
{% include 'seminar/novinky.html' %} {% include 'seminar/novinky.html' %}

2
seminar/templates/seminar/zadani/AktualniVysledkovka.html

@ -19,7 +19,7 @@
{% if vysledkovka %} {% if vysledkovka %}
{% include "seminar/vysledkovka_rocnik.html" %} {% include "seminar/vysledkovka_rocnik.html" %}
{% else %} {% else %}
V tomto ročníku zatím žádné výsledky nejsou V tomto ročníku zatím žádné výsledky nejsou.
{% endif %} {% endif %}
{% if user.is_staff and vysledkovka_s_neverejnymi %} {% if user.is_staff and vysledkovka_s_neverejnymi %}

15
seminar/templates/seminar/zadani/AktualniZadani.html

@ -13,7 +13,11 @@
{% block content %} {% block content %}
<div> <div>
{% with nastaveni.aktualni_cislo as ac %} {% with nastaveni.aktualni_cislo as ac %}
{# Zobrazovani neverejnych zadani jen organizatorum #}
{% if user.is_staff or verejne %}
{% if user.is_staff and not verejne %}<div class="mam-org-only">{% endif %}
{% if ac.zadane_problemy.all %} {% if ac.zadane_problemy.all %}
<div class="zadani_azad_termin"> <div class="zadani_azad_termin">
@ -45,6 +49,12 @@
Aktuálně nejsou zadané žádné úlohy k řešení. Aktuálně nejsou zadané žádné úlohy k řešení.
{% endfor %} {% endfor %}
{% if user.is_staff and not verejne%}</div>{% endif %}
{% else %}
<h3>Aktuálně nejsou zveřejněny žádné úlohy</h3>
{% endif %}
<h3>Témata</h3> <h3>Témata</h3>
<ul> <ul>
{% for problem in temata %} {% for problem in temata %}
@ -56,7 +66,8 @@
Aktuálně nejsou zadána žádná témata k řešení. Aktuálně nejsou zadána žádná témata k řešení.
{% endfor %} {% endfor %}
</ul> </ul>
{% endwith %}
{% endwith %}
</div> </div>
{% endblock content %} {% endblock content %}

63
seminar/views.py

@ -7,6 +7,7 @@ from django.core.urlresolvers import reverse
from django.views import generic from django.views import generic
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import Http404 from django.http import Http404
from django.db.models import Q
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici
from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
@ -29,6 +30,7 @@ def verejna_temata(rocnik):
def AktualniZadaniView(request): def AktualniZadaniView(request):
nastaveni = get_object_or_404(Nastaveni) nastaveni = get_object_or_404(Nastaveni)
verejne = nastaveni.aktualni_cislo.verejne()
problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany') problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany')
ulohy = problemy.filter(typ = 'uloha').order_by('kod') ulohy = problemy.filter(typ = 'uloha').order_by('kod')
serialy = problemy.filter(typ = 'serial').order_by('kod') serialy = problemy.filter(typ = 'serial').order_by('kod')
@ -37,6 +39,7 @@ def AktualniZadaniView(request):
{'nastaveni': nastaveni, {'nastaveni': nastaveni,
'jednorazove_problemy': jednorazove_problemy, 'jednorazove_problemy': jednorazove_problemy,
'temata': verejna_temata(nastaveni.aktualni_rocnik), 'temata': verejna_temata(nastaveni.aktualni_rocnik),
'verejne': verejne,
}, },
) )
@ -66,23 +69,23 @@ def ZadaniAktualniVysledkovkaView(request):
class TitulniStranaView(generic.ListView): class TitulniStranaView(generic.ListView):
model = Novinky model = Novinky
template_name='seminar/titulnistrana.html' template_name='seminar/titulnistrana.html'
queryset = Novinky.objects.filter(zverejneno=True).order_by('-datum')[:5] queryset = Novinky.objects.order_by('-datum')[:5]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(TitulniStranaView, self).get_context_data(**kwargs) context = super(TitulniStranaView, self).get_context_data(**kwargs)
nastaveni = get_object_or_404(Nastaveni) nastaveni = get_object_or_404(Nastaveni)
cas_deadline = nastaveni.aktualni_cislo.datum_deadline cas_deadline = nastaveni.aktualni_cislo.datum_deadline
# Pokud neni zverejnene cislo nezverejnuj odpocet
if nastaveni.aktualni_cislo.verejne():
# pokus se zjistit termin odeslani a pokud neni zadany,
# nezverejnuj odpocet
try: try:
rozdil_casu = datetime.combine(cas_deadline, datetime.max.time()) \
- datetime.now()
context['cas_do_konce_dni'] = rozdil_casu.days
context['cas_do_konce_hodin'] = rozdil_casu.seconds / 3600
context['cas_do_konce_minut'] = (rozdil_casu.seconds / 60) % 60
context['cas_do_konce_sekund'] = rozdil_casu.seconds % 60
context['dead'] = datetime.combine(cas_deadline, datetime.max.time()) context['dead'] = datetime.combine(cas_deadline, datetime.max.time())
context['ted'] = datetime.now() context['ted'] = datetime.now()
except: except:
context['dead'] = None context['dead'] = None
else:
context['dead'] = None
return context return context
class StareNovinkyView(generic.ListView): class StareNovinkyView(generic.ListView):
@ -128,8 +131,9 @@ def sloupec_s_poradim(vysledky):
poradi_l += ["{}.".format(lepsich_resitelu + 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 # je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního
else: else:
poradi_l += ["{}.&ndash;{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1) poradi_l += [u"{}.{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1)
lepsich_resitelu += len(skupina) lepsich_resitelu += len(skupina)
#pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu
return poradi_l return poradi_l
@ -293,6 +297,7 @@ class CisloView(generic.DetailView):
v.poradi = poradi v.poradi = poradi
v.body_celkem_rocnik = v.body v.body_celkem_rocnik = v.body
v.body_celkem_odjakziva = VysledkyKCisluOdjakziva.objects.get(resitel=v.resitel, cislo=context['cislo']).body v.body_celkem_odjakziva = VysledkyKCisluOdjakziva.objects.get(resitel=v.resitel, cislo=context['cislo']).body
v.resitel.rocnik = v.resitel.rocnik(v.cislo.rocnik)
# je tady '', aby se nezobrazovala 0, pokud se řešitel o řešení úlohy ani nepokusil # je tady '', aby se nezobrazovala 0, pokud se řešitel o řešení úlohy ani nepokusil
v.body_ulohy = [''] * len(problemy) v.body_ulohy = [''] * len(problemy)
@ -342,28 +347,42 @@ class RocnikVysledkovkaView(RocnikView):
### Generovani obalek ### Generovani obalek
class CisloObalkyStruct: class CisloObalkyStruct:
resitele = None
rocnik = None rocnik = None
problemy = None cisla = None
def cisloObalkyView(request,rocnik,cislo):
# Vraci QuerySet aktualnich resitelu = nekdy neco poslali, ale jeste neodmaturovali
def aktualniResitele(rocnik):
letos = Rocnik.objects.filter(rocnik = rocnik).first()
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))
# Vraci QuerySet aktivnich resitelu =
# jeste neodmaturovali &&
# (pokud je aktualni cislo mensi nez 3, pak (letos || loni) neco poslali
# jinak letos neco poslali)
def aktivniResitele(rocnik,cislo):
letos = CisloObalkyStruct() letos = CisloObalkyStruct()
loni = CisloObalkyStruct() loni = CisloObalkyStruct()
letos.rocnik = Rocnik.objects.filter(rocnik = rocnik)[0] aktualni_resitele = aktualniResitele(rocnik)
loni.rocnik = Rocnik.objects.filter(rocnik = int(rocnik)-1)[0]
letos.problemy = Problem.objects.filter(cislo_zadani = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo))
loni.problemy = Problem.objects.filter(cislo_zadani = Cislo.objects.filter(rocnik=loni.rocnik))
letos.resitele = Resitel.objects.filter(reseni = Reseni.objects.filter(problem=letos.problemy)).distinct()
loni.resitele = Resitel.objects.filter(reseni = Reseni.objects.filter(problem=loni.problemy)).distinct()
loni.resitele = loni.resitele.filter(rok_maturity__gt = letos.rocnik.prvni_rok)
letos.rocnik = Rocnik.objects.filter(rocnik = rocnik).first()
loni.rocnik = Rocnik.objects.filter(rocnik = int(rocnik)-1).first()
letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo)
loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik)
if int(cislo) > 3: if int(cislo) > 3:
resitele = letos.resitele problemy = Problem.objects.filter(cislo_zadani = letos.cisla)
else: else:
resitele = list(letos.resitele) + list(loni.resitele) problemy = Problem.objects.filter(Q(cislo_zadani = letos.cisla)|Q(cislo_zadani=loni.cisla))
return obalkyView(request,resitele) resitele = aktualni_resitele.filter(reseni = Reseni.objects.filter(problem=problemy)).distinct()
return resitele
def cisloObalkyView(request,rocnik,cislo):
return obalkyView(request,aktivniResitele(rocnik,cislo))
def obalkyView(request,resitele): def obalkyView(request,resitele):

Loading…
Cancel
Save