Another Step in the Quest to clean up the code base.
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
"""
|
||||
Created on 19.09.2011
|
||||
"""Django admin interface for the event app.
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
# import stuff we need from django
|
||||
It's the best way to add eventseries, or edit/delete events."""
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
@@ -11,12 +8,14 @@ from events.models import Event, Photo, Location
|
||||
|
||||
|
||||
class EventInline(admin.TabularInline):
|
||||
"""To list events of an eventseries below the 'master event'"""
|
||||
model = Event
|
||||
fields = ('name', 'start', 'end')
|
||||
verbose_name_plural = _('Event Series')
|
||||
|
||||
|
||||
class EventAdmin(admin.ModelAdmin):
|
||||
"""Admin Interface to list and edit events."""
|
||||
list_display = ('name', 'start', 'end', 'location',)
|
||||
list_editable = ('start', 'end', 'location')
|
||||
readonly_fields = ('event_series',)
|
||||
@@ -27,11 +26,12 @@ class EventAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
class LocationAdmin(admin.ModelAdmin):
|
||||
"""Admin Interace to list and edit event locations."""
|
||||
list_display = ('name', 'street_address', 'postal_code', 'locality')
|
||||
|
||||
|
||||
class PhotoAdmin(admin.ModelAdmin):
|
||||
# admin_thumbnail = AdminThumbnail(image_field='thumbnail')
|
||||
"""Admin Interface to list and edit photos of events."""
|
||||
fields = ('image', 'event', 'name', 'description',
|
||||
('photographer', 'created_date'))
|
||||
list_filter = ('event', 'on_startpage',)
|
||||
@@ -41,8 +41,6 @@ class PhotoAdmin(admin.ModelAdmin):
|
||||
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)
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
# -*- encoding: UTF-8 -*-
|
||||
"""
|
||||
Created on 30.09.2011
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
""" Content processor to display upcoming events on every page you want. """
|
||||
from django.core.cache import cache
|
||||
from .models import Event
|
||||
|
||||
|
||||
def upcoming_events(request):
|
||||
def events_overview(request): # Ignore PyLintBear (W0613)
|
||||
"""
|
||||
Adds event information as variables to the template context on every page.
|
||||
|
||||
For speed reasons everything will be cached for an hour. the following
|
||||
variables will be added to the template context:
|
||||
* current_event: If an event is running at this moment, the correspondi
|
||||
event object.
|
||||
* next_event: the next event that is upcoming.
|
||||
* upcoming_events: the next 3 events that are upcoming.
|
||||
|
||||
:param request: An Django HTTPRequest object
|
||||
:return: dict() with the new context variables
|
||||
"""
|
||||
current_event = cache.get('current_event', False)
|
||||
next_event = cache.get('next_event', False)
|
||||
upcoming_events = cache.get('upcoming_events', False)
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
"""
|
||||
Created on 03.10.2011
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
"""Django Forms to administrate the event content on the frontend."""
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
user_query = get_user_model().objects.all()
|
||||
|
||||
|
||||
class PhotoUploadForm(forms.Form):
|
||||
"""Form to upload multiple photos to a single event."""
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
photographer = forms.ModelChoiceField(user_query, required=True, )
|
||||
photographer = forms.ModelChoiceField(get_user_model().objects.all(),
|
||||
required=True, )
|
||||
event = forms.ModelChoiceField(models.Event.objects.all(), required=True, )
|
||||
upload = forms.FileField(
|
||||
label=_('Images'),
|
||||
@@ -31,16 +26,19 @@ class PhotoUploadForm(forms.Form):
|
||||
|
||||
|
||||
class EditPhotoForm(forms.ModelForm):
|
||||
"""From to edit the metadata of a photo."""
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta(object):
|
||||
"""Metadata to localize and customize the ModelForm."""
|
||||
model = models.Photo
|
||||
fields = ('event', 'name', 'description', 'photographer',
|
||||
'created_date', 'on_startpage')
|
||||
|
||||
|
||||
class EventForm(forms.ModelForm):
|
||||
"""Form to add or edit an event."""
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
@@ -54,9 +52,13 @@ class EventForm(forms.ModelForm):
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
"""Metadata to localize and customize the ModelForm."""
|
||||
model = models.Event
|
||||
exclude = ('event_count', 'event_series', )
|
||||
exclude = ('event_count', 'event_series',)
|
||||
|
||||
|
||||
EventSeriesFormset = forms.inlineformset_factory(
|
||||
models.Event, models.Event, fields=('start', 'end'), form=EventForm)
|
||||
EventSeriesFormset = forms.inlineformset_factory( # Ignore PyLintBear (C0103)
|
||||
models.Event,
|
||||
models.Event,
|
||||
fields=('start', 'end'),
|
||||
form=EventForm)
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
""" urls for the event gallery part of the events app. """
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import *
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', EventGallery.as_view(), name='event-gallery'),
|
||||
url(r'^(?P<event>[\d]+)/$', EventPhotoList.as_view(),
|
||||
url(r'^$', views.EventGallery.as_view(), name='event-gallery'),
|
||||
url(r'^(?P<event>[\d]+)/$', views.EventPhotoList.as_view(),
|
||||
name='event-photo-list'),
|
||||
url(r'^(?P<event>[\d]+)/upload/$', EventPhotoUpload.as_view(),
|
||||
url(r'^(?P<event>[\d]+)/upload/$', views.EventPhotoUpload.as_view(),
|
||||
name='event-photo-upload'),
|
||||
url(r'^(?P<event>[\d]+)/(?P<pk>[\d]+)/$', EventPhoto.as_view(),
|
||||
url(r'^(?P<event>[\d]+)/(?P<pk>[\d]+)/$', views.EventPhoto.as_view(),
|
||||
name='event-photo'),
|
||||
url(r'^delete/(?P<pk>[\d]+)/$', DeleteEventPhoto.as_view(),
|
||||
url(r'^delete/(?P<pk>[\d]+)/$', views.DeleteEventPhoto.as_view(),
|
||||
name='delete-event-photo'),
|
||||
url(r'^upload/$', EventPhotoUpload.as_view(), name='event-photo-upload'),
|
||||
url(r'^upload/$', views.EventPhotoUpload.as_view(),
|
||||
name='event-photo-upload'),
|
||||
]
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: kasu.events\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-05-10 23:16+0200\n"
|
||||
"POT-Creation-Date: 2017-06-19 22:46+0200\n"
|
||||
"PO-Revision-Date: 2016-09-28 00:24+0200\n"
|
||||
"Last-Translator: Christian Berg <xeniac@posteo.at>\n"
|
||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||
@@ -19,58 +19,58 @@ msgstr ""
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
"X-Translated-Using: django-rosetta 0.7.6\n"
|
||||
|
||||
#: events/admin.py:16 events/models.py:114
|
||||
#: events/admin.py:14 events/models.py:82
|
||||
msgid "Event Series"
|
||||
msgstr "Veranstaltungsreihen"
|
||||
|
||||
#: events/forms.py:23
|
||||
#: events/forms.py:18
|
||||
msgid "Images"
|
||||
msgstr "Bilder"
|
||||
|
||||
#: events/forms.py:49
|
||||
#: events/forms.py:46
|
||||
msgid "start"
|
||||
msgstr "Beginn"
|
||||
|
||||
#: events/forms.py:53
|
||||
#: events/forms.py:50
|
||||
msgid "end"
|
||||
msgstr "Ende"
|
||||
|
||||
#: events/models.py:84 events/models.py:207 events/models.py:260
|
||||
#: events/models.py:52 events/models.py:176 events/models.py:217
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: events/models.py:85 events/models.py:208 events/models.py:268
|
||||
#: events/models.py:53 events/models.py:177 events/models.py:225
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: events/models.py:87 events/templates/events/event_detail.html:29
|
||||
#: events/models.py:55 events/templates/events/event_detail.html:29
|
||||
#: events/templates/events/event_detail.html:87
|
||||
#: events/templates/events/event_list.html:28
|
||||
#: events/templates/events/photo_upload.html:13
|
||||
msgid "Start"
|
||||
msgstr "Beginn"
|
||||
|
||||
#: events/models.py:88 events/templates/events/event_detail.html:30
|
||||
#: events/models.py:56 events/templates/events/event_detail.html:30
|
||||
#: events/templates/events/event_detail.html:89
|
||||
msgid "End"
|
||||
msgstr "Ende"
|
||||
|
||||
#: events/models.py:89 events/models.py:216
|
||||
#: events/models.py:57 events/models.py:185
|
||||
#: events/templates/events/event_detail.html:34
|
||||
#: events/templates/events/event_detail.html:80
|
||||
#: events/templates/events/event_detail.html:92
|
||||
msgid "Homepage"
|
||||
msgstr "Homepage"
|
||||
|
||||
#: events/models.py:91 events/models.py:210 events/models.py:262
|
||||
#: events/models.py:59 events/models.py:179 events/models.py:219
|
||||
msgid "Image"
|
||||
msgstr "Bild"
|
||||
|
||||
#: events/models.py:98
|
||||
#: events/models.py:66
|
||||
msgid "Mahjong Tournament"
|
||||
msgstr "Mahjong Turnier"
|
||||
|
||||
#: events/models.py:100
|
||||
#: events/models.py:68
|
||||
msgid ""
|
||||
"This event is a tournament, different rules apply for the kyu "
|
||||
"ranking."
|
||||
@@ -78,11 +78,11 @@ msgstr ""
|
||||
"Diese Veranstaltung ist ein Turnier, es gelten andere Regeln für das Kyu "
|
||||
"Ranking."
|
||||
|
||||
#: events/models.py:104
|
||||
#: events/models.py:72
|
||||
msgid "Mahjong Season"
|
||||
msgstr "Mahjong Saison"
|
||||
|
||||
#: events/models.py:115
|
||||
#: events/models.py:83
|
||||
msgid ""
|
||||
"Wenn dieser Event zu einer Veranstaltungsreihe gehört werden Ort, "
|
||||
"Beschreibung, Bild und Homepage von dem hier angegebenen Event "
|
||||
@@ -91,72 +91,72 @@ msgstr ""
|
||||
"Wenn dieser Termin zu einer Veranstaltungsreihe gehört werden Ort, "
|
||||
"Beschreibung, Bild und Homepage von dem hier angegebenen Event übernommen."
|
||||
|
||||
#: events/models.py:124 events/models.py:226 events/models.py:290
|
||||
#: events/models.py:92 events/models.py:195 events/models.py:247
|
||||
msgid "first created at"
|
||||
msgstr ""
|
||||
|
||||
#: events/models.py:129 events/models.py:231 events/models.py:295
|
||||
#: events/models.py:97 events/models.py:200 events/models.py:252
|
||||
msgid "latest updated at"
|
||||
msgstr ""
|
||||
|
||||
#: events/models.py:134
|
||||
#: events/models.py:103
|
||||
msgid "Event"
|
||||
msgstr "Termin"
|
||||
|
||||
#: events/models.py:135
|
||||
#: events/models.py:104
|
||||
msgid "Events"
|
||||
msgstr "Termine"
|
||||
|
||||
#: events/models.py:148
|
||||
#: events/models.py:117
|
||||
msgid "A event can't end before it had started"
|
||||
msgstr "Eine Veranstaltung kann nicht enden bevor sie begonnen hat"
|
||||
|
||||
#: events/models.py:217
|
||||
#: events/models.py:186
|
||||
msgid "Postal Code"
|
||||
msgstr "Postleitzahl"
|
||||
|
||||
#: events/models.py:218
|
||||
#: events/models.py:187
|
||||
msgid "Street Address"
|
||||
msgstr "Straße"
|
||||
|
||||
#: events/models.py:219
|
||||
#: events/models.py:188
|
||||
msgid "Locality"
|
||||
msgstr "Ort"
|
||||
|
||||
#: events/models.py:220
|
||||
#: events/models.py:189
|
||||
msgid "Country"
|
||||
msgstr "Land"
|
||||
|
||||
#: events/models.py:235
|
||||
#: events/models.py:204
|
||||
msgid "Venue"
|
||||
msgstr "Veranstaltungsort"
|
||||
|
||||
#: events/models.py:236
|
||||
#: events/models.py:205
|
||||
msgid "Venues"
|
||||
msgstr "Veranstaltungsorte"
|
||||
|
||||
#: events/models.py:274
|
||||
#: events/models.py:231
|
||||
msgid "Startpage"
|
||||
msgstr "Startseite"
|
||||
|
||||
#: events/models.py:277
|
||||
#: events/models.py:234
|
||||
msgid "Display this Photo on the Startpage Teaser"
|
||||
msgstr "Foto als Teaser auf der Startseite verwenden."
|
||||
|
||||
#: events/models.py:279
|
||||
#: events/models.py:236
|
||||
msgid "Published on"
|
||||
msgstr "Veröffentlicht am"
|
||||
|
||||
#: events/models.py:281
|
||||
#: events/models.py:238
|
||||
msgid "Number of views"
|
||||
msgstr "Wie oft gesehen"
|
||||
|
||||
#: events/models.py:306 events/templates/events/event_archive.html:38
|
||||
#: events/models.py:262 events/templates/events/event_archive.html:38
|
||||
#: events/templates/events/event_list.html:18
|
||||
msgid "Event Image"
|
||||
msgstr "Veranstaltungsbild"
|
||||
|
||||
#: events/models.py:307
|
||||
#: events/models.py:263
|
||||
msgid "Event Images"
|
||||
msgstr "Veranstaltungsbilder"
|
||||
|
||||
@@ -253,7 +253,7 @@ msgid "Show on Google Maps"
|
||||
msgstr "Auf Google Maps zeigen"
|
||||
|
||||
#: events/templates/events/event_detail.html:127
|
||||
#: events/templates/events/event_form.html:9 events/views.py:106
|
||||
#: events/templates/events/event_form.html:9 events/views.py:104
|
||||
msgid "Edit Event"
|
||||
msgstr "Termin bearbeiten"
|
||||
|
||||
@@ -262,16 +262,16 @@ msgid "Add Dates"
|
||||
msgstr "Termine hinzufügen"
|
||||
|
||||
#: events/templates/events/event_form.html:9
|
||||
#: events/templates/events/page.html:9 events/views.py:108
|
||||
#: events/templates/events/page.html:9 events/views.py:106
|
||||
msgid "Add Event"
|
||||
msgstr "Neuer Termin"
|
||||
|
||||
#: events/templates/events/event_form.html:19
|
||||
#: events/templates/events/event_form.html:18
|
||||
#: events/templates/events/photo_list.html:35
|
||||
msgid "reset"
|
||||
msgstr "Zurücksetzen"
|
||||
|
||||
#: events/templates/events/event_form.html:20
|
||||
#: events/templates/events/event_form.html:19
|
||||
#: events/templates/events/eventseries_form.html:25
|
||||
msgid "save"
|
||||
msgstr "Speichern"
|
||||
@@ -328,7 +328,7 @@ msgstr "Speichern"
|
||||
msgid "Upload"
|
||||
msgstr "Hochladen"
|
||||
|
||||
#: events/views.py:209
|
||||
#: events/views.py:203
|
||||
msgid "Event does not exist"
|
||||
msgstr "Veranstaltung gibt es nicht"
|
||||
|
||||
|
||||
40
src/events/managers.py
Normal file
40
src/events/managers.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""Django ORM Managers for the event models."""
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
class EventManager(models.Manager):
|
||||
"""Django ORM Manager that adds some queryshortcuts to Event Models."""
|
||||
|
||||
def get_queryset(self):
|
||||
"""Joins the location info to every event query."""
|
||||
return super(EventManager, self).get_queryset().select_related(
|
||||
'location')
|
||||
|
||||
def current_event(self):
|
||||
"""Returns the event that is currently running."""
|
||||
try:
|
||||
current = self.filter(start__lte=now())
|
||||
current = current.filter(end__gte=now())
|
||||
return current.order_by('start', 'end')[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def next_event(self):
|
||||
"""Returns the next upcoming event."""
|
||||
try:
|
||||
return self.filter(start__gt=now()).order_by('start', 'end')[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def archive(self):
|
||||
"""Returns all past events."""
|
||||
return self.filter(start__lt=now())
|
||||
|
||||
def upcoming(self, limit=None):
|
||||
"""Returns the next 'limit' upcoming events.
|
||||
|
||||
:param limit: how many upcoming events should be returned?
|
||||
"""
|
||||
result = self.filter(start__gt=now()).order_by('start', 'end')
|
||||
return result[0:limit] if limit else result
|
||||
43
src/events/mixins.py
Normal file
43
src/events/mixins.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""Mixins for Events."""
|
||||
from . import models
|
||||
|
||||
|
||||
# Ignore PyLintBear (R0903)
|
||||
class EventArchiveMixin(object):
|
||||
"""Adds an is_archive = True variable to the template context."""
|
||||
context_object_name = 'event_list'
|
||||
date_field = 'start'
|
||||
make_object_list = True
|
||||
model = models.Event
|
||||
ordering = ('start', 'end')
|
||||
paginate_by = 15
|
||||
template_name = 'events/event_archive.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add is_archive to the context and set it to true.
|
||||
|
||||
:return: TemplateContext object"""
|
||||
context = super(EventArchiveMixin, self).get_context_data(**kwargs)
|
||||
context['is_archive'] = True
|
||||
return context
|
||||
|
||||
|
||||
# Ignore PyLintBear (R0903)
|
||||
class EventDetailMixin(object):
|
||||
"""A very simple Mixin to add the related event to the template context."""
|
||||
event = None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add self.event or the related event of self.object to the template
|
||||
context.
|
||||
|
||||
:return: TemplateContext object"""
|
||||
context = super(EventDetailMixin, self).get_context_data(**kwargs)
|
||||
if hasattr(self, 'event') and self.event:
|
||||
context['event'] = self.event
|
||||
elif hasattr(self, 'object') and isinstance(self.object, models.Event):
|
||||
context['event'] = self.object
|
||||
elif hasattr(self, 'object') and hasattr(self.object, 'event'):
|
||||
context['event'] = self.object.event
|
||||
print(context)
|
||||
return context
|
||||
@@ -1,5 +1,4 @@
|
||||
# -'- Encoding: utf-8 -*-
|
||||
|
||||
"""Models to solitary events, events series with an location and photos."""
|
||||
import os
|
||||
|
||||
from ckeditor.fields import RichTextField
|
||||
@@ -14,6 +13,7 @@ from django.utils.translation import ugettext as _
|
||||
from easy_thumbnails.fields import ThumbnailerImageField
|
||||
|
||||
from utils import COUNTRIES, OverwriteStorage
|
||||
from .managers import EventManager
|
||||
|
||||
|
||||
def get_upload_path(instance, filename):
|
||||
@@ -47,40 +47,8 @@ def get_upload_path(instance, filename):
|
||||
)
|
||||
|
||||
|
||||
class EventManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(EventManager, self).get_queryset().select_related(
|
||||
'location')
|
||||
|
||||
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 IndexError:
|
||||
return None
|
||||
|
||||
def next_event(self):
|
||||
try:
|
||||
return self.filter(start__gt=now()).order_by('start', 'end')[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def archive(self):
|
||||
return self.filter(start__lt=now())
|
||||
|
||||
def latest_events(self, num=3):
|
||||
return self.filter(start__lt=now()).order_by('-start', '-end')[:num]
|
||||
|
||||
def upcoming(self, limit=None):
|
||||
result = self.filter(start__gt=now()).order_by('start', 'end')
|
||||
if limit:
|
||||
return result[0:limit]
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
class Event(models.Model):
|
||||
"""An Event that could be a tournament, a game session, or an convention."""
|
||||
name = models.CharField(_('Name'), max_length=255)
|
||||
description = RichTextField(_("Description"), blank=True)
|
||||
location = models.ForeignKey('Location')
|
||||
@@ -131,6 +99,7 @@ class Event(models.Model):
|
||||
objects = EventManager()
|
||||
|
||||
class Meta(object):
|
||||
"""order the events by start date."""
|
||||
verbose_name = _('Event')
|
||||
verbose_name_plural = _('Events')
|
||||
ordering = ('start', 'end',)
|
||||
@@ -244,18 +213,6 @@ class Location(models.Model):
|
||||
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(models.Model):
|
||||
name = models.CharField(_("Name"), max_length=100, blank=True)
|
||||
image = ThumbnailerImageField(
|
||||
@@ -295,7 +252,6 @@ class Photo(models.Model):
|
||||
verbose_name=_('latest updated at'),
|
||||
)
|
||||
|
||||
objects = PhotoManager()
|
||||
metadata = None
|
||||
orientation = 1
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
"""To geneate a Sitemap with all events."""
|
||||
from django.contrib.sitemaps import Sitemap
|
||||
from django.utils import timezone
|
||||
from .models import Event
|
||||
from .models import Photo
|
||||
|
||||
|
||||
class EventSitemap(Sitemap):
|
||||
"""To add an event to the global sitemap."""
|
||||
changefreq = "never"
|
||||
protocol = 'https'
|
||||
|
||||
def items(self):
|
||||
"""add all events to the sitemap."""
|
||||
return Event.objects.all()
|
||||
|
||||
def priority(self, event):
|
||||
delta = timezone.now() - event.start
|
||||
delta = abs(delta.days / 300.0)
|
||||
"""give events closer to the present a higer priority for crawlers."""
|
||||
delta = abs((timezone.now() - event.start) / 300.0)
|
||||
return max(1 - delta, 0.1)
|
||||
|
||||
def lastmod(self, event):
|
||||
"""return the last modification date."""
|
||||
return event.date_modified
|
||||
|
||||
@@ -25,7 +25,7 @@ if ($('a.next').attr('href')) {
|
||||
{% block teaser %}
|
||||
<h1 class="grid_12">{{event.name}} - {{ photo.name }}</h1>
|
||||
{% if event.description %}
|
||||
<div id="teaser_text">{{event.description|truncatewords_html:75}}</div>
|
||||
<div id="teaser_text">{{event.description|truncatewords_html:75|safe}}</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
This file should test the functionality of the events app using the unittest
|
||||
module. These will pass when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
Usefull tests have to been written yet. sorry!
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
class EventTest(TestCase):
|
||||
""" Here we should test the creation and modifiaction of Events. """
|
||||
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
|
||||
class LocationTest(TestCase):
|
||||
""" Here we should test the creation and modifiaction of Locations. """
|
||||
|
||||
|
||||
class PhotoTest(TestCase):
|
||||
""" Here we should test the creation and modifiaction of Photos. """
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
"""URLS to access upcoming events and the event archive."""
|
||||
from django.conf.urls import url
|
||||
from django.views.generic import RedirectView
|
||||
from .views import *
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', RedirectView.as_view(url='/events/upcoming/', permanent=True)),
|
||||
url(r'^(?P<year>[\d]{4})/$', EventArchiveYear.as_view(),
|
||||
url(r'^(?P<year>[\d]{4})/$', views.EventArchiveYear.as_view(),
|
||||
name='event-archive'),
|
||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$', EventArchiveMonth.as_view(),
|
||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$',
|
||||
views.EventArchiveMonth.as_view(),
|
||||
name='event-archive'),
|
||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/$',
|
||||
EventDetail.as_view(), name='event-detail'),
|
||||
views.EventDetail.as_view(), name='event-detail'),
|
||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/add_dates/$',
|
||||
EventSeriesForm.as_view(), name='eventseries-form'),
|
||||
views.EventSeriesForm.as_view(), name='eventseries-form'),
|
||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/edit/$',
|
||||
EventForm.as_view(), name='event-form'),
|
||||
url(r'^add/$', EventForm.as_view(), name='event-form'),
|
||||
url(r'^archive/$', EventArchiveIndex.as_view(), name='event-archive'),
|
||||
url(r'^upcoming/$', UpcomingEvents.as_view(), name='upcoming-events'),
|
||||
views.EventForm.as_view(), name='event-form'),
|
||||
url(r'^add/$', views.EventForm.as_view(), name='event-form'),
|
||||
url(r'^archive/$', views.EventArchiveIndex.as_view(), name='event-archive'),
|
||||
url(r'^upcoming/$', views.UpcomingEvents.as_view(), name='upcoming-events'),
|
||||
]
|
||||
|
||||
@@ -1,107 +1,63 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Create your views here.
|
||||
""" All views to display or edit events and event-photos. """
|
||||
from datetime import timedelta
|
||||
|
||||
from django.db.models import Q
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.core.urlresolvers import reverse
|
||||
from extra_views import InlineFormSetView
|
||||
from extra_views import ModelFormSetView
|
||||
from django.db.models import Q
|
||||
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 extra_views import InlineFormSetView
|
||||
from icalendar import Calendar, Event
|
||||
|
||||
from utils.mixins import PermissionRequiredMixin
|
||||
|
||||
from . import models, forms
|
||||
from . import forms, mixins, models
|
||||
|
||||
|
||||
class DeleteEventPhoto(generic.DeleteView):
|
||||
class DeleteEventPhoto(PermissionRequiredMixin, mixins.EventDetailMixin,
|
||||
generic.DeleteView):
|
||||
"""Delete a requested photo and redirect to the album view."""
|
||||
model = models.Photo
|
||||
permission_required = 'events.delete_photo'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('event-photo-list', args=[self.object.event.id])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DeleteEventPhoto, self).get_context_data()
|
||||
context['event'] = self.object.event
|
||||
return context
|
||||
|
||||
@method_decorator(permission_required('events.delete_photo'))
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(DeleteEventPhoto, self).dispatch(*args, **kwargs)
|
||||
"""Redirect to the album view of the event from the deleted image."""
|
||||
return reverse('event-photo-list', args=[self.object.event_id])
|
||||
|
||||
|
||||
class EventArchiveIndex(generic.ArchiveIndexView):
|
||||
class EventArchiveIndex(mixins.EventArchiveMixin, generic.ArchiveIndexView):
|
||||
"""Index of the event archive, displays the upcoming events first."""
|
||||
allow_empty = True
|
||||
context_object_name = 'event_list'
|
||||
date_field = 'start'
|
||||
model = models.Event
|
||||
ordering = ('-start', '-end')
|
||||
paginate_by = 15
|
||||
|
||||
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
|
||||
ordering = ('start', 'end')
|
||||
class EventArchiveMonth(mixins.EventArchiveMixin, generic.MonthArchiveView):
|
||||
"""List the events from the specific month of the given year."""
|
||||
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):
|
||||
model = models.Event
|
||||
date_field = 'start'
|
||||
make_object_list = True
|
||||
ordering = ('start', 'end')
|
||||
paginate_by = 15
|
||||
template_name = 'events/event_archive.html'
|
||||
class EventArchiveYear(mixins.EventArchiveMixin, generic.YearArchiveView):
|
||||
"""List all events from the specified year."""
|
||||
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):
|
||||
class EventDetail(mixins.EventDetailMixin, generic.DetailView):
|
||||
"""Detail View to see all details of an event."""
|
||||
model = models.Event
|
||||
|
||||
|
||||
class EventDetailMixin(object):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EventDetailMixin, self).get_context_data(**kwargs)
|
||||
if hasattr(self, 'event'):
|
||||
context['event'] = self.event
|
||||
else:
|
||||
context['event'] = self.object.event
|
||||
return context
|
||||
|
||||
|
||||
class EventForm(PermissionRequiredMixin, generic.UpdateView):
|
||||
class EventForm(PermissionRequiredMixin, mixins.EventDetailMixin,
|
||||
generic.UpdateView):
|
||||
"""Frontend formular to add or edit a Event."""
|
||||
form_class = forms.EventForm
|
||||
template_name = 'events/event_form.html'
|
||||
permission_required = 'events.add_event'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Dynamicle set the title to Add or Edit Event, depanding if an
|
||||
event ID was given, or not."""
|
||||
context = super(EventForm, self).get_context_data(**kwargs)
|
||||
if self.kwargs.get('pk'):
|
||||
context['title'] = _("Edit Event")
|
||||
@@ -110,18 +66,15 @@ class EventForm(PermissionRequiredMixin, generic.UpdateView):
|
||||
return context
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
"""Try return the existing Event for an update if an id has been
|
||||
submitted, else create a new one.
|
||||
"""
|
||||
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'):
|
||||
return models.Event.objects.get(pk=self.kwargs['pk'])
|
||||
else:
|
||||
return models.Event()
|
||||
return models.Event.objects.get(pk=self.kwargs['pk']) \
|
||||
if self.kwargs.get('pk') else models.Event()
|
||||
|
||||
|
||||
class EventGallery(generic.ListView):
|
||||
class EventGallery(mixins.EventDetailMixin, generic.ListView):
|
||||
"""Display a overview of all event photo albums."""
|
||||
template_name = 'events/photo_gallery.html'
|
||||
queryset = models.Event.objects.filter(
|
||||
start__lt=timezone.now(),
|
||||
@@ -132,69 +85,56 @@ class EventGallery(generic.ListView):
|
||||
|
||||
|
||||
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)
|
||||
"""Generates an returns an iCal File with all upcoming events."""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Add all upcoming events to an .ics file and send it."""
|
||||
response = HttpResponse(content_type="text/calendar; charset=UTF-8")
|
||||
self.calendar = Calendar()
|
||||
self.calendar.add('prodid', 'http://www.kasu.at/')
|
||||
self.calendar.add('version', '2.0')
|
||||
calendar = Calendar()
|
||||
calendar.add('prodid', 'http://www.kasu.at/')
|
||||
calendar.add('version', '2.0')
|
||||
for event in models.Event.objects.upcoming(limit=None):
|
||||
self.add_event(event)
|
||||
response.write(self.calendar.to_ical())
|
||||
ics_event = Event()
|
||||
ics_event.add('DTSTART', timezone.localtime(event.start))
|
||||
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', timezone.localtime(event.end))
|
||||
calendar.add_component(ics_event)
|
||||
response.write(calendar.to_ical())
|
||||
return response
|
||||
|
||||
|
||||
class EventPhoto(generic.UpdateView):
|
||||
class EventPhoto(mixins.EventDetailMixin, generic.UpdateView):
|
||||
"""Display the requested Photo and allows rotation if the user has change
|
||||
permissions."""
|
||||
form_class = forms.EditPhotoForm
|
||||
model = models.Photo
|
||||
template_name = 'events/photo_detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EventPhoto, self).get_context_data()
|
||||
try:
|
||||
event = models.Event.objects.get(id=self.kwargs['event'])
|
||||
except models.Event.DoesNotExist:
|
||||
event = self.object.event
|
||||
context['event'] = event
|
||||
return context
|
||||
|
||||
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):
|
||||
class EventPhotoList(mixins.EventDetailMixin, generic.ListView):
|
||||
"""List all Photos of the event or event series in an album."""
|
||||
context_object_name = 'photo_list'
|
||||
event = None
|
||||
paginate_by = 36
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['event'] = self.event
|
||||
context = super(EventPhotoList, self).get_context_data(**kwargs)
|
||||
context['form'] = forms.PhotoUploadForm(
|
||||
initial={'event': self.event, 'photographer': self.request.user})
|
||||
return context
|
||||
@@ -210,13 +150,10 @@ class EventPhotoList(generic.ListView):
|
||||
raise Http404(_('Event does not exist'))
|
||||
|
||||
|
||||
class EventPhotoUpload(generic.FormView):
|
||||
class EventPhotoUpload(mixins.EventDetailMixin, 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)
|
||||
permission_required = 'events.add_photo'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.FormView.get_context_data(self, **kwargs)
|
||||
@@ -224,45 +161,30 @@ class EventPhotoUpload(generic.FormView):
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
"""
|
||||
Set the current logged in user a default value for the photographer.
|
||||
"""
|
||||
return {
|
||||
'photographer': self.request.user,
|
||||
}
|
||||
""" Set the current user as 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',
|
||||
self.request.user.id)
|
||||
photographer = get_user_model().objects.get(id=photographer)
|
||||
self.counter = 1
|
||||
for upload in self.request.FILES.getlist('upload'):
|
||||
name = upload.name
|
||||
created_date, description = self.read_exif(upload)
|
||||
event = models.Event.objects.get(id=self.request.POST.get('event'))
|
||||
photographer = get_user_model().objects.get(
|
||||
id=self.request.POST.get('photographer', self.request.user.id))
|
||||
counter = 0
|
||||
for image_file in self.request.FILES.getlist('upload'):
|
||||
photo = models.Photo(
|
||||
event=self.event,
|
||||
event=event,
|
||||
photographer=photographer,
|
||||
image=upload,
|
||||
name=name,
|
||||
created_date=created_date,
|
||||
description=description
|
||||
image=image_file,
|
||||
name=image_file.name,
|
||||
created_date=event.start + timedelta(minutes=counter),
|
||||
description=''
|
||||
)
|
||||
photo.save()
|
||||
self.counter += 1
|
||||
return redirect('event-photo-list', event=self.event.id)
|
||||
|
||||
def read_exif(self, photo):
|
||||
created_date = self.event.start + timedelta(minutes=self.counter)
|
||||
description = ''
|
||||
return created_date, description
|
||||
counter += 1
|
||||
return redirect('event-photo-list', event=event.id)
|
||||
|
||||
|
||||
class EventSeriesForm(EventDetailMixin, PermissionRequiredMixin, InlineFormSetView):
|
||||
class EventSeriesForm(mixins.EventDetailMixin, PermissionRequiredMixin,
|
||||
InlineFormSetView):
|
||||
model = models.Event
|
||||
inline_model = models.Event
|
||||
fk_name = 'event_series'
|
||||
|
||||
Reference in New Issue
Block a user