diff --git a/galerie/admin.py b/galerie/admin.py
index 4f95c951..1441ce09 100644
--- a/galerie/admin.py
+++ b/galerie/admin.py
@@ -3,6 +3,9 @@
from galerie.models import Obrazek, Galerie
from django.contrib import admin
from django.http import HttpResponseRedirect
+from django import forms
+from django.db import models
+import autocomplete_light
# akction
@@ -27,17 +30,25 @@ def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset):
'Přepnout do režimu úprav (zneveřejní galerii)'
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):
- list_display = ('obrazek_velky', 'nazev', 'popis')
+ list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag')
-class GalerieAdmin(admin.ModelAdmin):
- model = Galerie
- fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi')
- list_display = ('nazev', 'pk', 'datum_zmeny', 'zobrazit', 'soustredeni')
- inlines = [GalerieInline]
- actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu]
+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', 'pk', 'poradi', 'datum_zmeny', 'zobrazit', 'soustredeni')
+ 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/autocomplete_light_registry.py b/galerie/autocomplete_light_registry.py
new file mode 100644
index 00000000..7b624d20
--- /dev/null
+++ b/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 = '''
+
+
+ {}
+ {}
+
+
+ '''
+
+ def choice_label(self, obrazek):
+ cesta = "/".join(g.nazev for g in cesta_od_korene(obrazek.galerie))
+ popis = "{}
".format(obrazek.popis) if obrazek.popis else ""
+ return '{}
{}{}'.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)
diff --git a/galerie/migrations/0007_obrazek_odstranen_datum.py b/galerie/migrations/0007_obrazek_odstranen_datum.py
new file mode 100644
index 00000000..94c3596b
--- /dev/null
+++ b/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',
+ ),
+ ]
diff --git a/galerie/models.py b/galerie/models.py
index 9d55a3bf..3cc4b682 100644
--- a/galerie/models.py
+++ b/galerie/models.py
@@ -1,19 +1,12 @@
# coding: utf-8
from django.db import models
-import seminar.models
-from django.db.models import Q
-from django.utils import timezone
+#from django.db.models import Q
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
-from cStringIO import StringIO
-from django.core.files.base import ContentFile
-from datetime import datetime
from seminar.models import Soustredeni
@@ -26,14 +19,6 @@ VIDITELNOST = (
(NIKDY, 'Nikdy'),
)
-def get_exif(fn):
- ret = {}
- info = fn._getexif()
- for tag, value in info.items():
- decoded = TAGS.get(tag, tag)
- ret[decoded] = value
- return ret
-
# 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():
@@ -46,40 +31,55 @@ def obrazek_filename_velky():
def obrazek_filename(self, filename):
gal = self.galerie
cislo_gal = force_unicode(gal.pk)
- cesta = ""
- while(not gal.soustredeni):
+
+ # najdi kořenovou galerii
+ while (gal.galerie_up):
gal = gal.galerie_up
- return os.path.join('Galerie', "soustredeni_" + force_unicode(gal.soustredeni.pk), "galerie_" + cislo_gal, "velky", 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)
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)
- datum = models.DateTimeField('Datum pořízení fotografie', blank = True, null = True)
- galerie = models.ForeignKey('Galerie', blank=True, null=True)
- poradi = models.IntegerField('Pořadí', blank = True, null = True)
- def __unicode__(self):
- return self.nazev + " -- " + unicode(self.obrazek_velky.name) + " (" + str(self.datum) + ")"
- class Meta:
- verbose_name = 'Obrázek'
- verbose_name_plural = 'Obrázky'
- ordering = ['datum']
- def save(self):
- original = Image.open(self.obrazek_velky)
- # vycteni EXIFu
- exif = get_exif(original)
- if exif['DateTimeOriginal']:
- datum_ints = map(int, ":".join(exif['DateTimeOriginal'].split(' ')).split(":"))
- self.datum = datetime(*datum_ints)
- super(Obrazek, self).save()
+ 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)
+
class Galerie(models.Model):
diff --git a/galerie/templates/galerie/GalerieNahled.html b/galerie/templates/galerie/GalerieNahled.html
index 9a26f101..7d9313fc 100644
--- a/galerie/templates/galerie/GalerieNahled.html
+++ b/galerie/templates/galerie/GalerieNahled.html
@@ -6,6 +6,10 @@ Galerie {{galerie.nazev}}
{% block content %}
+ {% if galerie.zobrazit > 0 %}
+
+ {% endif %}
+
Korigovátko slouží ke korigování PDF souborů. Umožňuje přidávat a komentovat
korektury a označovat je jako zanesené / irelevantní. Rovněž umožňuje o PDF
@@ -32,17 +31,17 @@ původní korekturou.
Stavy
Korektura
- - K opravě - zadaná, čeká na zanesení / zahození
-
- Zanesená - zanesená v TeXu
-
- Irelevantní - není to chyba, nebude zanesena
+
- K opravě - zadaná, čeká na zanesení / zahození
+
- Zanesená - zanesená v TeXu
+
- Irelevantní - není to chyba, nebude zanesena
- K reakci - vyžaduje reakci od autora (zatím není
implementováno)
PDF
- - Přidávání - šedé pozadí - probíhá přidávání korektur
-
- Zanášení - probíhá zanášení korektur do TeXu
-
- Zastaralé - PDF je zastaralé, nepřidávat nové korektury
+
- Přidávání - probíhá přidávání korektur
+
- Zanášení - probíhá zanášení korektur do TeXu
+
- Zastaralé - PDF je zastaralé, nepřidávat nové korektury
{% endblock content %}
diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py
index 3b222eb5..d3eaabb5 100644
--- a/mamweb/settings_common.py
+++ b/mamweb/settings_common.py
@@ -161,6 +161,7 @@ CKEDITOR_IMAGE_BACKEND = 'pillow'
#CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'
CKEDITOR_CONFIGS = {
'default': {
+ 'entities': False,
'toolbar': [
['Source', 'ShowBlocks', '-', 'Maximize'],
['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'],
@@ -241,12 +242,3 @@ LOGGING = {
SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni')
KOREKTURY_PDF_DIR = os.path.join(BASE_DIR, 'media', 'korektury','pdf')
KOREKTURY_IMG_DIR = os.path.join(BASE_DIR, 'media', 'korektury','img')
-
-
-CKEDITOR_CONFIGS = {
- 'default': {
- 'entities': False
- }
-}
-
-
diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css
index 1daecacc..a8f48643 100644
--- a/mamweb/static/css/mamweb.css
+++ b/mamweb/static/css/mamweb.css
@@ -18,6 +18,15 @@ body {
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 {
border-right: solid 1px;
}
@@ -446,7 +455,8 @@ div.zadani_azad_termin {
/* galerie */
/* velká fotka */
-.galerie .obrazek {
+/* zmenšování spolu s oknem prohlížeče */
+.galerie .obrazek, .titulni_obrazek {
max-width: 100%;
height: auto;
width: auto\9; /* ie8 */
@@ -482,7 +492,6 @@ div.zadani_azad_termin {
.galerie {
position: relative;
text-align: center;
- /*width: 100%;*/
margin: 20px auto 0 auto;
}
@@ -503,6 +512,11 @@ div.zadani_azad_termin {
text-align: center;
}
+/* titulní obrázek hlavní galerie soustředění */
+.titulni_obrazek {
+ border: 1px solid black;
+}
+
.galerie_nahledy{
/*margin: 1em 0;*/
margin: 0 auto 30px auto;
@@ -599,6 +613,14 @@ div.zadani_azad_termin {
.podgalerie_nahled img {
margin-top: 20px;
margin-bottom: 15px;
+ max-height: 125px;
+ max-width: 167px;
+}
+
+.podgalerie_nahled .nazev_galerie {
+ position: absolute;
+ width: 100%;
+ top: 160px;
}
/* plus a minus tlacitka */
diff --git a/mamweb/templates/flatpages/default.html b/mamweb/templates/flatpages/default.html
index e3f03920..b5d2d24d 100644
--- a/mamweb/templates/flatpages/default.html
+++ b/mamweb/templates/flatpages/default.html
@@ -22,6 +22,10 @@
{% with "jak-resit" as selected %}
{% include "seminar/cojemam/submenu.html" %}
{% endwith %}
+ {% elif "odmeny" in flatpage.url %}
+ {% with "odmeny" as selected %}
+ {% include "seminar/cojemam/submenu.html" %}
+ {% endwith %}
{% elif "FAQ" in flatpage.url %}
{% with "FAQ" as selected %}
{% include "seminar/cojemam/submenu.html" %}
diff --git a/seminar/admin.py b/seminar/admin.py
index 3a680fc4..8fd5aa61 100644
--- a/seminar/admin.py
+++ b/seminar/admin.py
@@ -11,7 +11,7 @@ from django.db import models
from django.contrib.auth.models import User
-from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Novinky, Organizator
+from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Novinky, Organizator
import autocomplete_light
@@ -163,6 +163,19 @@ class Soustredeni_UcastniciInline(admin.TabularInline):
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')
+
### Resitel
class ResitelAdmin(VersionAdmin):
@@ -334,7 +347,7 @@ create_modeladmin(ProblemNavrhAdmin, Problem, 'ProblemNavrh', verbose_name=u'Pro
class ProblemZadanyAdmin(ProblemAdmin):
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]
def get_queryset(self, request):
@@ -362,7 +375,7 @@ class SoustredeniAdmin(VersionAdmin):
(u'Data', {'fields': ['datum_zacatku', 'datum_konce']}),
]
list_display = ['rocnik', 'misto', 'datum_zacatku', 'typ', 'exportovat', 'verejne']
- inlines = [Soustredeni_UcastniciInline]
+ inlines = [Soustredeni_UcastniciInline, Soustredeni_OrganizatoriInline]
list_filter = ['typ', 'rocnik']
view_on_site = Soustredeni.verejne_url
actions = [
diff --git a/seminar/autocomplete_light_registry.py b/seminar/autocomplete_light_registry.py
index c7762054..76b9f6ba 100644
--- a/seminar/autocomplete_light_registry.py
+++ b/seminar/autocomplete_light_registry.py
@@ -2,7 +2,7 @@
import autocomplete_light
-from models import Skola, Resitel, Problem
+from models import Skola, Resitel, Problem, Organizator
from taggit.models import Tag
@@ -64,6 +64,38 @@ class ResitelAutocomplete(autocomplete_light.AutocompleteModelBase):
autocomplete_light.register(ResitelAutocomplete)
+class OrganizatorAutocomplete(autocomplete_light.AutocompleteModelBase):
+
+ model = Organizator
+
+ search_fields=['user__first_name', 'user__last_name', 'prezdivka']
+
+ split_words = False
+
+ limit_choices = 15
+
+ def choice_label(self, organizator):
+ return u"%s '%s' %s" % (organizator.user.first_name,
+ organizator.prezdivka,
+ organizator.user.last_name)
+
+ attrs={
+ # This will set the input placeholder attribute:
+ 'placeholder': u'Organizátor',
+ # This will set the yourlabs.Autocomplete.minimumCharacters
+ # options, the naming conversion is handled by jQuery
+ 'data-autocomplete-minimum-characters': 1,
+ }
+
+ widget_attrs={
+ 'data-widget-maximum-values': 15,
+ # Enable modern-style widget !
+ 'class': 'modern-style',
+ }
+
+autocomplete_light.register(OrganizatorAutocomplete)
+
+
class ProblemAutocomplete(autocomplete_light.AutocompleteModelBase):
diff --git a/seminar/migrations/0036_add_org_to_soustredeni.py b/seminar/migrations/0036_add_org_to_soustredeni.py
new file mode 100644
index 00000000..cd6a2aed
--- /dev/null
+++ b/seminar/migrations/0036_add_org_to_soustredeni.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('seminar', '0035_django_imagekit'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Soustredeni_Organizatori',
+ fields=[
+ ('id', models.AutoField(serialize=False, primary_key=True)),
+ ('poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka k \xfa\u010dasti organiz\xe1tora (plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)),
+ ('organizator', models.ForeignKey(verbose_name='organiz\xe1tor', to='seminar.Organizator')),
+ ('soustredeni', models.ForeignKey(verbose_name='soust\u0159ed\u011bn\xed', to='seminar.Soustredeni')),
+ ],
+ options={
+ 'ordering': ['soustredeni', 'organizator'],
+ 'db_table': 'seminar_soustredeni_organizatori',
+ 'verbose_name': '\xda\u010dast organiz\xe1tor\u016f na soust\u0159ed\u011bn\xed',
+ 'verbose_name_plural': '\xda\u010dasti organiz\xe1tor\u016f na soust\u0159ed\u011bn\xed',
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AddField(
+ model_name='soustredeni',
+ name='organizatori',
+ field=models.ManyToManyField(help_text='Seznam organiz\xe1tor\u016f soust\u0159ed\u011bn\xed', to='seminar.Organizator', verbose_name='Organiz\xe1to\u0159i soust\u0159ed\u011bn\xed', through='seminar.Soustredeni_Organizatori'),
+ preserve_default=True,
+ ),
+ ]
diff --git a/seminar/models.py b/seminar/models.py
index c33488f1..d54a5868 100644
--- a/seminar/models.py
+++ b/seminar/models.py
@@ -185,6 +185,9 @@ class Resitel(SeminarModelBase):
def plne_jmeno(self):
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):
return force_unicode(self.plne_jmeno())
@@ -576,6 +579,51 @@ class PrilohaReseni(SeminarModelBase):
return force_unicode(self.soubor)
+@reversion.register(ignore_duplicate_revisions=True)
+@python_2_unicode_compatible
+class Organizator(models.Model):
+ user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name='Osoba',
+ help_text = 'Vyber účet spřažený s organizátorem.')
+ prezdivka = models.CharField('Přezdívka', max_length = 32,
+ null = True, blank = True)
+ organizuje_od_roku = models.IntegerField('Organizuje od roku',
+ null = True, blank = True)
+ organizuje_do_roku = models.IntegerField('Organizuje do roku',
+ null = True, blank = True)
+ studuje = models.CharField('Studium aj.', max_length = 256,
+ null = True, blank = True,
+ help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', "
+ "'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo "
+ "'Přednáší na MFF'")
+ strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
+ null = True, blank = True)
+ foto = ProcessedImageField(verbose_name='Fotografie organizátora',
+ upload_to='image_organizatori/velke/%Y/', null = True, blank = True,
+ help_text = 'Vlož fotografii organizátora o libovolné velikosti',
+ processors=[
+ 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):
+ if self.prezdivka:
+ return u"%s '%s' %s" % (self.user.first_name,
+ self.prezdivka,
+ self.user.last_name)
+ else:
+ return u"%s %s" % (self.user.first_name, self.user.last_name)
+
+ class Meta:
+ verbose_name = 'Organizátor'
+ verbose_name_plural = 'Organizátoři'
+
+
@reversion.register(ignore_duplicate_revisions=True)
@python_2_unicode_compatible
class Soustredeni(SeminarModelBase):
@@ -605,6 +653,11 @@ class Soustredeni(SeminarModelBase):
ucastnici = models.ManyToManyField(Resitel, verbose_name=u'účastníci soustředění',
help_text=u'Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
+ organizatori = models.ManyToManyField(Organizator,
+ verbose_name=u'Organizátoři soustředění',
+ help_text=u'Seznam organizátorů soustředění',
+ through='Soustredeni_Organizatori')
+
text = models.TextField(u'text k soustředění (HTML)', blank=True, default='')
TYP_JARNI = 'jarni'
@@ -656,6 +709,30 @@ class Soustredeni_Ucastnici(models.Model):
return force_unicode(u'%s na %s' % (self.resitel, self.soustredeni, ))
# NOTE: Poteciální DB HOG bez select_related
+@reversion.register(ignore_duplicate_revisions=True)
+@python_2_unicode_compatible
+class Soustredeni_Organizatori(models.Model):
+
+ class Meta:
+ db_table = 'seminar_soustredeni_organizatori'
+ verbose_name = u'Účast organizátorů na soustředění'
+ verbose_name_plural = u'Účasti organizátorů na soustředění'
+ ordering = ['soustredeni', 'organizator']
+
+ # Interní ID
+ id = models.AutoField(primary_key = True)
+
+ organizator = models.ForeignKey(Organizator, verbose_name=u'organizátor')
+
+ soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění')
+
+ poznamka = models.TextField(u'neveřejná poznámka', blank=True,
+ help_text=u'Neveřejná poznámka k účasti organizátora (plain text)')
+
+ def __str__(self):
+ return force_unicode(u'%s na %s' % (self.organizator, self.soustredeni, ))
+ # NOTE: Poteciální DB HOG bez select_related
+
@python_2_unicode_compatible
class VysledkyBase(SeminarModelBase):
@@ -775,41 +852,3 @@ class Novinky(models.Model):
verbose_name_plural = 'Novinky'
-@reversion.register(ignore_duplicate_revisions=True)
-@python_2_unicode_compatible
-class Organizator(models.Model):
- user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name='Osoba',
- help_text = 'Vyber účet spřažený s organizátorem.')
- prezdivka = models.CharField('Přezdívka', max_length = 32,
- null = True, blank = True)
- organizuje_od_roku = models.IntegerField('Organizuje od roku',
- null = True, blank = True)
- organizuje_do_roku = models.IntegerField('Organizuje do roku',
- null = True, blank = True)
- studuje = models.CharField('Studium aj.', max_length = 256,
- null = True, blank = True,
- help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', "
- "'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo "
- "'Přednáší na MFF'")
- strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
- null = True, blank = True)
- foto = ProcessedImageField(verbose_name='Fotografie organizátora',
- upload_to='image_organizatori/velke/%Y/', null = True, blank = True,
- help_text = 'Vlož fotografii organizátora o libovolné velikosti',
- processors=[
- 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):
- return str(self.user)
-
- class Meta:
- verbose_name = 'Organizátor'
- verbose_name_plural = 'Organizátoři'
diff --git a/seminar/templates/seminar/archiv/cislo.html b/seminar/templates/seminar/archiv/cislo.html
index 2978cc84..a398a6cb 100644
--- a/seminar/templates/seminar/archiv/cislo.html
+++ b/seminar/templates/seminar/archiv/cislo.html
@@ -27,6 +27,16 @@
{% endfor %}
+ {% if user.is_staff %}
+
+ {% endif %}
+
{% if cislo.verejna_vysledkovka %}
Výsledkovka
{% else %}
diff --git a/seminar/templates/seminar/archiv/cislo_vysledkovka.tex b/seminar/templates/seminar/archiv/cislo_vysledkovka.tex
index e988484c..619b30e1 100644
--- a/seminar/templates/seminar/archiv/cislo_vysledkovka.tex
+++ b/seminar/templates/seminar/archiv/cislo_vysledkovka.tex
@@ -1,33 +1,9 @@
-\begin{longtable}{r|l|c|l|c
- {% for p in problemy %}
- @\hskip.5em}c {% endfor %}
- |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
+\setlength{\tabcolsep}{3pt}
+\begin{longtable}{|r|l|c|r|{% for p in problemy %}c@{\hskip.5em}{% endfor %}|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}}}&{% 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
\endhead
\hline
-\endfoot
-
-{% 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 }} \\
+\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}}\\
{% endfor %}
\end{longtable}
diff --git a/seminar/templates/seminar/archiv/problem.html b/seminar/templates/seminar/archiv/problem.html
index 67071dea..6fdc5fac 100644
--- a/seminar/templates/seminar/archiv/problem.html
+++ b/seminar/templates/seminar/archiv/problem.html
@@ -3,7 +3,7 @@
{% load comments %}
{% block content %}
-
+
{% block problem %}
{% endblock %}
diff --git a/seminar/templates/seminar/archiv/problem_uloha_tema.html b/seminar/templates/seminar/archiv/problem_uloha_tema.html
index 12ee40ac..bd68a6fb 100644
--- a/seminar/templates/seminar/archiv/problem_uloha_tema.html
+++ b/seminar/templates/seminar/archiv/problem_uloha_tema.html
@@ -8,14 +8,10 @@
{% if problem.cislo_zadani %}
Zadáno v čísle {{ problem.cislo_zadani.kod }}.
- {% if problem.cislo_reseni %}
-
Řešeno v čísle {{ problem.cislo_reseni.kod }}.
- {% endif %}
- {% else %}
- {# TODO ? #}
-
Problém {{ problem.nazev }}
{% endif %}
-
+ {% if problem.cislo_reseni %}
+
Řešeno v čísle {{ problem.cislo_reseni.kod }}.
+ {% endif %}
Zadání
{{ problem.text_zadani |safe }}
diff --git a/seminar/templates/seminar/cojemam/submenu.html b/seminar/templates/seminar/cojemam/submenu.html
index 18b6901a..f7e64f3b 100644
--- a/seminar/templates/seminar/cojemam/submenu.html
+++ b/seminar/templates/seminar/cojemam/submenu.html
@@ -4,6 +4,7 @@
diff --git a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html b/seminar/templates/seminar/soustredeni/seznam_soustredeni.html
index 121d19b0..4c863fc4 100644
--- a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html
+++ b/seminar/templates/seminar/soustredeni/seznam_soustredeni.html
@@ -21,61 +21,72 @@
{# Projdi vsechna soustredeni #}
{% for soustredeni in object_list %}
{# Kdyz je verejne -> zobraz #}
- {% if soustredeni.verejne_db or user.is_authenticated %}
- {% if not soustredeni.verejne_db and user.is_authenticated %}
- Groups of user: {{user.groups.all}}
-
- Toto soustředění není veřejné, vidíte ho jen proto,
- že jste přihlášení.
+ {% if soustredeni.verejne_db or user.is_staff %}
+ {% if not soustredeni.verejne_db and user.is_staff %}
+
+
{% endif %}
{# misto soustredeni TODO upravit#}
-
- {{soustredeni.get_typ_display}}
- {{soustredeni.misto}}
-
-
- -
- {{soustredeni.datum_zacatku}} – {{soustredeni.datum_konce}}
-
- {# Zobrazeni odkazu na galerie #}
- {% if soustredeni.galerie_set.all %}
- {% for galerie in soustredeni.galerie_set.all %}
- {% if galerie.zobrazit == 0 or user.is_staff %}
+
+ {{soustredeni.get_typ_display}}
+ {{soustredeni.misto}}
+
+
-
- Fotogalerie
- {# TODO kdyz je titulni obrazek, tak asi i titulni obrazek #}
+ {{soustredeni.datum_zacatku}} – {{soustredeni.datum_konce}}
+ {# Zobrazeni odkazu na galerie #}
+ {% if soustredeni.galerie_set.all %}
+ {% for galerie in soustredeni.galerie_set.all %}
+ {% if galerie.zobrazit == 0 or user.is_staff %}
+ - 0 and user.is_staff %}class="mam-org-only"{% endif %}>
+ Fotogalerie
+ {# TODO kdyz je titulni obrazek, tak asi i titulni obrazek #}
+
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+
+ {% if user.is_staff %}
+
{% endif %}
- {% endfor %}
- {% endif %}
-
- {% if user.is_staff %}
-
- {% endif %}
- {# popis soustredeni #}
- {% if soustredeni.text %}
- {% autoescape off %}{{soustredeni.text}}{% endautoescape %}
- {% endif %}
+ {# popis soustredeni #}
+ {% if soustredeni.text %}
+ {% autoescape off %}{{soustredeni.text}}{% endautoescape %}
+ {% endif %}
{% if user.is_authenticated %}
+
{# Účastníci #}
Soustředění se zúčastnili tito účastníci:
-
+
+
Soustředění se účastnili tito organizátoři:
+
+ {% for i in soustredeni.soustredeni_organizatori_set.all %}
+ {{i.organizator}}{% if forloop.last %}.{% else %},{% endif %}
+ {% empty %}
+ Nic!
+ {% endfor %}
+
+ {% endif %}
+
+
+ {% if not soustredeni.verejne_db and user.is_staff %}
+
{# class="mam-org-only" #}
{% endif %}
{% endif %}
- {% empty %}
- Žádná soustředění zatím neproběhla!
+ {% empty %}
+ Žádná soustředění zatím neproběhla!
{% endfor %}
{% endblock %}
diff --git a/seminar/templates/seminar/titulnistrana.html b/seminar/templates/seminar/titulnistrana.html
index c6c1a522..ae949d7e 100644
--- a/seminar/templates/seminar/titulnistrana.html
+++ b/seminar/templates/seminar/titulnistrana.html
@@ -22,23 +22,9 @@ M&M je korespondeční seminář. Několikrát do roka zdarma vydáváme ča
{% endif %}
+
{# Novinky #}
Novinky
{% include 'seminar/novinky.html' %}
diff --git a/seminar/templates/seminar/zadani/AktualniVysledkovka.html b/seminar/templates/seminar/zadani/AktualniVysledkovka.html
index 9996b567..8e0898ca 100644
--- a/seminar/templates/seminar/zadani/AktualniVysledkovka.html
+++ b/seminar/templates/seminar/zadani/AktualniVysledkovka.html
@@ -19,7 +19,7 @@
{% if vysledkovka %}
{% include "seminar/vysledkovka_rocnik.html" %}
{% else %}
- V tomto ročníku zatím žádné výsledky nejsou
+ V tomto ročníku zatím žádné výsledky nejsou.
{% endif %}
{% if user.is_staff and vysledkovka_s_neverejnymi %}
diff --git a/seminar/templates/seminar/zadani/AktualniZadani.html b/seminar/templates/seminar/zadani/AktualniZadani.html
index ff6c3cd3..a9f84a64 100644
--- a/seminar/templates/seminar/zadani/AktualniZadani.html
+++ b/seminar/templates/seminar/zadani/AktualniZadani.html
@@ -13,7 +13,11 @@
{% block content %}
- {% 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 %}
{% endif %}
{% if ac.zadane_problemy.all %}
@@ -45,6 +49,12 @@
Aktuálně nejsou zadané žádné úlohy k řešení.
{% endfor %}
+{% if user.is_staff and not verejne%}
{% endif %}
+{% else %}
+
Aktuálně nejsou zveřejněny žádné úlohy
+{% endif %}
+
+
Témata
{% for problem in temata %}
@@ -56,7 +66,8 @@
Aktuálně nejsou zadána žádná témata k řešení.
{% endfor %}
- {% endwith %}
+
+{% endwith %}
{% endblock content %}
diff --git a/seminar/views.py b/seminar/views.py
index c43799fd..93945778 100644
--- a/seminar/views.py
+++ b/seminar/views.py
@@ -1,12 +1,13 @@
# coding:utf-8
from django.shortcuts import get_object_or_404, render
-from django.http import HttpResponseRedirect
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.core.urlresolvers import reverse
+from django.core.exceptions import PermissionDenied
from django.views import generic
from django.utils.translation import ugettext as _
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 VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
@@ -29,6 +30,7 @@ def verejna_temata(rocnik):
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')
@@ -37,6 +39,7 @@ def AktualniZadaniView(request):
{'nastaveni': nastaveni,
'jednorazove_problemy': jednorazove_problemy,
'temata': verejna_temata(nastaveni.aktualni_rocnik),
+ 'verejne': verejne,
},
)
@@ -72,16 +75,16 @@ class TitulniStranaView(generic.ListView):
context = super(TitulniStranaView, self).get_context_data(**kwargs)
nastaveni = get_object_or_404(Nastaveni)
cas_deadline = nastaveni.aktualni_cislo.datum_deadline
- 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['ted'] = datetime.now()
- except:
+ # Pokud neni zverejnene cislo nezverejnuj odpocet
+ if nastaveni.aktualni_cislo.verejne():
+ # pokus se zjistit termin odeslani a pokud neni zadany,
+ # nezverejnuj odpocet
+ try:
+ context['dead'] = datetime.combine(cas_deadline, datetime.max.time())
+ context['ted'] = datetime.now()
+ except:
+ context['dead'] = None
+ else:
context['dead'] = None
return context
@@ -128,8 +131,9 @@ def sloupec_s_poradim(vysledky):
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 += ["{}.–{}.".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)
+ #pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu
return poradi_l
@@ -231,11 +235,14 @@ class ProblemView(generic.DetailView):
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
+
class RadekVysledkovky(object):
pass
@@ -293,6 +300,7 @@ class CisloView(generic.DetailView):
v.poradi = poradi
v.body_celkem_rocnik = v.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
v.body_ulohy = [''] * len(problemy)
@@ -342,28 +350,42 @@ class RocnikVysledkovkaView(RocnikView):
### Generovani obalek
class CisloObalkyStruct:
- resitele = None
rocnik = None
- problemy = None
-
-def cisloObalkyView(request,rocnik,cislo):
+ cisla = None
+
+
+# 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()
loni = CisloObalkyStruct()
- letos.rocnik = Rocnik.objects.filter(rocnik = rocnik)[0]
- 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)
-
+ aktualni_resitele = aktualniResitele(rocnik)
+
+ 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:
- resitele = letos.resitele
+ problemy = Problem.objects.filter(cislo_zadani = letos.cisla)
else:
- resitele = list(letos.resitele) + list(loni.resitele)
- return obalkyView(request,resitele)
+ problemy = Problem.objects.filter(Q(cislo_zadani = letos.cisla)|Q(cislo_zadani=loni.cisla))
+ 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):