neue Verzeichnissstruktur

This commit is contained in:
Christian Berg
2014-11-26 13:13:07 +01:00
parent daa35f5913
commit f34281089d
3372 changed files with 168 additions and 2544 deletions

0
events/__init__.py Normal file
View File

49
events/admin.py Normal file
View File

@@ -0,0 +1,49 @@
'''
Created on 19.09.2011
@author: christian
'''
# import stuff we need from django
from django.contrib import admin
from events.models import Event, Photo, Location
from imagekit.admin import AdminThumbnail
from django.utils.translation import gettext as _
class EventInline(admin.TabularInline):
model = Event
fields = ('name', 'start', 'end')
verbose_name_plural = _('Event Series')
class EventAdmin(admin.ModelAdmin):
list_display = ('name', 'start', 'end', 'location',)
list_editable = ('start', 'end', 'location',)
readonly_fields = ('event_series',)
date_hierarchy = 'start'
search_fields = ('name', 'description')
list_per_page = 50
inlines = (EventInline,)
class LocationAdmin(admin.ModelAdmin):
list_display = ('name', 'street_address', 'postal_code', 'locality')
class PhotoAdmin(admin.ModelAdmin):
admin_thumbnail = AdminThumbnail(image_field='thumbnail')
fields = ('image', 'event', 'name', 'description',
('anchor_horizontal', 'anchor_vertical'),
('photographer', 'created_date'))
list_filter = ('event', 'on_startpage',)
list_display = ('admin_thumbnail', 'image', 'name', 'event',
'photographer', 'on_startpage')
list_display_links = ('image',)
list_editable = ('on_startpage', 'name', 'event', 'photographer')
# register with CMS
admin.site.register(Event, EventAdmin)
admin.site.register(Photo, PhotoAdmin)
admin.site.register(Location, LocationAdmin)

50
events/forms.py Normal file
View File

@@ -0,0 +1,50 @@
'''
Created on 03.10.2011
@author: christian
'''
from . import models
from django import forms
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from utils.html5.widgets import DateTimeInput
class PhotoUploadForm(forms.Form):
error_css_class = 'error'
required_css_class = 'required'
photographer = forms.ModelChoiceField(User.objects.all(), required=True,)
event = forms.ModelChoiceField(models.Event.objects.all(), required=True,)
upload = forms.FileField(label=_('Images'), required=True,
widget=forms.widgets.ClearableFileInput(attrs={'multiple': 'multiple',
'accept': "image/gif,image/png,image/jpeg"})
)
class EditPhotoForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta(object):
model = models.Photo
fields = ('event', 'name', 'description', 'photographer',
'anchor_horizontal', 'anchor_vertical',
'created_date', 'on_startpage')
class EventForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
start = forms.DateTimeField(
label=_('start'), required=True,
widget=DateTimeInput() # widget=SplitDateTimeWidget()
)
end = forms.DateTimeField(
label=_('end'), required=False,
widget=DateTimeInput() # widget=SplitDateTimeWidget()
)
class Meta(object):
model = models.Event

392
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)

40
events/specs.py Normal file
View File

@@ -0,0 +1,40 @@
from imagekit.specs import ImageSpec
from imagekit import processors
class ResizeDisplay(processors.Resize):
width = 780
crop = False
upscale = False
# first we define our thumbnail resize processor
class ResizeCallout(processors.Resize):
width = 620
height = 300
crop = True
class ResizeAdmin(processors.Resize):
width = 60
height = 60
crop = True
class ResizeThumbnail(processors.Resize):
width = 140
height = 140
crop = True
# Different Image Sizes
class Admin(ImageSpec):
pre_cache = False
processors = [processors.Transpose, ResizeAdmin]
class Display(ImageSpec):
pre_cache = False
processors = [processors.Transpose, ResizeDisplay]
class Callout(ImageSpec):
pre_cache = False
processors = [processors.Transpose, ResizeCallout]
class Thumbnail(ImageSpec):
pre_cache = False
processors = [processors.Transpose, ResizeThumbnail]

View File

