Milestone 08-14

* Mahjong Ranking wurde stark vereinfacht um Fehler besser vorzubeugen.
* Online WYSIWYG Editor auf CKEditor umgeändert, damit online bearbeiten für unbedarfte besser funktioniert.
* Viele kleine Optimierungen am CSS für bessere Performance.
* CSS wird jetzt aus LESS Code generiert
* Für dise Arbeit wird jetzt grunt und node package management lokal verwendet.
This commit is contained in:
Christian Berg
2015-08-23 16:37:39 +02:00
parent 8981d4b261
commit a7bfd2157d
279 changed files with 14708 additions and 2429 deletions

View File

@@ -7,19 +7,19 @@ msgid ""
msgstr ""
"Project-Id-Version: kasu.events\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-08-16 11:34+0200\n"
"PO-Revision-Date: 2014-12-08 16:06+0100\n"
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
"POT-Creation-Date: 2015-08-22 23:28+0200\n"
"PO-Revision-Date: 2015-08-22 15:08+0100\n"
"Last-Translator: Christian Berg <xeniac@posteo.at>\n"
"Language-Team: Kasu <verein@kasu.at>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Translated-Using: django-rosetta 0.7.2\n"
"X-Generator: Poedit 1.6.11\n"
"X-Generator: Poedit 1.8.3\n"
"X-Translated-Using: django-rosetta 0.7.6\n"
#: admin.py:17 models.py:81
#: admin.py:17 models.py:82
msgid "Event Series"
msgstr "Veranstaltungsreihen"
@@ -35,11 +35,11 @@ msgstr "Beginn"
msgid "end"
msgstr "Ende"
#: models.py:67 models.py:161
#: models.py:67 models.py:163
msgid "Name"
msgstr "Name"
#: models.py:68 models.py:162
#: models.py:68 models.py:164
msgid "Description"
msgstr "Beschreibung"
@@ -53,18 +53,18 @@ msgstr "Beginn"
msgid "End"
msgstr "Ende"
#: models.py:72 models.py:165 templates/events/event_detail.html:36
#: models.py:72 models.py:167 templates/events/event_detail.html:36
#: templates/events/event_detail.html:72 templates/events/event_detail.html:78
msgid "Homepage"
msgstr "Homepage"
#: models.py:73 models.py:163
#: models.py:73 models.py:165
msgid "Image"
msgstr "Bild"
#: models.py:75
msgid "Tournament"
msgstr "Turnier"
msgid "Mahjong Tournament"
msgstr "Mahjong Turnier"
#: models.py:76
msgid ""
@@ -74,7 +74,11 @@ msgstr ""
"Diese Veranstaltung ist ein Turnier, es gelten andere Regeln für das Kyu "
"Ranking."
#: models.py:82
#: models.py:78
msgid "Mahjong Season"
msgstr ""
#: models.py:83
msgid ""
"Wenn dieser Event zu einer Veranstaltungsreihe gehört werden Ort, "
"Beschreibung, Bild und Homepage von dem hier angegebenen Event "
@@ -83,35 +87,35 @@ msgstr ""
"Wenn dieser Termin zu einer Veranstaltungsreihe gehört werden Ort, "
"Beschreibung, Bild und Homepage von dem hier angegebenen Event übernommen."
#: models.py:88
#: models.py:90
msgid "Event"
msgstr "Termin"
#: models.py:89
#: models.py:91
msgid "Events"
msgstr "Termine"
#: models.py:166
#: models.py:168
msgid "Postal Code"
msgstr "Postleitzahl"
#: models.py:167
#: models.py:169
msgid "Street Address"
msgstr "Straße"
#: models.py:168
#: models.py:170
msgid "Locality"
msgstr "Ort"
#: models.py:169
#: models.py:171
msgid "Country"
msgstr "Land"
#: models.py:172
#: models.py:174
msgid "Venue"
msgstr "Veranstaltungsort"
#: models.py:173
#: models.py:175
msgid "Venues"
msgstr "Veranstaltungsorte"
@@ -198,10 +202,8 @@ msgid "Edit Event"
msgstr "Termin bearbeiten"
#: templates/events/event_detail.html:112
#, fuzzy
#| msgid "Date"
msgid "Add Dates"
msgstr "Datum"
msgstr "Termine hinzufügen"
#: templates/events/event_form.html:9 templates/events/page.html:9
#: views.py:105
@@ -223,7 +225,7 @@ msgstr "Bevorstehende Veranstaltungen"
#: templates/events/eventseries_form.html:29
msgid "back"
msgstr ""
msgstr "Zurück"
#: views.py:214
msgid "Event does not exist"

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('events', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='event',
name='is_tournament',
),
migrations.AddField(
model_name='event',
name='mahjong_season',
field=models.PositiveSmallIntegerField(null=True, verbose_name='Mahjong Season', blank=True),
),
migrations.AddField(
model_name='event',
name='mahjong_tournament',
field=models.BooleanField(default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Mahjong Tournament'),
),
]

