Anfänglicher Commit: Producion Version Stand: Oktober 2014

This commit is contained in:
Christian Berg
2014-11-25 23:43:21 +01:00
commit daa35f5913
3381 changed files with 132346 additions and 0 deletions

392
src/events/models.py Normal file
View File

@@ -0,0 +1,392 @@
# -'- Encoding: utf-8 -*-
import os
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.template.defaultfilters import slugify
from django.utils.timezone import now
from django.utils.translation import ugettext as _
from imagekit import ImageSpec
import imagekit
from imagekit.models import ImageSpecField
from pilkit import processors
import pyexiv2
from utils import COUNTRIES, OverwriteStorage
CHOICES_HORIZONTAL = (
(0.00000001, _('left')),
(0.5, _('center')),
(1, _('right'))
)
CHOICES_VERTICAL = (
(0.00000001, _('top')),
(0.5, _('middle')),
(1, _('bottom'))
)
def get_upload_path(instance, filename):
'''
Generates the desired file path and filename for an uploaded Image.
With this function Django can save the uploaded images to subfolders that
also have a meaning for humans.
@param instance: an Django Object for which the Image has been uploaded.
@type instance: a instace of an models.Model sub-class.
@param filename: The filename of the uploaded image.
@type filename: String
'''
extension = filename[filename.rfind('.') + 1:]
if isinstance(instance, Event):
if instance.id:
return "events/%s.%s" % (instance.id, extension)
else:
return "events/%s.%s" % (slugify(instance.name), extension)
elif isinstance(instance, Location):
if instance.id:
return "events/location/%s.%s" % (instance.id, extension)
else:
return "events/location/%s.%s" % (instance.id, extension)
elif isinstance(instance, Photo):
return "events/%s/%s" % (instance.event.id, filename)
def post_save_image(sender, instance=None, created=False, raw=False, **kwargs):
'''
Reganerate the images.
'''
os.remove(instance.display.path)
os.remove(instance.callout.path)
os.remove(instance.thumbnail.path)
"""
instance.callout.generate(force=True)
instance.display.generate(force=True)
instance.thumbnail.generate(force=True)
"""
class CalloutImage(ImageSpec):
format = 'PNG'
width = 620
height = 300
@property
def processors(self):
model, field_name = imagekit.utils.get_field_info(self.source) # @UnusedVariable @IgnorePep8
anchor = model.get_anchor()
if anchor:
return [processors.Transpose(), processors.ResizeToFill(
width=self.width,
height=self.height, anchor=anchor
)]
else:
return [processors.Transpose(), processors.SmartResize(
width=self.width,
height=self.height
)]
class DisplayImage(ImageSpec):
format = 'PNG'
processors = [processors.Transpose(),
processors.ResizeToFit(width=940, height=940, upscale=False)]
class ThumbnailImage(CalloutImage):
format = 'PNG'
width = 140
height = 140
imagekit.register.generator('kasu:image:callout', CalloutImage)
imagekit.register.generator('kasu:image:display', DisplayImage)
imagekit.register.generator('kasu:image:thumbnail', ThumbnailImage)
class ImageModel(models.Model):
callout = ImageSpecField(source='image', id='kasu:image:callout')
display = ImageSpecField(source='image', id='kasu:image:display')
thumbnail = ImageSpecField(source='image', id='kasu:image:thumbnail')
def get_anchor(self):
try:
anchor_horizontal = getattr(self, 'anchor_horizontal')
anchor_vertical = getattr(self, 'anchor_vertical')
except AttributeError:
return None
if anchor_horizontal and anchor_vertical:
return (self.anchor_horizontal, self.anchor_vertical)
else:
return None
class Meta:
abstract = True
class EventManager(models.Manager):
def current_event(self):
try:
current = self.filter(start__lte=now())
current = current.filter(end__gte=now())
return current.order_by('start', 'end')[0]
except:
return None
def next_event(self):
try:
return self.filter(start__gt=now()).order_by('start', 'end')[0]
except:
return None
def archive(self):
return self.filter(start__lt=now())
def upcoming(self, limit=3):
result = self.filter(start__gt=now()).order_by('start', 'end')
if limit:
return result[1:(limit + 1)]
else:
return result
class Event(ImageModel):
name = models.CharField(_('Name'), max_length=255)
description = models.TextField(_("Description"), blank=True)
location = models.ForeignKey('Location')
start = models.DateTimeField(_('Start'))
end = models.DateTimeField(_('End'), blank=True, null=True)
url = models.URLField(_('Homepage'), blank=True)
image = models.ImageField(_("Image"), upload_to=get_upload_path,
storage=OverwriteStorage(), blank=True, null=True)
is_tournament = models.BooleanField(_('Tournament'), default=False,
help_text=_(u'This event is a tournament, different rules apply for \
the kyu ranking.'))
photo_count = models.PositiveIntegerField(default=0, editable=False)
event_series = models.ForeignKey('Event', blank=True, null=True,
on_delete=models.SET_NULL, editable=False,
verbose_name=_('Event Series'),
help_text=_(u'Wenn dieser Event zu einer Veranstaltungsreihe gehört \
werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen \
Event übernommen.'))
objects = EventManager()
class Meta(object):
verbose_name = _('Event')
verbose_name_plural = _('Events')
ordering = ('-start', '-end',)
def __unicode__(self):
try:
return "%(name)s (%(date)s)" % {'name': self.name,
'date': self.start.date()}
except:
return "New Event Model"
def get_absolute_url(self):
kwargs = {
'pk': self.id,
'year': self.start.strftime('%Y'),
'month': self.start.strftime('%m')
}
return reverse('event-detail', kwargs=kwargs)
def get_edit_url(self):
kwargs = {
'pk': self.id,
'year': self.start.strftime('%Y'),
'month': self.start.strftime('%m')
}
return reverse('event-form', kwargs=kwargs)
def get_callout(self):
if self.image:
return self.callout
elif self.photo_set.count():
return self.photo_set.all().order_by('?')[0].callout
elif self.location.image:
return self.location.callout
else:
return None
def get_thumbnail(self):
if self.image:
return self.thumbnail
elif self.photo_set.count():
return self.photo_set.all().order_by('?')[0].thumbnail
elif self.location.image:
return self.location.thumbnail
else:
return None
def save(self, **kwargs):
if self.event_series:
master_event = self.event_series
self.description = master_event.description
self.location = master_event.location
self.url = master_event.url
self.image = master_event.image
self.photo_count = self.photo_set.count()
models.Model.save(self, **kwargs)
# Update the rest of the event series:
for sub_event in Event.objects.filter(event_series=self):
sub_event.save()
# Update the Hanchans if necesery:
for hanchan in self.hanchan_set.all():
hanchan.save()
class Location(ImageModel):
name = models.CharField(_("Name"), max_length=200)
description = models.TextField(_("Description"), blank=True)
image = models.ImageField(_("Image"), upload_to=get_upload_path,
storage=OverwriteStorage(), blank=True, null=True)
url = models.URLField(_('Homepage'), blank=True)
postal_code = models.CharField(_('Postal Code'), max_length=6)
street_address = models.CharField(_('Street Address'), max_length=127)
locality = models.CharField(_('Locality'), max_length=127)
country = models.CharField(_('Country'), max_length=2, choices=COUNTRIES)
class Meta(object):
verbose_name = _('Venue')
verbose_name_plural = _('Venues')
def __unicode__(self):
return self.name
@property
def address(self):
address = (self.street_address, self.locality, self.country,)
return ','.join(address)
class PhotoManager(models.Manager):
def get_random(self, startpage=True):
if startpage:
queryset = self.filter(on_startpage=True)
else:
queryset = self.all().order_by('?')[0]
try:
return queryset.order_by('?')[0]
except IndexError:
return Photo()
class Photo(ImageModel):
name = models.CharField(_("Name"), max_length=100, blank=True)
image = models.ImageField(_("Image"), upload_to=get_upload_path,
storage=OverwriteStorage())
anchor_horizontal = models.FloatField(
_('horizontal Anchorpoint'),
choices=CHOICES_HORIZONTAL,
blank=True, null=True,
help_text='Der Ankerpunkt ist der interessante Teil des Bildes,\
welcher nie abgeschnitten werden darf'
)
anchor_vertical = models.FloatField(
_('vertical Anchorpoint'),
choices=CHOICES_VERTICAL,
blank=True, null=True,
help_text='Wenn kein Ankerpunkt von Hand (horizontal und vertikal)\
festgelegt wird, versucht die Software diesen selbst zu erraten.'
)
event = models.ForeignKey(Event)
description = models.TextField(_("Description"), max_length=300,
blank=True)
photographer = models.ForeignKey('auth.User')
on_startpage = models.BooleanField(_("Startpage"), default=False,
help_text=_('Display this Photo on the Startpage Teaser'))
created_date = models.DateTimeField(_("Published on"))
views = models.PositiveIntegerField(_("Number of views"), editable=False,
default=0)
objects = PhotoManager()
metadata = None
orientation = 1
class Meta:
verbose_name = _('Event Image')
verbose_name_plural = _('Event Images')
ordering = ["created_date"]
get_latest_by = "created_date"
def __unicode__(self):
return os.path.basename(self.image.name)
def read_metadata(self):
image_path = os.path.join(settings.MEDIA_ROOT, self.image.name)
self.metadata = pyexiv2.ImageMetadata(image_path)
self.metadata.read()
try:
self.orientation = self.metadata['Exif.Image.Orientation'].value
except:
self.orientation = 1
def save_metadata(self):
if not self.metadata:
self.read_metadata()
self.metadata['Exif.Image.DateTime'] = self.created_date
self.metadata['Exif.Image.ImageDescription'] = self.description
self.metadata['Exif.Image.Artist'] = self.photographer.username
self.metadata['Exif.Image.Orientation'] = self.orientation or 1
self.metadata.write()
def rotate(self, rotate):
'''
Sets an the Exif tag in an image to set the right direction.
This provides lossless image rotation.
@param rotate: 'clockwise' or 'counter-clockwise' the direction in
which we should rotate the image in 90° steps.
'''
if not self.metadata:
self.read_metadata()
if rotate == 'clockwise':
if self.orientation == 1:
self.orientation = 6
elif self.orientation == 6:
self.orientation = 3
elif self.orientation == 3:
self.orientation = 8
else:
self.orientation = 1
elif rotate == 'counter-clockwise':
if self.orientation == 1:
self.orientation = 8
elif self.orientation == 8:
self.orientation = 3
elif self.orientation == 3:
self.orientation = 6
else:
self.orientation = 1
self.save()
def get_absolute_url(self):
return reverse('event-photo', kwargs={'event': self.event.id,
'pk': self.id})
@property
def next_photo(self):
return self.get_next_by_created_date(event=self.event)
@property
def previous_photo(self):
return self.get_previous_by_created_date(event=self.event)
def save(self, **kwargs):
'''
Triggers to save related Event to save. This should force an update for
the denormalized Photo count.
'''
ImageModel.save(self, **kwargs)
self.save_metadata()
self.event.save()
models.signals.post_save.connect(post_save_image, sender=Photo)