@@ -0,0 +1,51 @@
{% extends "events/page.html" %}
{% load i18n comments%}
{% block title %}
{% trans 'Event Archive' %} {% if month %}{{ month|date:'F Y' }} {% else %}{% if year %}{{year}}{% endif %}{% endif %}
{% endblock %}
{% block content %}
<h2>{% trans 'Event Archive' %} {% if month %}{{ month|date:'F Y' }} {% else %}{% if year %}{{year}}{% endif %}{% endif %}</h2>
{% for event in event_list %}
{% ifchanged %}<h3 class="clearfix">{{ event.start|date:'F Y' }}</h3>{% endifchanged %}
<div class="clearfix">
{% get_comment_count for event as comment_count %}
<a href="{{ event.get_absolute_url }}"><img src="{{ event.get_thumbnail.url }}" alt=" {% trans 'Event Image' %}" class="thumbnail"/></a>
<h4><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></h4>
{{event.description}}
<img src="{{ STATIC_URL }}/icons/date.png" alt="{% trans 'Start' %}:" title="{% trans 'Start' %}">
{{ event.start|date }}
{% if event.end %}
{% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }}
{% else %}
{{ event.start|time:'H:i' }}
{% endif %}
<ul class="info">
<li class="location">{{ event.location }}</li>
<li class="comment"><a href="{{event.get_absolute_url}}#comments">{{ comment_count }} {% trans 'Comments' %}</a></li>
<li class="photo"><a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }} {% trans 'Photos' %}</a></li>
<li class="hanchan"><a href="{% url 'event-hanchan-list' event.pk %}">{{ event.hanchan_set.count }} {% trans 'Hanchans' %}</a></li>
{% if perms.events.change_event %}<li><a href="{{ event.get_edit_url }}" class="button"><img src="{{ STATIC_URL }}icons/page_edit.png" alt="{%trans "Edit" %}"></a></li>{% endif %}
</ul>
</div>
{% endfor %}
{% endblock %}
{% block sidebar %}
<h2>Archive</h2>
<ul>
{% if year %}
{% for date in date_list %}
<li><a href="{% url 'event-archive' year|date:'Y' date|date:'m' %}">{{ date|date:'F' }}</a></li>
{% endfor %}
{% elif month %}
<li><a href="{% url 'event-archive' month|date:'Y' %}">{{ month|date:'Y' }}</a></li>
{% else %}
{% for date in date_list %}
<li><a href="{% url 'event-archive' date|date:'Y' %}">{{ date|date:'Y' }}</a></li>
{% endfor %}
{% endif %}
</ul>
{% endblock %}

View File

