diff --git a/galerie/TODO b/galerie/TODO new file mode 100644 index 00000000..ccfd1200 --- /dev/null +++ b/galerie/TODO @@ -0,0 +1,24 @@ +======== +| TODO | +|======| + +Aktualni +* do CSS + * nahledy + * nastylovat tabulku s nahledy + * komentare uz na nahledy? + * detail + * nahledy pred a po + * opravit prechodove sipky + * vyrobit prechodove sipky ve M&M-stylu + +Dlouhodobe +* sipky na prechazeni mezi fotkami +* hromadne PRIDANI fotek do jiz existujici galerie + +Fylozoficke +* zvolit velikosti velke a male fotky +* je potreba i jine razeni nez automaticky podle casu nebo staci podgalerie? + * napr. dve hry na dvou ruznych mistech ve stejny cas + * fotky od ucastniku ze hry (skupinky se pohybuji ve stejny cas, ale maji sled fotek) -- nestaci to pripadne vrazit do podgalerii? + diff --git a/galerie/__init__.py b/galerie/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/galerie/admin.py b/galerie/admin.py new file mode 100644 index 00000000..60af9e3d --- /dev/null +++ b/galerie/admin.py @@ -0,0 +1,43 @@ +#coding: utf-8 + +from galerie.models import Obrazek, Galerie +from django.contrib import admin +from django.http import HttpResponseRedirect + +# 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' + + +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)' + +class GalerieInline(admin.TabularInline): + model = Obrazek + +class ObrazekAdmin(admin.ModelAdmin): + list_display = ('obrazek_velky', 'nazev', 'popis') + +class GalerieAdmin(admin.ModelAdmin): + model = Galerie + fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni') + list_display = ('nazev', 'pk', 'datum_zmeny', 'zobrazit', 'soustredeni') + inlines = [GalerieInline] + actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] + +admin.site.register(Obrazek, ObrazekAdmin) +admin.site.register(Galerie, GalerieAdmin) diff --git a/galerie/forms.py b/galerie/forms.py new file mode 100644 index 00000000..3e09fe15 --- /dev/null +++ b/galerie/forms.py @@ -0,0 +1,11 @@ +#coding: utf-8 + +from django import forms +from seminar.models import Soustredeni + +class KomentarForm(forms.Form): + 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) diff --git a/galerie/migrations/0001_initial.py b/galerie/migrations/0001_initial.py new file mode 100644 index 00000000..b7406da6 --- /dev/null +++ b/galerie/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0033_organizator_studuje_popisek'), + ] + + operations = [ + migrations.CreateModel( + name='Galerie', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('nazev', models.CharField(max_length=100, verbose_name=b'N\xc3\xa1zev')), + ('datum_vytvoreni', models.DateTimeField(auto_now_add=True, verbose_name=b'Datum vytvo\xc5\x99en\xc3\xad')), + ('datum_zmeny', models.DateTimeField(auto_now=True, verbose_name=b'Datum posledn\xc3\xad zm\xc4\x9bny')), + ('popis', models.TextField(null=True, verbose_name=b'Popis', blank=True)), + ('zobrazit', models.IntegerField(default=1, verbose_name=b'Zobrazit?', choices=[(0, b'V\xc5\xbedy'), (1, b'Organiz\xc3\xa1tor\xc5\xafm'), (2, b'Nikdy')])), + ('galerie_up', models.ForeignKey(blank=True, to='galerie.Galerie', null=True)), + ('soustredeni', models.ForeignKey(blank=True, to='seminar.Soustredeni', null=True)), + ], + options={ + 'verbose_name': 'Galerie', + 'verbose_name_plural': 'Galerie', + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='Obrazek', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('obrazek_velky', models.ImageField(help_text=b'Lze vlo\xc5\xbeit libovoln\xc4\x9b velk\xc3\xbd obr\xc3\xa1zek. Ide\xc3\xa1ln\xc3\xad je, aby alespo\xc5\x88 jeden rozm\xc4\x9br m\xc4\x9bl alespo\xc5\x88 500px.', upload_to=b'Galerie/%Y/%m/%d')), + ('obrazek_stredni', models.ImageField(upload_to=b'Galerie/%Y/%m/%d/stredni', null=True, editable=False)), + ('obrazek_maly', models.ImageField(upload_to=b'Galerie/%Y/%m/%d/male', null=True, editable=False)), + ('nazev', models.CharField(max_length=50, null=True, verbose_name=b'N\xc3\xa1zev', blank=True)), + ('popis', models.TextField(null=True, verbose_name=b'Popis', blank=True)), + ('datum_vlozeni', models.DateTimeField(auto_now_add=True, verbose_name=b'Datum vlo\xc5\xbeen\xc3\xad')), + ('datum', models.DateTimeField(verbose_name=b'Datum po\xc5\x99\xc3\xadzen\xc3\xad fotografie')), + ('poradi', models.IntegerField(null=True, verbose_name=b'Po\xc5\x99ad\xc3\xad', blank=True)), + ('galerie', models.ForeignKey(to='galerie.Galerie')), + ], + options={ + 'verbose_name': 'Obr\xe1zek', + 'verbose_name_plural': 'Obr\xe1zky', + }, + bases=(models.Model,), + ), + migrations.AddField( + model_name='galerie', + name='titulni_obrazek', + field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to='galerie.Obrazek', null=True), + preserve_default=True, + ), + ] diff --git a/galerie/migrations/0002_auto_20151013_1145.py b/galerie/migrations/0002_auto_20151013_1145.py new file mode 100644 index 00000000..ea241267 --- /dev/null +++ b/galerie/migrations/0002_auto_20151013_1145.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('galerie', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='galerie', + name='titulni_obrazek', + field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='galerie.Obrazek', null=True), + preserve_default=True, + ), + migrations.AlterField( + model_name='obrazek', + name='datum', + field=models.DateTimeField(null=True, verbose_name=b'Datum po\xc5\x99\xc3\xadzen\xc3\xad fotografie', blank=True), + preserve_default=True, + ), + ] diff --git a/galerie/migrations/0003_add_galerie_poradi.py b/galerie/migrations/0003_add_galerie_poradi.py new file mode 100644 index 00000000..d9b32f48 --- /dev/null +++ b/galerie/migrations/0003_add_galerie_poradi.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import galerie.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('galerie', '0002_auto_20151013_1145'), + ] + + operations = [ + migrations.AddField( + model_name='galerie', + name='poradi', + field=models.IntegerField(null=True, verbose_name=b'Po\xc5\x99ad\xc3\xad', blank=True), + preserve_default=True, + ), + migrations.AlterField( + model_name='obrazek', + name='obrazek_maly', + field=models.ImageField(upload_to=galerie.models.obrazek_filename_maly, null=True, editable=False), + preserve_default=True, + ), + migrations.AlterField( + model_name='obrazek', + name='obrazek_stredni', + field=models.ImageField(upload_to=galerie.models.obrazek_filename_stredni, null=True, editable=False), + preserve_default=True, + ), + migrations.AlterField( + model_name='obrazek', + name='obrazek_velky', + field=models.ImageField(help_text=b'Lze vlo\xc5\xbeit libovoln\xc4\x9b velk\xc3\xbd obr\xc3\xa1zek. Ide\xc3\xa1ln\xc3\xad je, aby alespo\xc5\x88 jeden rozm\xc4\x9br m\xc4\x9bl alespo\xc5\x88 500px.', upload_to=galerie.models.obrazek_filename), + preserve_default=True, + ), + ] diff --git a/galerie/migrations/__init__.py b/galerie/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/galerie/models.py b/galerie/models.py new file mode 100644 index 00000000..e3f15f06 --- /dev/null +++ b/galerie/models.py @@ -0,0 +1,178 @@ +# coding: utf-8 + +from django.db import models +import seminar.models +from django.db.models import Q +from django.utils import timezone +from django.utils.encoding import force_unicode + +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 + +VZDY=0 +ORG=1 +NIKDY=2 +VIDITELNOST = ( + (VZDY, 'Vždy'), + (ORG, 'Organizátorům'), + (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 + +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): + 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 + cesta = "" + while(not gal.soustredeni): + cesta = os.path.join(force_unicode(gal.nazev), cesta) + gal = gal.galerie_up + return os.path.join('Galerie', force_unicode(gal.soustredeni), cesta, typ, force_unicode(self.nazev)) + +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 = models.ImageField(upload_to=obrazek_filename_stredni, null = True, editable = False) + obrazek_maly = models.ImageField(upload_to=obrazek_filename_maly, null = True, editable = False) + 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') + 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' + def save(self): + original = Image.open(self.obrazek_velky) + # vycteni EXIFu + exif = get_exif(original) + 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, 500, 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 _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 = 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): + nazev = models.CharField('Název', max_length=100) + datum_vytvoreni = models.DateTimeField('Datum vytvoření', auto_now_add = True) + datum_zmeny = models.DateTimeField('Datum poslední změny', auto_now = True) + popis = models.TextField('Popis', blank = True, null = True) + titulni_obrazek = models.ForeignKey(Obrazek, blank = True, null = True, related_name = "+", on_delete = models.SET_NULL) + zobrazit = models.IntegerField('Zobrazit?', default = ORG, choices = VIDITELNOST) + galerie_up = models.ForeignKey('Galerie', blank = True, null = True) + soustredeni = models.ForeignKey(Soustredeni, blank = True, null = True) + poradi = models.IntegerField('Pořadí', blank = True, null = True) + + def __unicode__(self): + return self.nazev + class Meta: + 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 + #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 +# + #@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() diff --git a/galerie/static/galerie/.gitignore b/galerie/static/galerie/.gitignore new file mode 100644 index 00000000..386e17f3 --- /dev/null +++ b/galerie/static/galerie/.gitignore @@ -0,0 +1,2 @@ +images +lightbox diff --git a/galerie/static/galerie/prvky/dalsi.png b/galerie/static/galerie/prvky/dalsi.png new file mode 100644 index 00000000..37064569 Binary files /dev/null and b/galerie/static/galerie/prvky/dalsi.png differ diff --git a/galerie/static/galerie/prvky/predchozi.png b/galerie/static/galerie/prvky/predchozi.png new file mode 100644 index 00000000..7a091bd6 Binary files /dev/null and b/galerie/static/galerie/prvky/predchozi.png differ diff --git a/galerie/templates/galerie/Base.html b/galerie/templates/galerie/Base.html new file mode 100644 index 00000000..61f96eb4 --- /dev/null +++ b/galerie/templates/galerie/Base.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} + +{# TODO predelat pres context processor #} +{% block header %}soustredeni{% endblock %} +{% block menu_soustredeni %}selected{% endblock %} +{% block submenu %}{% include 'seminar/soustredeni/submenu.html' %}{% endblock %} diff --git a/galerie/templates/galerie/Galerie.html b/galerie/templates/galerie/Galerie.html new file mode 100644 index 00000000..4c212566 --- /dev/null +++ b/galerie/templates/galerie/Galerie.html @@ -0,0 +1,62 @@ +{% extends "galerie/Base.html" %} + +{% block title %}{% block nadpis1a %} +{{galerie.nazev}} | Galerie +{% endblock %}{% endblock %} + +{% block content %} +
+ + + + + | + {% if forloop.last %} + {% if not forloop.counter|divisibleby:3 %} ++ {% endif %} + {% if not forloop.counter|divisibleby:2 %} + | + {% endif %} + {% endif %} + {% if forloop.counter|divisibleby:3 or forloop.last %} + |