View File

@@ -72,16 +72,18 @@ class Event(image_models.ImageModel):
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 \
mahjong_tournament = models.BooleanField(_('Mahjong Tournament'), default=False,
help_text=_(u'This event is a tournament, different rules apply for \
the kyu ranking.'))
mahjong_season = models.PositiveSmallIntegerField(_('Mahjong Season'), blank=True, null=True)
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 \
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.'))
Event übernommen.')
)
objects = EventManager()
class Meta(object):

View File

@@ -36,7 +36,7 @@
{% if event.url %}<li><span class="fa-li fa fa-globe"></span> <strong>{% trans "Homepage" %}:</strong> <a href="{{ event.url }}">{{ event.url }}</a></li>{% endif %}
<li><span class="fa-li fa fa-table"></span> <strong>{% trans "Hanchans" %}:</strong> <a href="{% url 'event-hanchan-list' event.pk %}" >{{ event.hanchan_set.count }}</a></li>
<li><span class="fa-li fa fa-camera-retro"></span> <strong>{% trans 'Photos' %}:</strong> <a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }}</a></li>
{% if event.is_tournament %}<li><span class="fa-li fa fa-trophy"></span> <strong>{% trans "tourney" %}:</strong> <a href="{% url 'event-ranking' event.pk %}">{% trans "other rules apply here" %}</a></li>{% endif%}
{% if event.mahjong_tournament %}<li><span class="fa-li fa fa-trophy"></span> <strong>{% trans "tourney" %}:</strong> <a href="{% url 'event-ranking' event.pk %}">{% trans "other rules apply here" %}</a></li>{% endif%}
</ul>
{% endblock %}
@@ -49,7 +49,7 @@
<li><a href="{% url 'event-hanchan-list' event.pk %}" ><span class="fa fa-table"></span> {{ event.hanchan_set.count }} {% trans "Hanchans" %}</a></li>
<li><a href="{% url 'maistar-game-list' event.pk %}" ><span class="fa fa-glass"></span> {{ event.maistargame_set.count }} {% trans "Mai-Star Games" %}</a></li>
{% if event.is_tournament %}
{% if event.mahjong_tournament %}
<li><a href="{% url 'event-ranking' event.id %}"><span class="fa fa-trophy"></span> {% trans "Tournament Ranking" %}</a></li>
{% endif %}
</ul>

225
src/events/views.py~ Normal file
View File

@@ -0,0 +1,225 @@
# Create your views here.
from . import models, forms
from datetime import datetime
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
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views import generic
from utils.icalendar import Calendar, Event
from dateutil.tz import gettz
import pyexiv2
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(generic.UpdateView):
template_name = 'events/event_form.html'
form_class = forms.EventForm
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'):
return models.Event.objects.get(pk=self.kwargs['pk'])
else:
return models.Event()
@method_decorator(permission_required('events.update_event'))
def dispatch(self, *args, **kwargs):
return super(EventForm, self).dispatch(*args, **kwargs)
class EventGallery(generic.ListView):
template_name = 'events/event_gallery.html'
queryset = models.Event.objects.filter(start__lt=datetime.now(), photo_count__gt=0)
class EventListIcal(generic.View):
'''
Generates an returns an iCal File with all upcoming events.
'''
def add_event(self, event):
ics_event = Event()
dtstart = event.start.replace(tzinfo=self.tzinfo)
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('attach', '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:
dtend = event.end.replace(tzinfo=self.tzinfo)
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')
self.tzinfo = gettz('CET')
for event in models.Event.objects.upcoming(limit=None):
self.add_event(event)
response.write(self.calendar.as_string())
return response
class EventPhoto(generic.UpdateView):
form_class = forms.EditPhotoForm
model = models.Photo
template_name = 'events/event_photo.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'])
photo.save()
#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
template_name = 'events/event_photolist.html'
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})
print self.event.get_absolute_url()
return context
def get_queryset(self):
self.event = models.Event.objects.get(id=self.kwargs['event'])
return models.Photo.objects.filter(event=self.event)
class EventPhotoUpload(generic.FormView):
form_class = forms.PhotoUploadForm
template_name = 'events/upload_photo.html'
@method_decorator(permission_required('event.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):
'''
'''
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
for upload in self.request.FILES.getlist('upload'):
name = upload.name
created_date, description = self.read_exif(upload)
photo = models.Photo(
event=event,
photographer=photographer,
image=upload,
name=name,
created_date=created_date,
description=description
)
photo.save()
return redirect('event-photo-list', event=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 = datetime.now()
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