@@ -0,0 +1,111 @@
{% extends "events/page.html" %}
{% load i18n django_markdown comments %}
{% block title %}{{ event.name }}{% endblock %}
{% block opengraph %}
<meta property="og:type" content="activity" />
<meta property="og:title" content="{{event.name}}" />
<meta property="og:url" content="http://www.kasu.at{{event.get_absolute_url}}" />
<meta property="og:image" content="http://www.kasu.at{{ event.get_thumbnail.url }}" />
{% if event.description %}<meta property="og:description" content="{{event.description}}" />{% endif %}
{% endblock %}
{% block extra_head %}
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
<style type="text/css" media="screen and (min-width: 701px)">#teaser{background-image:url('{{ event.get_callout.url }}')}</style>
<!--[if lt IE 9]><style type="text/css" media="screen">#teaser{background-image:url("{{event.get_callout.url}}")}</style><![endif]-->
{% endblock %}
{% block maincontent %}
<div class="grid_8" id="teaser">
{% if event.description %}
<div id="teaser_text">
<h2>{{event.name}}</h2>
{{event.description|markdown}}
</div>
{% else %}
<div id="teaser_text">
<h2>{{event.name}}</h2>
{{event.location.description|markdown}}
</div>
{% endif %}
</div>
<div class="grid_4 red_box" id="map_canvas" style="width:300px; height:300px;"></div>
<div class="grid_4">
<h3>{% trans "Date" %}</h3>
<p></p>
<ul>
<li><strong>{% trans "Start" %}:</strong> {{ event.start }}</li>
{% if event.end %}<li><strong>{% trans "End" %}:</strong> {{ event.end }}</li>{% endif %}
{% if event.url %}<li><strong>{% trans "Homepage" %}:</strong> <a href="{{ event.url }}">{{ event.url }}</a></li>{% endif %}
</ul>
</div>
<div class="grid_4">
<h3>{% trans 'Location' %}</h3>
<strong>{{ event.location.name }}</strong>
<address>
{{event.location.street_address}}<br />
{{event.location.postal_code}} {{event.location.locality}} <br />
{{event.location.get_country_display}}
</address>
{% if event.location.url %}
<p><strong>{% trans "Homepage" %}:</strong> <a href="{{ event.location.url }}">{{ event.location.url }}</a></p>
{% endif %}
</div>
<div class="grid_4 center">
<ul class="info">
<li><img src="{{ STATIC_URL }}icons/table.png" alt="{% trans 'Hanchans' %}" /><a href="{% url 'event-hanchan-list' event.pk %}" >{{ event.hanchan_set.count }} {% trans "Hanchans" %}</a></li>
<li><img src="{{ STATIC_URL }}icons/camera.png" alt="{% trans 'Photos' %}" /><a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }} {% trans 'Photos' %}</a></li>
</ul>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwww.kasu.at{{event.get_absolute_url|urlencode}}" onclick="javascript:window.open(this.href,
'', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;"><img src="{{STATIC_URL}}img/google_plus.png" alt="Google+" title="{% trans 'Share on Google+'%}" width="39" height="39"/></a>
<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at{{event.get_absolute_url|urlencode}}" target='_blank'><img src="{{STATIC_URL}}img/twitter.png" alt="Twitter" title=" {% trans 'Share on Twitter' %}" width="39" height="39"/></a>
<a href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{event.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/facebook.png" alt="Facebook" title="{% trans 'Share on Facebook'%}" width="39" height="39"/></a>
<a href="http://maps.google.com/maps?q={{event.location.address|urlencode}}&z=16" target="gmaps"><img src="{{ STATIC_URL }}img/google_maps.png" alt="Google Maps" title="{% trans 'Show on Google Maps' %}" width="39" height="39"/></a>
</div>
<br class="clear" />
{% render_comment_list for event %}
{% render_comment_form for event %}
{% endblock %}
{% block buttonbar %}
{% if perms.events.change_event %}
<a class="button" href="{{ event.get_edit_url }}"><img src="{{ STATIC_URL }}icons/page_edit.png" alt="" /> {% trans "Edit Event" %}</a>
{% endif %}
{% endblock %}
{% block javascript %}
var geocoder;
var map;
function codeAddress() {
var address = "{{ event.location.address }}";
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
zoom: 16,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
codeAddress();
}
initialize();
{% endblock %}

View File

@@ -0,0 +1,49 @@
{% extends "events/page.html" %}
{% load i18n %}
{% block extra_head %}
<link rel="stylesheet" href="{{STATIC_URL}}css/jquery-ui-1.8.16.custom.css" type="text/css">
<script type="text/javascript" src="{{STATIC_URL}}js/jquery-ui-1.8.16.custom.min.js"></script>
{% endblock %}
{% block maincontent %}
<form method="post" enctype="multipart/form-data" class="grid_12">
{% csrf_token %}
<fieldset>
<legend>{% if event.pk %}{% trans "Edit Event"%}{% else %}{% trans "Add Event"%}{% endif %}</legend>
{% include "form.html" %}
{% if event.id and event.event_set.count %}
<p class="warning">
<strong>Achtung! Das ist eine Veranstaltungsreihe!</strong> Diese kann man im Moment nur im Admin-Interface vernünfig bearbeiten.<br />
Du bearbeitest hier den "Hauptevent" der Reihe ({{event.event_set.count}}). Alle Änderungen (abgesehen von Name, Start und Ende) werden von den darauf folgendem Veranstaltungen übernommen.
</strong>
</p>
{% endif %}
<p class="buttonbar">
<button type="reset"><img src="{{STATIC_URL}}icons/arrow_undo.png" alt="{% trans 'reset' %}" /> {% trans 'reset' %}</button>
<button type="submit"><img src="{{STATIC_URL}}icons/disk.png" alt="{% trans 'save' %}" /> {% trans 'save' %}</button>
</p>
</fieldset>
</form>
{% endblock %}
{% block buttonbar %}{% endblock %}
{% block javascript %}
$(function() {
$.datepicker.setDefaults($.datepicker.regional['{{LANGUAGE_CODE}}']);
$( "#id_start_0" ).datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'yy-mm-dd'
});
$( "#id_end_0" ).datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'yy-mm-dd',
});
});
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "events/event_archive.html" %}
{% load i18n comments%}
{% block title %}{% trans "Upcoming Events" %}{% endblock %}
{% block maincontent %}
<h2 class="grid_12">{% trans "Upcoming Events" %}</h2>
{% for event in event_list %}
{% ifchanged %}<h3 class="grid_12">{{ event.start|date:'F Y' }}</h3>{% endifchanged %}
{% get_comment_count for event as comment_count %}
<div class="grid_6">
<a href="{{ event.get_absolute_url }}"><img src="{{ event.get_thumbnail.url }}" alt=" {% trans 'Event Image' %}" class="thumbnail"/></a>
<h4><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></h4>
{{event.description}}
<ul class="info">
<li>
<img src="{{ STATIC_URL }}/icons/date.png" alt="{% trans 'Start' %}:" title="{% trans 'Start' %}">
{{ event.start|date:'SHORT_DATE_FORMAT' }}
{% if event.end %}
{% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }}
{% else %}
{{ event.start|time:'H:i' }}
{% endif %}
</li>
<li>
<img src="{{ STATIC_URL }}/icons/map.png" alt="{% trans 'Location' %}" title="{% trans 'Location' %}">
{{ event.location }}
</li>
<li>
<img src="{{ STATIC_URL }}/icons/comments.png" alt="{% trans 'Comments' %}" title="{% trans 'Comments' %}">
<a href="{{event.get_absolute_url}}#comments">{{ comment_count }}</a>
</li>
</ul>
<p style="text-align:right">
{% if perms.events.change_event %}
<a href="{{ event.get_edit_url }}" class="button"><img src="{{ STATIC_URL }}icons/page_edit.png" alt="{%trans "Edit" %}"></a>
{% endif %}
{% if perms.events.add_photo %}
<a href="{% url 'event-photo-list' event.pk %}" class="button"><img src="{{ STATIC_URL }}icons/image_add.png" alt="{%trans "Upload" %}"></a>
{% endif %}
</p>
</div>
{% endfor %}
{% if page_obj.has_other_pages %}{% include 'paginator.html' %}{% endif %}
{% endblock %}

View File

@@ -0,0 +1,47 @@
{% extends "events/page.html" %}
{% load i18n django_markdown%}
{% block title %}{{ event.name }}{% endblock %}
{% block opengraph %}
<meta property="og:type" content="activity" />
<meta property="og:title" content="{{event.name}}" />
<meta property="og:url" content="http://www.kasu.at{{event.get_absolute_url}}" />
<meta property="og:image" content="http://www.kasu.at{{ event.get_thumbnail.url }}" />
{% if event.description %}<meta property="og:description" content="{{event.description}}" />{% endif %}
{% endblock %}
{% block extra_head %}
<style type="text/css" media="screen and (min-width: 701px)">#teaser{background-image:url('{{ event.get_callout.url }}')}</style>
<!--[if lt IE 9]><style type="text/css" media="screen">#teaser{background-image:url("{{event.get_callout.url}}")}</style><![endif]-->
{% endblock %}
{% block maincontent %}
<div class="grid_8" id="teaser">
<div id="teaser_text">
<h2>{{event.name}}</h2>
{% if event.description %}{{event.description|markdown}}{% else %}{{event.location.description|markdown}}{% endif %}
</div>
</div>
<div class="red_box grid_4">
{% block red_box %}
<h2>Info</h2>
<p>&nbsp;</p>
<ul>
<li class="date"><strong>{% trans "Start" %}:</strong> {{ event.start }}</li>
{% if event.end %}<li class="date"><strong>{% trans "End" %}:</strong> {{ event.end }}</li>{% endif %}
<li class="location">
{% if event.location.url %}<a href="{{ event.location.url }}">{% else %}<a href="http://maps.google.com/maps?q={{event.location.address|urlencode}}&z=16">{% endif %}
<strong>{% trans "Location" %}:</strong> {{event.location.name}}</a>
</li>
{% if event.url %}<li><a href="{{ event.url }}"><strong>{% trans "Homepage" %}:</strong> {{ event.url }}</a></li>{% endif %}
<li class="hanchan"><a href="{% url 'event-hanchan-list' event.pk %}" ><strong>{% trans "Hanchans" %}:</strong> {{ event.hanchan_set.count }}</a></li>
<li class="photo"><a href="{% url 'event-photo-list' event.pk %}"><strong>{% trans 'Photos' %}:</strong> {{ event.photo_count }}</a></li>
{% if event.is_tournament %}<li class="season"><a href="{% url 'event-ranking' event.pk %}"><strong>{% trans "tourney" %}:</strong> {% trans "other rules apply here" %}</a></li>{% endif%}
</ul>
{% endblock %}
</div>
{% block event_content %} {% endblock %}
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends "base.html" %}
{% load comments i18n %}
{% block title %}{{page.title}}{% endblock %}
{% block content %}
<h2>{{ page.title }}</h2>
{{ page.content }}
{% endblock %}
{% block sidebar %}
<a href="http://www.google.com/calendar/render?cid=http%3A%2F%2Fwww.kasu.at%2Fevents.ics" target="_blank"><img src="http://www.google.com/calendar/images/ext/gc_button6.gif" alt="0" border="0"></a>
{% endblock %}
{% block additional_buttonbar %}
{% if perms.events.add_event %}
<a href="{% url 'event-form' %}" class="button"><img src="{{ STATIC_URL }}icons/calendar_add.png" alt=""/> {% trans 'Add Event' %}</a>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,21 @@
{% extends "base.html" %}
{% load i18n comments %}
{% block maincontent %}
<header>
<form action="" method="post">
{% csrf_token %}
<h1 class="grid_12">Dieses Photo wirklich löschen?</h1>
</header>
<img src="{{photo.display.url}}" alt="{{photo.name}}" title="{{photo.name}}" class="grid_10 push_1" />
<br class="clear" />
<p>Sind Sie sicher, dass Sie das Bild &ldquo;{{photo.name}}&rdquo; löschen wollen?</p>
<p class="buttonbar">
<a href="{% url 'event-photo-list' photo.event.id %}" class="button" style="float: left;"><img src="{{STATIC_URL}}icons/cancel.png" alt="{% trans 'Cancel' %}" /> {% trans 'Cancel' %}</a>
<button type="submit"><img src="{{STATIC_URL}}icons/delete.png" alt="{% trans 'Delete' %}" /> {% trans 'Delete' %}</button>
</form>
</p>
{% endblock %}

View File

@@ -0,0 +1,56 @@
{% extends "events/photo_gallery.html" %}
{% load i18n comments %}
{% block title %} {{ photo.name }} - {{ photo.event.name }} {% endblock %}
{% block opengraph %}
<meta property="og:type" content="photo" />
<meta property="og:title" content="{{photo.name}} - Foto" />
<meta property="og:url" content="http://www.kasu.at{{photo.get_absolute_url}}" />
<meta property="og:image" content="http://www.kasu.at{{photo.thumbnail.url}}" />
{% if photo.description %}<meta property="og:description" content="{{photo.description}}" />{% endif %}
{% endblock %}
{% block maincontent %}
<h2 class="grid_12"><a href="{% url 'event-photo-list' photo.event.id %}">{{photo.event.name}}</a> &raquo; {{ photo.name }}</h2>
<div id="display" class="grid_12 clearfix">
<img src="{{photo.display.url}}" alt="{{photo.name}}" title="{{photo.name}}"/>
{% if photo.previous_photo %}
<a href="{{ photo.previous_photo.get_absolute_url }}" class="previous">{% trans 'previous' %}</a>
{% endif %}
{% if photo.next_photo %}
<a href="{{ photo.next_photo.get_absolute_url }}" class="next">Next</a>
{% endif %}
</div>
{% if perms.events.change_photo %}
<form method="post" enctype="multipart/form-data" class="grid_12">
{% csrf_token %}
<fieldset>
{% include "form.html" %}
<p class="buttonbar">
<a href="{{ photo.image.url }}" class="button" type="application/octet-stream"><img src="{{ STATIC_URL}}icons/drive_go.png" alt="{% trans 'download' %}" title="{% trans 'download' %}" /></a>
<button type="submit" name="rotate" value="counter-clockwise"><img src="{{STATIC_URL}}icons/shape_rotate_anticlockwise.png" title="Gegen den Uhrzeiger drehen"></button>
<button type="submit" name="rotate" value="clockwise"><img src="{{STATIC_URL}}icons/shape_rotate_clockwise.png" title="Im Uhrzeiger drehen"></button>
<button type="submit"><img src="{{STATIC_URL}}icons/disk.png" alt=""> {% trans "save" %}</button>
</p>
</fieldset>
</form>
{% else %}
<p class="grid_10 push_1">{{ photo.description }}</p>
<ul class="info grid_11 push_1">
<li class="user"><strong>{% trans 'Photographer' %}: </strong>{{ photo.photographer }}</li>
<li class="date"><strong>{% trans 'on' %}</strong> {{ photo.created_date }}</li>
</ul>
{% endif %}
<div class="grid_12 more_link">
<a href="https://m.google.com/app/plus/x/?v=compose&content={{photo.headline|urlencode}}+-+http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/google_plus.png" alt="Google+" title="{% trans 'Share on Google+'%}" /></a>
<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target='_blank'><img src="{{STATIC_URL}}img/twitter.png" alt="Twitter" title="{% trans 'Share on Twitter' %}" /></a>
<a href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{photo.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/facebook.png" alt="Facebook" title="{% trans 'Share on Facebook'%}" /></a>
</div>
{% render_comment_list for photo %}
{% render_comment_form for photo %}
{% endblock %}

View File

@@ -0,0 +1,36 @@
{% extends "base.html" %}
{% load i18n %}
{% block navigation %}
<ul id="navigation">
{% if event %}
<li><a href="{{ current_top_page.get_absolute_url }}">{{current_top_page.menu_name}}</a></li>
<li><a class="active">{{event.name}}</a></li>
{% elif photo %}
<li><a href="{{ current_top_page.get_absolute_url }}">{{current_top_page.menu_name}}</a></li>
<li><a href="{% url 'event-photo-list' photo.event.id %}">{{photo.event.name}}</a></li>
<li><a class="active">{{photo.name}}</a></li>
{% else %}
<li><a href="{{ current_top_page.get_absolute_url }}" {% ifequal current_page current_top_page %}class="active"{% endifequal %}>{{current_top_page.menu_name}}</a></li>
{% if perms.event.add_photo %}
<li><a href="/gallery/upload/" {% ifequal current_path 'gallery/upload' %}class="active"{% endifequal %}>{% trans 'Upload' %}</a></li>
{% endif %}
{% endif %}
</ul>
{% endblock %}
{% block maincontent %}
<h2 class="grid_12">{% trans 'Photos' %}</h2>
{% for event in event_list %}
<div class="gallery grid_4">
<h3><a href="{% url 'event-photo-list' event.id %}">{{event.name}}</a></h3>
<a href="{% url 'event-photo-list' event.id %}"><img src="{{event.get_thumbnail.url}}" class="thumbnail"/></a>
</div>
{% empty %}
<p>Sorry da kommt erst was hin!</p>
{% endfor %}
{% endblock %}
{% block buttonbar %}{% endblock %}

View File

@@ -0,0 +1,68 @@
{% extends "events/event_site.html" %}
{% load i18n %}
{% block title %}{{event.name}}{% endblock %}
{% block opengraph %}
<meta property="og:type" content="album" />
<meta property="og:title" content="{{event.name}}" />
<meta property="og:url" content="http://www.kasu.at{% url 'event-photo-list' event.pk %}" />
<meta property="og:image" content="http://www.kasu.at{{ event.get_thumbnail.url }}" />
{% if event.description %}<meta property="og:description" content="{{event.description}}" />{% endif %}
{% endblock %}
{% block navigation %}
<ul id="navigation">
{% if event %}
<li><a href="{{ current_top_page.get_absolute_url }}">{{current_top_page.menu_name}}</a></li>
<li><a class="active">{{event.name}}</a></li>
{% elif photo %}
<li><a href="{{ current_top_page.get_absolute_url }}">{{current_top_page.menu_name}}</a></li>
<li><a href="{% url 'event-photo-list' photo.event.id %}">{{photo.event.name}}</a></li>
<li><a class="active">{{photo.name}}</a></li>
{% else %}
<li><a href="{{ current_top_page.get_absolute_url }}" {% ifequal current_page current_top_page %}class="active"{% endifequal %}>{{current_top_page.menu_name}}</a></li>
{% if perms.event.add_photo %}
<li><a href="/gallery/upload/" {% ifequal current_path 'gallery/upload' %}class="active"{% endifequal %}>{% trans 'Upload' %}</a></li>
{% endif %}
{% endif %}
</ul>
{% endblock %}
{% block event_content %}
{% if perms.events.delete_photo %}
{% for photo in photo_list %}
<div class="thumbnail">
<a href="{{photo.get_absolute_url}}"><img src="{{photo.thumbnail.url}}" alt=""/></a>
<a href="{% url 'delete-event-photo' photo.pk %}" class="delete_image"><img src="{{STATIC_URL}}icons/delete.png" title="{% trans 'delete' %}"/></a>
</div>
{% endfor %}
{% else %}
{% for photo in photo_list %}
<a href="{{photo.get_absolute_url}}" class="thumbnail"><img src="{{photo.thumbnail.url}}" alt=""/></a>
{% endfor %}
{% endif %}
{% if perms.events.add_photo %}
<br class="clear" />
<form action="{% url 'event-photo-upload' event.id %}" method="post" enctype="multipart/form-data" class="grid_12">
{% csrf_token %}
<fieldset>
<legend>Photos hochladen</legend>
{% include "form.html"%}
<p class="buttonbar">
<button type="reset"><img src="{{STATIC_URL}}icons/arrow_undo.png" alt="{% trans 'reset' %}" /> {% trans 'reset' %}</button>
<button type="submit"><img src="{{STATIC_URL}}icons/image_add.png" alt="{% trans 'upload' %}" /> {% trans 'upload' %}</button>
</p>
</fieldset>
</form>
{% endif %}
{% if page_obj.has_other_pages %}{% include 'paginator.html' %}{% endif %}
{% endblock %}
{% block buttonbar %}{% endblock %}

View File

@@ -0,0 +1,54 @@
{% extends "events/photo_gallery.html" %}
{% load i18n comments %}
{% block maincontent %}
<h2 class="grid_12">Photos</a> &raquo; {% trans "upload"%}</h2>
{% for event in event_list %}
{% get_comment_count for event as comment_count %}
{% ifchanged %}<h3 class="grid_12">{{ event.start|date:'F Y' }}</h3>{% endifchanged %}
<div style="float:left">
<a href="{% url 'event-photo-list' event.pk %}"><img src="{{ event.get_thumbnail.url }}" alt="" class="thumbnail"/></a>
<div class="grid_4" />
<h4><a href="{% url 'event-photo-list' event.pk %}">{{ event.name }}</a></h4>
<div class="info">
<img src="{{ STATIC_URL }}/icons/date.png" alt="{% trans 'Start' %}" title="{% trans 'Start' %}">
{{ event.start|date }}
{% if event.end %}
{% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }}
{% else %}
{{ event.start|time:'H:i' }}
{% endif %}
</div>
{% if event.description %}<p>{{event.description}}</p>{% endif %}
<div class="info">
<img src="{{ STATIC_URL }}/icons/map.png" alt="{% trans 'Location' %}" title="{% trans 'Location' %}">
{{ event.location }}
<img src="{{ STATIC_URL }}/icons/comments.png" alt="{% trans 'Comments' %}" title="{% trans 'Comments' %}">
<a href="{{event.get_absolute_url}}#comments">{{ comment_count }} {% trans 'Comments' %}</a>
<img src="{{ STATIC_URL }}/icons/images.png" alt="{% trans 'Photos' %}" title="{% trans 'Photos' %}">
<a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }} {% trans 'Photos' %}</a>
</div>
<p style="text-align:right">
{% if perms.events.add_photo %}
<a href="{% url 'event-photo-list' event.pk %}" class="button"><img src="{{ STATIC_URL }}icons/image_add.png" alt="{%trans "Upload" %}"></a>
{% endif %}
</p>
</div></div>
{% endfor %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="grid_8 push_2">
<legend>Photos hochladen</legend>
{% include "form.html" %}
<p class="buttonbar">
<button type="submit">
<img src="{{ STATIC_URL }}icons/drive_go.png" alt="" />
{% trans "upload" %}
</button>
</p>
</fieldset>
</form>
{% endblock %}

16
events/tests.py Normal file
View File

@@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

228
events/views.py Normal file
View File

@@ -0,0 +1,228 @@
# -*- encoding: utf-8 -*-
# Create your views here.
from . import models, forms
from datetime import timedelta
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.http import HttpResponse, Http404
from django.shortcuts import redirect
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
from django.views import generic
from icalendar import Calendar, Event
import pyexiv2
from utils.mixins import PermissionRequiredMixin
class DeleteEventPhoto(generic.DeleteView):
model = models.Photo
"""
def get_object(self, queryset=None):
return models.Photo.objects.get(pk=self.kwargs['pk'])
"""
def get_success_url(self):
return reverse('event-photo-list', args=[self.object.event.id])
@method_decorator(permission_required('events.delete_photo'))
def dispatch(self, *args, **kwargs):
return super(DeleteEventPhoto, self).dispatch(*args, **kwargs)
class EventArchiveIndex(generic.ArchiveIndexView):
allow_empty = True
context_object_name = 'event_list'
date_field = 'start'
model = models.Event
paginate_by = 5
def get_context_data(self, **kwargs):
context = generic.ArchiveIndexView.get_context_data(self, **kwargs)
context['is_archive'] = True
return context
class EventArchiveMonth(generic.MonthArchiveView):
date_field = 'start'
make_object_list = True
model = models.Event
month_format = '%m'
paginate_by = 15
template_name = 'events/event_archive.html'
def get_context_data(self, **kwargs):
context = generic.MonthArchiveView.get_context_data(self, **kwargs)
context['is_archive'] = True
return context
class EventArchiveYear(generic.YearArchiveView):
date_field = 'start'
make_object_list = True
model = models.Event
paginate_by = 15
template_name = 'events/event_archive.html'
year_format = '%Y'
def get_context_data(self, **kwargs):
context = generic.YearArchiveView.get_context_data(self, **kwargs)
context['is_archive'] = True
return context
class EventDetail(generic.DetailView):
model = models.Event
def get_context_data(self, **kwargs):
context = generic.DetailView.get_context_data(self, **kwargs)
context['form'] = forms.PhotoUploadForm(initial={'event':self.object, 'photographer': self.request.user})
return context
class EventForm(PermissionRequiredMixin, generic.UpdateView):
form_class = forms.EventForm
permission_required = 'events.add_event'
def get_object(self, queryset=None):
'''
If an id has been submitted, try return the existing Event for an update,
else creates a new one.
@param queryset:
'''
if self.kwargs.get('pk'):
event = models.Event.objects.get(pk=self.kwargs['pk'])
if event.event_series:
return event.event_series
else:
return event
else:
return models.Event()
class EventGallery(generic.ListView):
template_name = 'events/photo_gallery.html'
queryset = models.Event.objects.filter(start__lt=timezone.now(), photo_count__gt=0)
paginate_by = 12
class EventListIcal(generic.View):
'''
Generates an returns an iCal File with all upcoming events.
'''
def add_event(self, event):
ics_event = Event()
dtstart = timezone.localtime(event.start)
dtend = timezone.localtime(event.end)
ics_event.add('DTSTART', dtstart)
ics_event.add('SUMMARY', event.name)
ics_event.add('DESCRIPTION', event.description)
ics_event.add('LOCATION', event.location.address)
ics_event.add('URL', 'http://www.kasu.at' + event.get_absolute_url())
ics_event['UID'] = 'event-%d@www.kasu.at' % event.pk
ics_event.add('PRIORITY', 5)
if event.end:
ics_event.add('DTEND', dtend)
self.calendar.add_component(ics_event)
def get(self, request, *args, **kwargs):
response = HttpResponse(mimetype="text/calendar; charset=UTF-8")
self.calendar = Calendar()
self.calendar.add('prodid', 'http://www.kasu.at/')
self.calendar.add('version', '2.0')
for event in models.Event.objects.upcoming(limit=None):
self.add_event(event)
response.write(self.calendar.to_ical())
return response
class EventPhoto(generic.UpdateView):
form_class = forms.EditPhotoForm
model = models.Photo
template_name = 'events/photo_detail.html'
def post(self, request, *args, **kwargs):
if request.POST.get('rotate') and request.user.has_perm('events.change_photo'):
photo = models.Photo.objects.get(pk=kwargs['pk'])
photo.rotate(request.POST['rotate'])
# return redirect(photo.get_absolute_url())
return self.get(request)
else:
return generic.UpdateView.post(self, request, *args, **kwargs)
class EventPhotoList(generic.ListView):
context_object_name = 'photo_list'
paginate_by = 36
def get_context_data(self, **kwargs):
context = generic.ListView.get_context_data(self, **kwargs)
context['event'] = self.event
context['form'] = forms.PhotoUploadForm(initial={'event':self.event, 'photographer': self.request.user})
return context
def get_queryset(self):
try:
self.event = models.Event.objects.get(id=self.kwargs['event'])
return models.Photo.objects.filter(event=self.event)
except models.Event.DoesNotExist:
raise Http404(_('Event does not exist'))
class EventPhotoUpload(generic.FormView):
form_class = forms.PhotoUploadForm
template_name = 'events/photo_upload.html'
@method_decorator(permission_required('events.add_photo'))
def dispatch(self, *args, **kwargs):
return super(EventPhotoUpload, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
context = generic.FormView.get_context_data(self, **kwargs)
context['event_list'] = models.Event.objects.archive()[:12]
return context
def get_initial(self):
'''
Set the current logged in user a default value for the photographer.
'''
return {
'photographer': self.request.user,
}
def post(self, *args, **kwargs):
'''
'''
self.event = models.Event.objects.get(id=self.request.REQUEST.get('event'))
photographer = self.request.POST.get('photographer')
if photographer:
photographer = User.objects.get(id=photographer)
else:
photographer = self.request.user
self.counter = 1
for upload in self.request.FILES.getlist('upload'):
name = upload.name
created_date, description = self.read_exif(upload)
photo = models.Photo(
event=self.event,
photographer=photographer,
image=upload,
name=name,
created_date=created_date,
description=description
)
photo.save()
self.counter += 1
return redirect('event-photo-list', event=self.event.id)
def read_exif(self, photo):
exif_data = pyexiv2.ImageMetadata.from_buffer(photo.read())
exif_data.read()
try:
created_date = exif_data['Exif.Image.DateTime'].value
except:
created_date = self.event.start + timedelta(minutes=self.counter)
try:
description = exif_data['Exif.Image.ImageDescription'].value
except:
description = ''
return (created_date, description)
class UpcomingEvents(generic.ListView):
queryset = models.Event.objects.upcoming(limit=None)
paginate_by = 12