port to new django, AI automated
This commit is contained in:
@@ -1,20 +1,69 @@
|
||||
#from django.contrib import admin
|
||||
#from eventplanner.models import Event, EventParticipation
|
||||
#
|
||||
#
|
||||
#class EventParticipationInline(admin.TabularInline):
|
||||
# model = EventParticipation
|
||||
# extra = 1
|
||||
# readonly_fields = ('user',)
|
||||
# fields = ('user', 'status', 'comment',)
|
||||
# has_add_permission = lambda self, req: False
|
||||
# has_delete_permission = lambda self, req, obj: False
|
||||
#
|
||||
# template = "eventplanner/admin_tabular.html"
|
||||
#
|
||||
#
|
||||
#class EventAdmin(admin.ModelAdmin):
|
||||
# inlines = (EventParticipationInline,)
|
||||
#
|
||||
#
|
||||
#admin.site.register(Event, EventAdmin)
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Event, EventParticipation
|
||||
|
||||
|
||||
class EventParticipationInline(admin.TabularInline):
|
||||
"""Inline admin for event participations."""
|
||||
|
||||
model = EventParticipation
|
||||
extra = 0
|
||||
readonly_fields = ("user",)
|
||||
fields = ("user", "status", "comment")
|
||||
|
||||
def has_add_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(Event)
|
||||
class EventAdmin(admin.ModelAdmin):
|
||||
"""Admin configuration for Event model."""
|
||||
|
||||
list_display = ("title", "type", "date", "time", "location")
|
||||
list_filter = ("type", "date")
|
||||
search_fields = ("short_desc", "location", "desc")
|
||||
date_hierarchy = "date"
|
||||
ordering = ("-date",)
|
||||
|
||||
inlines = (EventParticipationInline,)
|
||||
|
||||
fieldsets = (
|
||||
(
|
||||
None,
|
||||
{
|
||||
"fields": ("type", "short_desc", "date", "end_date"),
|
||||
},
|
||||
),
|
||||
(
|
||||
"Time",
|
||||
{
|
||||
"fields": ("time", "meeting_time"),
|
||||
},
|
||||
),
|
||||
(
|
||||
"Location",
|
||||
{
|
||||
"fields": ("location", "map_location"),
|
||||
},
|
||||
),
|
||||
(
|
||||
"Description",
|
||||
{
|
||||
"fields": ("desc",),
|
||||
"classes": ("collapse",),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(EventParticipation)
|
||||
class EventParticipationAdmin(admin.ModelAdmin):
|
||||
"""Admin configuration for EventParticipation model."""
|
||||
|
||||
list_display = ("event", "user", "status", "comment")
|
||||
list_filter = ("status", "event__date")
|
||||
search_fields = ("user__username", "event__short_desc", "comment")
|
||||
raw_id_fields = ("event", "user")
|
||||
|
||||
55
eventplanner/migrations/0001_initial.py
Normal file
55
eventplanner/migrations/0001_initial.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Generated by Django 5.1.15 on 2026-03-30 19:15
|
||||
|
||||
import django.db.models.deletion
|
||||
import location_field.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Event',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(choices=[('Reh', 'Rehearsal'), ('Conc', 'Concert'), ('Party', 'Party'), ('Travel', 'Travel'), ('Option', 'Option')], default='Reh', max_length=6, verbose_name='type')),
|
||||
('short_desc', models.CharField(blank=True, max_length=100, null=True, verbose_name='Short Description')),
|
||||
('location', models.TextField(blank=True, verbose_name='location')),
|
||||
('map_location', location_field.models.PlainLocationField(based_field=models.TextField(blank=True, verbose_name='location'), blank=True, max_length=63, verbose_name='Location on map', zoom=7)),
|
||||
('desc', models.TextField(blank=True, verbose_name='description')),
|
||||
('date', models.DateField(verbose_name='date')),
|
||||
('time', models.TimeField(blank=True, null=True, verbose_name='time')),
|
||||
('meeting_time', models.TimeField(blank=True, null=True, verbose_name='meeting_time')),
|
||||
('end_date', models.DateField(blank=True, null=True, verbose_name='End Date')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['date', 'time'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventParticipation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('status', models.CharField(choices=[('?', '?'), ('Yes', 'Yes'), ('No', 'No'), ('-', '-')], default='?', max_length=3, verbose_name='status')),
|
||||
('comment', models.CharField(blank=True, max_length=64, verbose_name='comment')),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eventplanner.event', verbose_name='event')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||
],
|
||||
options={
|
||||
'permissions': (('admin', 'Admin'), ('member', 'Member')),
|
||||
'unique_together': {('event', 'user')},
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='participants',
|
||||
field=models.ManyToManyField(through='eventplanner.EventParticipation', to=settings.AUTH_USER_MODEL, verbose_name='participants'),
|
||||
),
|
||||
]
|
||||
0
eventplanner/migrations/__init__.py
Normal file
0
eventplanner/migrations/__init__.py
Normal file
@@ -1,9 +1,10 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.contrib.auth.models import User, Permission
|
||||
from django.db.models import Q
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from location_field.models import PlainLocationField
|
||||
|
||||
|
||||
@@ -14,38 +15,53 @@ class NoNextEventException(Exception):
|
||||
|
||||
class Event(models.Model):
|
||||
EVENT_TYPES = (
|
||||
('Reh', _('Rehearsal')),
|
||||
('Conc', _('Concert')),
|
||||
('Party', _('Party')),
|
||||
('Travel', _('Travel')),
|
||||
('Option', _('Option')),
|
||||
("Reh", _("Rehearsal")),
|
||||
("Conc", _("Concert")),
|
||||
("Party", _("Party")),
|
||||
("Travel", _("Travel")),
|
||||
("Option", _("Option")),
|
||||
)
|
||||
|
||||
type = models.CharField(max_length=6, choices=EVENT_TYPES, default='Reh', verbose_name=_("type"))
|
||||
short_desc = models.CharField(null=True, max_length=100, blank=True, verbose_name=_("Short Description"))
|
||||
type = models.CharField(
|
||||
max_length=6, choices=EVENT_TYPES, default="Reh", verbose_name=_("type")
|
||||
)
|
||||
short_desc = models.CharField(
|
||||
null=True, max_length=100, blank=True, verbose_name=_("Short Description")
|
||||
)
|
||||
location = models.TextField(blank=True, verbose_name=_("location"))
|
||||
map_location = PlainLocationField(blank=True, based_field=location, zoom=7, verbose_name=_("Location on map"))
|
||||
map_location = PlainLocationField(
|
||||
blank=True, based_field=location, zoom=7, verbose_name=_("Location on map")
|
||||
)
|
||||
desc = models.TextField(blank=True, verbose_name=_("description"))
|
||||
|
||||
date = models.DateField(verbose_name=_("date"))
|
||||
time = models.TimeField(null=True, blank=True, verbose_name=_("time"))
|
||||
meeting_time = models.TimeField(null=True, blank=True, verbose_name=_("meeting_time"))
|
||||
meeting_time = models.TimeField(
|
||||
null=True, blank=True, verbose_name=_("meeting_time")
|
||||
)
|
||||
|
||||
end_date = models.DateField(null=True, blank=True, verbose_name=_("End Date"))
|
||||
|
||||
participants = models.ManyToManyField(User, through='EventParticipation', verbose_name=_("participants"))
|
||||
participants = models.ManyToManyField(
|
||||
User, through="EventParticipation", verbose_name=_("participants")
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
class Meta:
|
||||
ordering = ["date", "time"]
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Call the "real" save() method
|
||||
super(Event, self).save(*args, **kwargs)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# Create a "Don't Know" participation for each Musician
|
||||
# Create a "Don't Know" participation for each Musician
|
||||
for u in User.objects.all():
|
||||
if not u in self.participants.all():
|
||||
EventParticipation.objects.create(event=self, user=u, status='-', comment='')
|
||||
if u not in self.participants.all():
|
||||
EventParticipation.objects.create(
|
||||
event=self, user=u, status="-", comment=""
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
@@ -64,7 +80,7 @@ class Event(models.Model):
|
||||
|
||||
@property
|
||||
def displaydatetime(self):
|
||||
if not self.displaytime == None:
|
||||
if self.displaytime is not None:
|
||||
return datetime.combine(self.date, self.displaytime)
|
||||
else:
|
||||
return datetime.combine(self.date, datetime.min.time())
|
||||
@@ -72,14 +88,18 @@ class Event(models.Model):
|
||||
@staticmethod
|
||||
def getNextEvent(eventType="", includePreviousFromToday=True):
|
||||
"""Return the next event, of the given type. If type is the empty string the next event is returned
|
||||
regardless of its type.
|
||||
if includePreviousFromToday the nextEvent returned could also have been today with a startime < now """
|
||||
regardless of its type.
|
||||
if includePreviousFromToday the nextEvent returned could also have been today with a startime < now"""
|
||||
|
||||
if includePreviousFromToday:
|
||||
if eventType == "":
|
||||
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by('date')[:1]
|
||||
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by(
|
||||
"date"
|
||||
)[:1]
|
||||
else:
|
||||
nextEvents = Event.objects.filter(date__gte=datetime.now(), type=eventType).order_by('date')[:1]
|
||||
nextEvents = Event.objects.filter(
|
||||
date__gte=datetime.now(), type=eventType
|
||||
).order_by("date")[:1]
|
||||
|
||||
if len(nextEvents) == 0:
|
||||
raise NoNextEventException()
|
||||
@@ -89,11 +109,13 @@ class Event(models.Model):
|
||||
maximalNumberOfEventsOnSameDay = 4
|
||||
nextEvents = []
|
||||
if eventType == "":
|
||||
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by('date')[
|
||||
:maximalNumberOfEventsOnSameDay]
|
||||
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by(
|
||||
"date"
|
||||
)[:maximalNumberOfEventsOnSameDay]
|
||||
else:
|
||||
nextEvents = Event.objects.filter(date__gte=datetime.now(), type=eventType).order_by('date')[
|
||||
:maximalNumberOfEventsOnSameDay]
|
||||
nextEvents = Event.objects.filter(
|
||||
date__gte=datetime.now(), type=eventType
|
||||
).order_by("date")[:maximalNumberOfEventsOnSameDay]
|
||||
|
||||
if len(nextEvents) == 0:
|
||||
raise NoNextEventException()
|
||||
@@ -112,28 +134,38 @@ class Event(models.Model):
|
||||
|
||||
|
||||
class EventParticipation(models.Model):
|
||||
OPTIONS = (('?', _('?')),
|
||||
('Yes', _('Yes')),
|
||||
('No', _('No')),
|
||||
('-', _('-'))
|
||||
)
|
||||
OPTIONS = (
|
||||
("?", _("?")),
|
||||
("Yes", _("Yes")),
|
||||
("No", _("No")),
|
||||
("-", _("-")),
|
||||
)
|
||||
|
||||
event = models.ForeignKey(Event, verbose_name=_("event"), on_delete=models.PROTECT)
|
||||
user = models.ForeignKey(User, verbose_name=_("user"), on_delete=models.PROTECT)
|
||||
status = models.CharField(max_length=3, choices=OPTIONS, default='?', verbose_name=_("status"))
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, verbose_name=_("event"))
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_("user"))
|
||||
status = models.CharField(
|
||||
max_length=3, choices=OPTIONS, default="?", verbose_name=_("status")
|
||||
)
|
||||
comment = models.CharField(max_length=64, blank=True, verbose_name=_("comment"))
|
||||
|
||||
class Meta:
|
||||
unique_together = ("event", "user")
|
||||
permissions = (
|
||||
("admin", _("Admin")),
|
||||
("member", _("Member")),
|
||||
)
|
||||
|
||||
def get_username(self):
|
||||
return self.user.username
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
prev = EventParticipation.objects.filter(event=self.event, user=self.user)
|
||||
if len(prev) == 0:
|
||||
super(EventParticipation, self).save(*args, **kwargs)
|
||||
super().save(*args, **kwargs)
|
||||
else:
|
||||
prev = prev[0]
|
||||
if prev.status != self.status or prev.comment != self.comment:
|
||||
super(EventParticipation, self).save(*args, **kwargs)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def hasUserSetParticipationForAllEvents(user):
|
||||
@@ -142,7 +174,7 @@ class EventParticipation(models.Model):
|
||||
|
||||
futurePart = EventParticipation.objects.filter(event__date__gte=datetime.now())
|
||||
|
||||
notYetEntered = futurePart.filter(user=user).filter(status='-')
|
||||
notYetEntered = futurePart.filter(user=user).filter(status="-")
|
||||
if len(notYetEntered) > 0:
|
||||
return False
|
||||
else:
|
||||
@@ -150,30 +182,27 @@ class EventParticipation(models.Model):
|
||||
|
||||
@staticmethod
|
||||
def isMember(user):
|
||||
return user.has_perm('eventplanner.member')
|
||||
return user.has_perm("eventplanner.member")
|
||||
|
||||
@staticmethod
|
||||
def isAdmin(user):
|
||||
return user.has_perm('eventplanner.admin')
|
||||
return user.has_perm("eventplanner.admin")
|
||||
|
||||
@staticmethod
|
||||
def members():
|
||||
perm = Permission.objects.get(codename='member')
|
||||
f = User.objects.filter(Q(groups__permissions=perm) | Q(user_permissions=perm)).distinct()
|
||||
return f.order_by('musician__position')
|
||||
perm = Permission.objects.get(codename="member")
|
||||
f = User.objects.filter(
|
||||
Q(groups__permissions=perm) | Q(user_permissions=perm)
|
||||
).distinct()
|
||||
return f.order_by("musician__position")
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(user, event):
|
||||
try:
|
||||
result = EventParticipation.objects.get(event=event, user=user)
|
||||
except EventParticipation.DoesNotExist:
|
||||
result = EventParticipation.objects.create(event=event, user=user, status='-', comment='')
|
||||
result = EventParticipation.objects.create(
|
||||
event=event, user=user, status="-", comment=""
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
class Meta:
|
||||
unique_together = ("event", "user")
|
||||
permissions = (
|
||||
("admin", _("Admin")),
|
||||
("member", _("Member")),
|
||||
)
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
from rest_framework import serializers
|
||||
from .models import EventParticipation, Event
|
||||
|
||||
from .models import Event, EventParticipation
|
||||
|
||||
|
||||
class ParticipationSerializer(serializers.ModelSerializer):
|
||||
event = serializers.PrimaryKeyRelatedField(many=False, read_only=False, queryset=Event.objects.all())
|
||||
user = serializers.Field(source='get_username')
|
||||
status = serializers.CharField(source='status', required=False)
|
||||
|
||||
def get_identity(self, data):
|
||||
""" This hook is required for bulk update. """
|
||||
try:
|
||||
return data.get('event', None), data.get('user')
|
||||
except AttributeError:
|
||||
return None
|
||||
event = serializers.PrimaryKeyRelatedField(queryset=Event.objects.all())
|
||||
user = serializers.CharField(source="get_username", read_only=True)
|
||||
status = serializers.CharField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = EventParticipation
|
||||
fields = ('event', 'user', 'status', 'comment')
|
||||
fields = ("event", "user", "status", "comment")
|
||||
|
||||
def create(self, validated_data):
|
||||
# Remove the get_username source field as it's read-only
|
||||
validated_data.pop("get_username", None)
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
instance.status = validated_data.get("status", instance.status)
|
||||
instance.comment = validated_data.get("comment", instance.comment)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
@@ -1,50 +1,58 @@
|
||||
from datetime import datetime
|
||||
from .models import Event, EventParticipation, NoNextEventException
|
||||
|
||||
from musicians.models import Musician
|
||||
|
||||
from .models import Event, EventParticipation, NoNextEventException
|
||||
|
||||
|
||||
def addEventCountdownForNextEventToContext(context, username, eventType=""):
|
||||
"""Returns an object that has to be added to the render context on the page where the countdown
|
||||
should be displayed . The username is required to also supply participation information."""
|
||||
"""Returns an object that has to be added to the render context on the page where the countdown
|
||||
should be displayed. The username is required to also supply participation information."""
|
||||
|
||||
try:
|
||||
nextEvent = Event.getNextEvent(eventType, False)
|
||||
except NoNextEventException:
|
||||
return
|
||||
|
||||
countdown = dict()
|
||||
countdown = {}
|
||||
|
||||
if EventParticipation.isMember(username):
|
||||
part = EventParticipation.objects.filter(user=username).filter(event=nextEvent)
|
||||
countdown['participation'] = part[0].status
|
||||
if part.exists():
|
||||
countdown["participation"] = part[0].status
|
||||
|
||||
eventTime = nextEvent.displaydatetime
|
||||
countdown['event'] = nextEvent
|
||||
countdown['epoch'] = int((eventTime - datetime.now()).total_seconds() * 1000)
|
||||
countdown["event"] = nextEvent
|
||||
countdown["epoch"] = int((eventTime - datetime.now()).total_seconds() * 1000)
|
||||
|
||||
context["countdown"] = countdown
|
||||
|
||||
|
||||
def addEventRouteForNextEventToContext(context, username, eventType=""):
|
||||
"""Returns an object that has to be added to the render context on the page where the route
|
||||
should be displayed . The starting address of the route will be the home of the specified user"""
|
||||
"""Returns an object that has to be added to the render context on the page where the route
|
||||
should be displayed. The starting address of the route will be the home of the specified user"""
|
||||
|
||||
try:
|
||||
nextEvent = Event.getNextEvent(eventType, True)
|
||||
except NoNextEventException:
|
||||
return
|
||||
|
||||
routeInfo = dict()
|
||||
routeInfo = {}
|
||||
|
||||
routeInfo['event'] = nextEvent
|
||||
routeInfo["event"] = nextEvent
|
||||
|
||||
musician = Musician.objects.get(user=username);
|
||||
routeInfo['origin'] = musician.street + ", " + str(musician.zip_code) + " " + musician.city
|
||||
try:
|
||||
musician = Musician.objects.get(user=username)
|
||||
routeInfo["origin"] = (
|
||||
musician.street + ", " + str(musician.zip_code) + " " + musician.city
|
||||
)
|
||||
except Musician.DoesNotExist:
|
||||
routeInfo["origin"] = ""
|
||||
|
||||
if nextEvent.map_location:
|
||||
# map_location has format "lat,longitute,zoomlevel"
|
||||
routeInfo['destination'] = ",".join(nextEvent.map_location.split(",")[:2])
|
||||
# map_location has format "lat,longitude,zoomlevel"
|
||||
routeInfo["destination"] = ",".join(nextEvent.map_location.split(",")[:2])
|
||||
else:
|
||||
routeInfo['destination'] = nextEvent.location
|
||||
routeInfo["destination"] = nextEvent.location
|
||||
|
||||
context["route"] = routeInfo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% load i18n admin_static admin_modify %}
|
||||
{% load i18n static admin_modify %}
|
||||
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
|
||||
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
|
||||
{{ inline_admin_formset.formset.management_form }}
|
||||
@@ -26,7 +26,7 @@
|
||||
id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
||||
<td class="original">
|
||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||
|
||||
|
||||
</p>{% endif %}
|
||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||
{{ inline_admin_form.fk_field.field }}
|
||||
|
||||
@@ -1,79 +1,71 @@
|
||||
{% load sekizai_tags staticfiles %}
|
||||
|
||||
|
||||
|
||||
{% addtoblock "css" strip %}<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/lib/animate.css" media="screen, projection">{% endaddtoblock %}
|
||||
{% addtoblock "css" strip %}<link rel="stylesheet" href="{{STATIC_URL}}css/coming-soon.css" type="text/css" media="screen" />{% endaddtoblock %}
|
||||
|
||||
{% if countdown %}
|
||||
|
||||
|
||||
|
||||
{% addtoblock "js" %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
function callback(event) {
|
||||
$this = $(this);
|
||||
$this.find('span#'+event.type).html(event.value);
|
||||
switch(event.type) {
|
||||
case "seconds":
|
||||
case "minutes":
|
||||
case "hours":
|
||||
case "days":
|
||||
case "weeks":
|
||||
case "daysLeft":
|
||||
case "finished":
|
||||
}
|
||||
}
|
||||
$('div#clock').countdown(new Date().valueOf() + {{ countdown.epoch }} , callback);
|
||||
});
|
||||
</script>
|
||||
{% load sekizai_tags static %} {% addtoblock "css" strip %}<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{{STATIC_URL}}/css/lib/animate.css"
|
||||
media="screen, projection"
|
||||
/>{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||
rel="stylesheet"
|
||||
href="{{STATIC_URL}}/css/coming-soon.css"
|
||||
type="text/css"
|
||||
media="screen"
|
||||
/>{% endaddtoblock %} {% if countdown %} {% addtoblock "js" %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
function callback(event) {
|
||||
$this = $(this);
|
||||
$this.find('span#'+event.type).html(event.value);
|
||||
switch(event.type) {
|
||||
case "seconds":
|
||||
case "minutes":
|
||||
case "hours":
|
||||
case "days":
|
||||
case "weeks":
|
||||
case "daysLeft":
|
||||
case "finished":
|
||||
}
|
||||
}
|
||||
$('div#clock').countdown(new Date().valueOf() + {{ countdown.epoch }} , callback);
|
||||
});
|
||||
</script>
|
||||
{% endaddtoblock %}
|
||||
|
||||
|
||||
|
||||
<div id="coming_soon">
|
||||
<div class="head">
|
||||
<div class="container">
|
||||
<div class="span6 text">
|
||||
<h4>Der nächste Termin:</h4>
|
||||
<p>
|
||||
{{countdown.event.title}} am {{countdown.event.date | date:"D, d.m.y" }}
|
||||
{% if coundown.event.displaytime %} um {{countdown.event.displaytime | time:"H:i" }} Uhr {% endif %}
|
||||
{% if coundown.event.location %} in <em>{{countdown.event.location}} </em> {% endif %}
|
||||
|
||||
<br/>
|
||||
{% if 'participation' in countdown %}
|
||||
{% if countdown.participation == "?" %}
|
||||
Du hast dich noch nicht für diesen Termin eingetragen!
|
||||
{% elif countdown.participation == "Yes" %}
|
||||
Du hast für diesen Termin zugesagt.
|
||||
{% elif countdown.participation == "No" %}
|
||||
Du hast für diesen Termin abgesagt.
|
||||
{%endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="span6 text">
|
||||
<h4>Der nächste Termin:</h4>
|
||||
<p>
|
||||
{{countdown.event.title}} am {{countdown.event.date | date:"D, d.m.y" }} {% if coundown.event.displaytime %} um
|
||||
{{countdown.event.displaytime | time:"H:i" }} Uhr {% endif %} {% if coundown.event.location %} in
|
||||
<em>{{countdown.event.location}} </em> {% endif %}
|
||||
|
||||
<br />
|
||||
{% if 'participation' in countdown %} {% if countdown.participation == "?"%} Du hast dich noch nicht
|
||||
für diesen Termin eingetragen! {% elif countdown.participation == "Yes"%} Du hast für diesen
|
||||
Termin zugesagt. {% elif countdown.participation == "No" %}
|
||||
Du hast für diesen Termin abgesagt. {%endif %} {% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="span6 count" id="clock">
|
||||
<div class="box">
|
||||
<div class="circle"> <span id="days"></span> </div>
|
||||
<div class="circle"><span id="days"></span></div>
|
||||
<p>Tage</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="circle"> <span id="hours"></span> </div>
|
||||
<div class="circle"><span id="hours"></span></div>
|
||||
<p>Stunden</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="circle"> <span id="minutes"></span> </div>
|
||||
<div class="circle"><span id="minutes"></span></div>
|
||||
<p>Minuten</p>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="circle"> <span id="seconds"></span> </div>
|
||||
<div class="circle"><span id="seconds"></span></div>
|
||||
<p>Sekunden</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
@@ -1,138 +1,131 @@
|
||||
{% extends "website/base.html" %}
|
||||
|
||||
{% load sekizai_tags staticfiles %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% extends "website/base.html" %} {% load sekizai_tags static %} {% load crispy_forms_tags %} {% block content %} {{ form.media }}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<h3>Termin bearbeiten</h3>
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<h3>Termin bearbeiten</h3>
|
||||
|
||||
{% crispy form %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% crispy form %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Datepicker -->
|
||||
|
||||
{% addtoblock "css" strip %}
|
||||
<link rel="stylesheet" href="{{STATIC_URL}}css/jquery-ui-1.12.1.min.css" type="text/css" media="screen" />
|
||||
{% endaddtoblock %}
|
||||
{% addtoblock "css" strip %}<link
|
||||
rel="stylesheet"
|
||||
href="{{STATIC_URL}}/css/datepicker.css"
|
||||
type="text/css"
|
||||
media="screen"
|
||||
/>
|
||||
{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||
rel="stylesheet"
|
||||
href="{{STATIC_URL}}/css/timepicker.css"
|
||||
type="text/css"
|
||||
media="screen"
|
||||
/>
|
||||
{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||
rel="stylesheet"
|
||||
href="{{STATIC_URL}}/css/jquery-ui-1.8.21.custom.css"
|
||||
type="text/css"
|
||||
media="screen"
|
||||
/>
|
||||
{% endaddtoblock %} {% addtoblock "js" %}
|
||||
<script src="{{STATIC_URL}}/js/bootstrap-timepicker.js"></script>
|
||||
<script src="{{STATIC_URL}}/js/bootstrap-datepicker.js"></script>
|
||||
<script src="{{STATIC_URL}}/js/bootstrap-datepicker.de.js"></script>
|
||||
|
||||
|
||||
{% addtoblock "js" %}
|
||||
<script src="{{STATIC_URL}}js/jquery-ui-1.12.1.min.js"></script>
|
||||
<script src="{{STATIC_URL}}js/bootstrap-datepicker.de.js"></script>
|
||||
<script>
|
||||
|
||||
$(document).ready(function(){
|
||||
$(document).ready(function(){
|
||||
|
||||
$.datepicker.setDefaults(
|
||||
$.extend(
|
||||
{'dateFormat':'dd-mm-yy'},
|
||||
$.datepicker.regional['de']
|
||||
)
|
||||
);
|
||||
$('.dateinput').datepicker({
|
||||
format: "dd.mm.yyyy",
|
||||
weekStart: 1,
|
||||
todayBtn: "linked",
|
||||
language: "de",
|
||||
todayHighlight: true,
|
||||
startDate: "{% now "SHORT_DATE_FORMAT" %}",
|
||||
});
|
||||
|
||||
$('.dateinput').datepicker();
|
||||
$('.timeinput').addClass('input-small').wrap('<div class="input-append bootstrap-timepicker">')
|
||||
$(".input-append").append( '<span class="add-on"><i class="icon-time"></i></span>' )
|
||||
|
||||
/*
|
||||
$('.timeinput').addClass('input-small').wrap('<div class="input-append bootstrap-timepicker">')
|
||||
$(".input-append").append( '<span class="add-on"><i class="icon-time"></i></span>' )
|
||||
|
||||
|
||||
$('.timeinput').timepicker({
|
||||
minuteStep: 15,
|
||||
showMeridian: false,
|
||||
defaultTime: false
|
||||
});
|
||||
*/
|
||||
|
||||
$('form').submit(function() {
|
||||
if ( $("#id_type").val() != "Option" && $("#id_location").val().trim() == "" ) {
|
||||
alert("Bitte Ort angeben");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
});
|
||||
|
||||
function onTypeChange( val )
|
||||
{
|
||||
if ( val == "Reh") {
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_map_location").hide();
|
||||
$("#div_id_end_date").hide();
|
||||
$("#div_id_location").show();
|
||||
$('.timeinput').timepicker({
|
||||
minuteStep: 15,
|
||||
showMeridian: false,
|
||||
defaultTime: false
|
||||
});
|
||||
|
||||
if ( ! $("#id_time").val() ) {
|
||||
$("#id_time").val("19:00");
|
||||
}
|
||||
if ( ! $("#id_location").val() ) {
|
||||
$("#id_location").val("Rohr");
|
||||
}
|
||||
}
|
||||
else if ( val == "Conc" ) {
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").show();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").hide();
|
||||
}
|
||||
else if ( val == "Party") {
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").hide();
|
||||
}
|
||||
else if ( val == "Travel") {
|
||||
$("#div_id_time").hide();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").show();
|
||||
}
|
||||
else if ( val == "Option" ) {
|
||||
$("#div_id_time").hide();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_location").hide();
|
||||
$("#div_id_map_location").hide();
|
||||
$("#div_id_end_date").show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").show();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").show();
|
||||
}
|
||||
}
|
||||
|
||||
$("#id_type").change( function() {
|
||||
onTypeChange( $(this).val() );
|
||||
} );
|
||||
onTypeChange( $("#id_type").val() );
|
||||
|
||||
} );
|
||||
$('form').submit(function() {
|
||||
if ( $("#id_type").val() != "Option" && $("#id_location").val().trim() == "" ) {
|
||||
alert("Bitte Ort angeben");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
});
|
||||
|
||||
function onTypeChange( val )
|
||||
{
|
||||
if ( val == "Reh") {
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_map_location").hide();
|
||||
$("#div_id_end_date").hide();
|
||||
$("#div_id_location").show();
|
||||
|
||||
if ( ! $("#id_time").val() ) {
|
||||
$("#id_time").val("19:00");
|
||||
}
|
||||
if ( ! $("#id_location").val() ) {
|
||||
$("#id_location").val("Rohr");
|
||||
}
|
||||
}
|
||||
else if ( val == "Conc" ) {
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").show();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").hide();
|
||||
}
|
||||
else if ( val == "Party") {
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").hide();
|
||||
}
|
||||
else if ( val == "Travel") {
|
||||
$("#div_id_time").hide();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").show();
|
||||
}
|
||||
else if ( val == "Option" ) {
|
||||
$("#div_id_time").hide();
|
||||
$("#div_id_meeting_time").hide();
|
||||
$("#div_id_location").hide();
|
||||
$("#div_id_map_location").hide();
|
||||
$("#div_id_end_date").show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#div_id_time").show();
|
||||
$("#div_id_meeting_time").show();
|
||||
$("#div_id_location").show();
|
||||
$("#div_id_map_location").show();
|
||||
$("#div_id_end_date").show();
|
||||
}
|
||||
}
|
||||
|
||||
$("#id_type").change( function() {
|
||||
onTypeChange( $(this).val() );
|
||||
} );
|
||||
onTypeChange( $("#id_type").val() );
|
||||
|
||||
} );
|
||||
</script>
|
||||
{% endaddtoblock %}
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% endaddtoblock %} {% endblock %}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{% extends "website/base.html" %}
|
||||
|
||||
{% load sekizai_tags staticfiles %}
|
||||
{% load sekizai_tags static %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
{% addtoblock "css" %}
|
||||
<style>
|
||||
/*
|
||||
@@ -13,49 +13,49 @@
|
||||
#eventTable th:nth-child(7) {display: none;}
|
||||
}*/
|
||||
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
|
||||
td input[type="text"]{
|
||||
width: 130px !important;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 760px)
|
||||
{
|
||||
/* Force table to not be like tables anymore */
|
||||
table, thead, tbody, th, td, tr {
|
||||
display: block;
|
||||
table, thead, tbody, th, td, tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
/* Hide table headers (but not display: none;, for accessibility) */
|
||||
thead tr {
|
||||
thead tr {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
|
||||
tr { border: 1px solid #ccc; }
|
||||
|
||||
td {
|
||||
|
||||
td {
|
||||
/* Behave like a "row" */
|
||||
border: none;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
position: relative;
|
||||
padding-left: 50% !important;
|
||||
padding-right: 1px !important;
|
||||
border: none;
|
||||
border-top: 0px solid rgb(221, 221, 221) !important;
|
||||
border: none;
|
||||
border-top: 0px solid rgb(221, 221, 221) !important;
|
||||
}
|
||||
td:before {
|
||||
td:before {
|
||||
/* Now like a table header */
|
||||
position: absolute;
|
||||
/* Top/left values mimic padding */
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
width: 45%;
|
||||
padding-right: 10px;
|
||||
width: 45%;
|
||||
padding-right: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td.empty {
|
||||
@@ -64,7 +64,7 @@
|
||||
td input[type="text"]{
|
||||
width: 105px !important;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Label the data
|
||||
*/
|
||||
@@ -76,26 +76,26 @@
|
||||
td:nth-of-type(6):before { content: "Status"; }
|
||||
td:nth-of-type(7):before { content: "Kommentar"; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
{% endaddtoblock %}
|
||||
|
||||
{% endaddtoblock %}
|
||||
|
||||
|
||||
{% addtoblock "js" %}
|
||||
<script>
|
||||
|
||||
|
||||
function setEventButtonStatus( button, status ) {
|
||||
others = button.siblings();
|
||||
all = button.add( button.siblings() );
|
||||
|
||||
|
||||
all.removeClass("btn-danger").
|
||||
removeClass("btn-warning").
|
||||
removeClass("btn-success").
|
||||
removeClass("btn-info");
|
||||
|
||||
|
||||
others.addClass("btn-info");
|
||||
|
||||
|
||||
if ( status === "Yes" ) {
|
||||
button.addClass("btn-success");
|
||||
}
|
||||
@@ -106,63 +106,63 @@
|
||||
button.addClass("btn-warning");
|
||||
}
|
||||
}
|
||||
|
||||
function putStatus( button, status )
|
||||
|
||||
function putStatus( button, status )
|
||||
{
|
||||
$("#saving").html("Speichere..");
|
||||
|
||||
|
||||
p = button.parent();
|
||||
putObject = [ { "event": p.data("event-id"),
|
||||
putObject = [ { "event": p.data("event-id"),
|
||||
"user": p.data("username"),
|
||||
"status": status } ];
|
||||
|
||||
|
||||
request = $.ajax( {
|
||||
type: "PUT",
|
||||
url: "{% url 'event_api' %}",
|
||||
url: "{% url 'eventplanner:event_api' %}",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(putObject),
|
||||
success: function() { $("#saving").html("Ok"); },
|
||||
error: function(jqXHR, text, errorText) { console.log("Ajax failed " + errorText + JSON.stringify(putObject) ); }
|
||||
});
|
||||
|
||||
|
||||
setEventButtonStatus( button, status );
|
||||
}
|
||||
|
||||
|
||||
$(function(){
|
||||
|
||||
|
||||
$(".event-comment").bindWithDelay("keyup", function() {
|
||||
$("#saving").html("Speichere..");
|
||||
|
||||
putObject = [ { "event": $(this).data("event-id"),
|
||||
putObject = [ { "event": $(this).data("event-id"),
|
||||
"user": $(this).data("username"),
|
||||
"comment": $(this).val() } ];
|
||||
|
||||
|
||||
$.ajax( {
|
||||
type: "PUT",
|
||||
url: "{% url 'event_api' %}",
|
||||
url: "{% url 'eventplanner:event_api' %}",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(putObject),
|
||||
success: function() { $("#saving").html("Ok"); }
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}, 800 );
|
||||
|
||||
|
||||
|
||||
|
||||
$(".event-status-yes").click(function () {
|
||||
putStatus( $(this), "Yes" );
|
||||
});
|
||||
|
||||
|
||||
$(".event-status-no").click(function () {
|
||||
putStatus( $(this), "No" );
|
||||
putStatus( $(this), "No" );
|
||||
});
|
||||
|
||||
$(".event-status-dontknow").click(function () {
|
||||
putStatus( $(this), "?" );
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endaddtoblock %}
|
||||
|
||||
<div class="navbar">
|
||||
@@ -176,14 +176,22 @@
|
||||
|
||||
|
||||
<div class="container">
|
||||
<div class="row-fluid eventTable">
|
||||
<div class="row-fluid eventTable">
|
||||
<h2>Termine</h2>
|
||||
|
||||
<div class="span12">
|
||||
|
||||
<div class="alert alert-info">
|
||||
Es gibt jetzt einen vierten Zustand "nicht eingetragen", angezeigt durch nur
|
||||
blaue Buttons.<br>
|
||||
|
||||
"?" bedeutet jetzt: Ich habe mich eingetragen weiss aber noch nicht ob ich komme.
|
||||
Bitte nur selten benutzen!
|
||||
</div>
|
||||
|
||||
<div class="box-content">
|
||||
<table id="eventTable" class="table table-striped">
|
||||
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th id='eventTitle'>Termin</th>
|
||||
@@ -194,10 +202,10 @@
|
||||
<th id='status' >Status ändern</th>
|
||||
<th id='comment' >Kommentar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% for event in events %}
|
||||
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
{% if not perms.eventplanner.change_event %}
|
||||
<td class="center">{{ event.title }}</td>
|
||||
@@ -214,43 +222,43 @@
|
||||
{{event.location}}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
|
||||
<td class="center">
|
||||
<div class="btn-group event-status-select" data-event-id="{{event.pk}}" data-username="{{user.username}}" >
|
||||
<button class="btn event-status-yes {% if event.participation.status == "Yes" %} btn-success {% else %} btn-info {% endif %}">
|
||||
<i class="icon-ok-sign icon-white"></i>
|
||||
<i class="icon-ok-sign icon-white"></i>
|
||||
</button>
|
||||
<button class="btn event-status-dontknow {% if event.participation.status == "?" %} btn-warning {% else %} btn-info {% endif %}">
|
||||
<i class="icon-question-sign icon-white "></i>
|
||||
<i class="icon-question-sign icon-white "></i>
|
||||
</button>
|
||||
<button class="btn event-status-no {% if event.participation.status == "No" %} btn-danger {% else %} btn-info {% endif %}">
|
||||
<i class="icon-remove-sign icon-white"></i>
|
||||
<i class="icon-remove-sign icon-white"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
<div class="input-append">
|
||||
<input size="16" type="text" data-event-id="{{event.pk}}" data-username="{{user.username}}" class="event-comment" value="{{ event.participation.comment }}" >
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
</div><!--/span-->
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<p>
|
||||
<em>Änderungen werden automatisch gespeichert: </em> <em id="saving">Ok</em>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{% extends "website/base.html" %}
|
||||
|
||||
{% load sekizai_tags staticfiles %}
|
||||
{% load sekizai_tags static %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% addtoblock "css" %}
|
||||
<style>
|
||||
|
||||
|
||||
.infoColumn {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
button.eventButton {
|
||||
width: 32px;
|
||||
font-size: 10px;
|
||||
@@ -21,11 +21,11 @@
|
||||
width: 18px;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
.eventButton i {
|
||||
margin-right:2px;
|
||||
}
|
||||
|
||||
|
||||
.with_comment {
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
@@ -38,10 +38,10 @@
|
||||
td a {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
{% endaddtoblock %}
|
||||
|
||||
|
||||
{% addtoblock "js" %}
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
@@ -59,19 +59,19 @@
|
||||
$(this).removeClass("btn-danger")
|
||||
.removeClass("btn-warning")
|
||||
.removeClass("btn-success");
|
||||
|
||||
if ( $(this).data('status') == "Yes" )
|
||||
|
||||
if ( $(this).data('status') == "Yes" )
|
||||
{
|
||||
$(this).data( "status", "?" )
|
||||
.addClass("btn-warning");
|
||||
$(this).children("span.text").html("?");
|
||||
} else if ( $(this).data('status') == "?" )
|
||||
} else if ( $(this).data('status') == "?" )
|
||||
{
|
||||
$(this).data( "status", "No" )
|
||||
.addClass("btn-danger");
|
||||
$(this).children("span.text").html("Nein");
|
||||
|
||||
} else if ( $(this).data('status') == "No" )
|
||||
} else if ( $(this).data('status') == "No" )
|
||||
{
|
||||
$(this).data( "status", "-" );
|
||||
$(this).children("span.text").html("-");
|
||||
@@ -81,45 +81,45 @@
|
||||
.addClass("btn-success");
|
||||
$(this).children("span.text").html("Ja");
|
||||
}
|
||||
|
||||
$('#saveButton').removeAttr('disabled');
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('#saveButton').removeAttr('disabled');
|
||||
});
|
||||
|
||||
|
||||
|
||||
$("#saveButton").click( function(e) {
|
||||
e.preventDefault();
|
||||
arr = [];
|
||||
|
||||
|
||||
$('.userEventTableData').each( function() {
|
||||
|
||||
|
||||
dataObject = {
|
||||
"event" : $(this).data("event"),
|
||||
"user" : $(this).data("username"),
|
||||
"status" : $(this).children("button").data("status")
|
||||
};
|
||||
|
||||
|
||||
arr.push(dataObject);
|
||||
});
|
||||
|
||||
$.ajax( {
|
||||
type: "PUT",
|
||||
url: "{% url 'event_api' %}",
|
||||
url: "{% url 'eventplanner:event_api' %}",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(arr)
|
||||
});
|
||||
$('#saveButton').attr('disabled','true');
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
$( ".deleteButton" ).click(function() {
|
||||
if (confirm('Termin wirklich löschen?')) {
|
||||
pk = $(this).data( "pk" )
|
||||
window.location = pk + "/delete"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endaddtoblock %}
|
||||
@@ -143,45 +143,52 @@
|
||||
|
||||
<h2>Termine</h2>
|
||||
|
||||
<div class="alert alert-info">
|
||||
Es gibt jetzt einen vierten Zustand "nicht eingetragen", angezeigt durch "-".<br>
|
||||
|
||||
"?" bedeutet jetzt: Ich habe mich eingetragen weiss aber noch nicht ob ich komme.
|
||||
Bitte nur selten benutzen!
|
||||
</div>
|
||||
|
||||
<div class="box-content">
|
||||
<table class="table table-striped">
|
||||
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
{% for name in usernames %}
|
||||
<th class='usernameHeader'> {{ name|capfirst }} </th>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
{% for event in events %}
|
||||
<tr class="eventRow">
|
||||
<td class="center" > <a href="{{ event.pk }}" > {{ event.title }} </a> </td>
|
||||
<td class="center">
|
||||
<div class="infoColumn" title="{% if event.time %} Uhrzeit: {{ event.time | time:"H:i" }} {% endif %}
|
||||
<div class="infoColumn" title="{% if event.time %} Uhrzeit: {{ event.time | time:"H:i" }} {% endif %}
|
||||
{% if event.meeting_time %} ( Treffpunkt: {{event.meeting_time | time:"H:i"}} ) {% endif %} ">
|
||||
|
||||
{% if event.location %} {{ event.location }} <br/> {% endif %}
|
||||
|
||||
{% if event.location %} {{ event.location }} <br/> {% endif %}
|
||||
{{ event.date | date:"D, d.m.y" }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
{% for p in event.participation %}
|
||||
|
||||
|
||||
{% if perms.eventplanner.admin %}
|
||||
|
||||
|
||||
<td class="center userEventTableData" data-username="{{p.user.username}}" data-event="{{event.pk}}">
|
||||
{% if p.status == "Yes" %}
|
||||
{% if p.status == "Yes" %}
|
||||
<button class="btn btn-mini btn-success eventButton" title="{{p.comment}}" data-status="{{p.status}}">
|
||||
<span class="text {% if p.comment %}with_comment{% endif %}">Ja</span>
|
||||
</button>
|
||||
{% elif p.status == "No" %}
|
||||
{% elif p.status == "No" %}
|
||||
<button class="btn btn-mini btn-danger eventButton" title="{{p.comment}}" data-status="{{p.status}}">
|
||||
<span class="text {% if p.comment %}with_comment{% endif %}">Nein</span>
|
||||
</button>
|
||||
@@ -194,14 +201,14 @@
|
||||
<span class="text {% if p.comment %}with_comment{% endif %}">-</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
{% else %}
|
||||
<td class="center userEventTableData" data-username="{{p.user.username}}" data-event="{{event.pk}}">
|
||||
{% if p.status == "Yes" %}
|
||||
{% if p.status == "Yes" %}
|
||||
<span class="badge badge-success eventButton" title="{{p.comment}}" data-status="{{p.status}}">
|
||||
<span class="text {% if p.comment %}with_comment{% endif %}">Ja</span>
|
||||
</span>
|
||||
{% elif p.status == "No" %}
|
||||
{% elif p.status == "No" %}
|
||||
<span class="badge badge-important eventButton" title="{{p.comment}}" data-status="{{p.status}}">
|
||||
<span class="text {% if p.comment %}with_comment{% endif %}">Nein</span>
|
||||
</span>
|
||||
@@ -215,9 +222,9 @@
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endfor %}
|
||||
|
||||
<td>
|
||||
@@ -225,20 +232,20 @@
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% if perms.eventplanner.admin %}
|
||||
<tr>
|
||||
<td class="center" colspan="80"> <a href="add">Termin hinzufügen...</a> </td>
|
||||
<tr>
|
||||
{% endif %}
|
||||
|
||||
|
||||
</form>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div><!--/span-->
|
||||
|
||||
|
||||
</div><!--/row-->
|
||||
|
||||
|
||||
@@ -249,7 +256,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,208 +1,215 @@
|
||||
{% comment %}
|
||||
Displays google map with directions to next conert
|
||||
|
||||
Context:
|
||||
Coordinates or textual adresses:
|
||||
{{route.origin}}
|
||||
{{route.destination}}
|
||||
Event object:
|
||||
{{route.event}}
|
||||
{% endcomment %}
|
||||
|
||||
|
||||
|
||||
|
||||
{% load sekizai_tags staticfiles %}
|
||||
|
||||
|
||||
{% if route %}
|
||||
|
||||
{% addtoblock "css" strip %}<link rel="stylesheet" href="{{STATIC_URL}}css/concert_route.css" type="text/css" media="screen" />{% endaddtoblock %}
|
||||
{% addtoblock "js" strip %}<script type="text/javascript" src="//maps.google.com/maps/api/js?key={{GOOGLE_MAPS_API_KEY}}&sensor=false&language=de"></script>{% endaddtoblock %}
|
||||
|
||||
|
||||
{% addtoblock "js" %}
|
||||
{% comment %} Displays google map with directions to next conert Context:
|
||||
Coordinates or textual adresses: {{route.origin}} {{route.destination}} Event
|
||||
object: {{route.event}} {% endcomment %} {% load sekizai_tags static %}
|
||||
{% if route %} {% addtoblock "css" strip %}<link
|
||||
rel="stylesheet"
|
||||
href="{{STATIC_URL}}/css/concert_route.css"
|
||||
type="text/css"
|
||||
media="screen"
|
||||
/>{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="//maps.google.com/maps/api/js?sensor=false&language=de"
|
||||
></script>
|
||||
{% endaddtoblock %} {% addtoblock "js" %}
|
||||
<script type="text/javascript">
|
||||
|
||||
function OpenWindowControl(controlDiv, map) {
|
||||
// Set CSS styles for the DIV containing the control
|
||||
// Setting padding to 5 px will offset the control
|
||||
// from the edge of the map
|
||||
controlDiv.style.paddingTop = '6px';
|
||||
|
||||
// Set CSS for the control border
|
||||
var controlUI = document.createElement('div');
|
||||
controlUI.style.backgroundColor = 'white';
|
||||
controlUI.style.borderStyle = 'solid';
|
||||
controlUI.style.borderWidth = '1px';
|
||||
controlUI.style.cursor = 'pointer';
|
||||
controlUI.style.textAlign = 'center';
|
||||
controlUI.title = 'Fenster mit Konzert Info anzeigen';
|
||||
controlDiv.appendChild(controlUI);
|
||||
|
||||
// Set CSS for the control interior
|
||||
var controlText = document.createElement('div');
|
||||
controlText.style.fontFamily = 'Arial,sans-serif';
|
||||
controlText.style.fontSize = '12px';
|
||||
controlText.style.paddingLeft = '4px';
|
||||
controlText.style.paddingRight = '4px';
|
||||
controlText.innerHTML = 'Konzert Info anzeigen';
|
||||
controlUI.appendChild(controlText);
|
||||
|
||||
google.maps.event.addDomListener(controlUI, 'click', function() {
|
||||
$("#map_box").show();
|
||||
});
|
||||
}
|
||||
function OpenWindowControl(controlDiv, map) {
|
||||
// Set CSS styles for the DIV containing the control
|
||||
// Setting padding to 5 px will offset the control
|
||||
// from the edge of the map
|
||||
controlDiv.style.paddingTop = '6px';
|
||||
|
||||
|
||||
function ShowTargetControl(controlDiv, map) {
|
||||
// Set CSS styles for the DIV containing the control
|
||||
// Setting padding to 5 px will offset the control
|
||||
// from the edge of the map
|
||||
controlDiv.style.paddingTop = '6px';
|
||||
controlDiv.style.paddingRight = '6px';
|
||||
// Set CSS for the control border
|
||||
var controlUI = document.createElement('div');
|
||||
controlUI.style.backgroundColor = 'white';
|
||||
controlUI.style.borderStyle = 'solid';
|
||||
controlUI.style.borderWidth = '1px';
|
||||
controlUI.style.cursor = 'pointer';
|
||||
controlUI.style.textAlign = 'center';
|
||||
controlUI.title = 'Fenster mit Konzert Info anzeigen';
|
||||
controlDiv.appendChild(controlUI);
|
||||
|
||||
// Set CSS for the control border
|
||||
var controlUI = document.createElement('div');
|
||||
controlUI.style.backgroundColor = 'white';
|
||||
controlUI.style.borderStyle = 'solid';
|
||||
controlUI.style.borderWidth = '1px';
|
||||
controlUI.style.cursor = 'pointer';
|
||||
controlUI.style.textAlign = 'center';
|
||||
controlUI.title = 'Zum Zielpunkt springen';
|
||||
controlDiv.appendChild(controlUI);
|
||||
|
||||
// Set CSS for the control interior
|
||||
var controlText = document.createElement('div');
|
||||
controlText.style.fontFamily = 'Arial,sans-serif';
|
||||
controlText.style.fontSize = '12px';
|
||||
controlText.style.paddingLeft = '4px';
|
||||
controlText.style.paddingRight = '4px';
|
||||
controlText.innerHTML = 'Konzertort anzeigen';
|
||||
controlUI.appendChild(controlText);
|
||||
|
||||
google.maps.event.addDomListener(controlUI, 'click', function()
|
||||
{
|
||||
{% if not route.event.map_location %}
|
||||
geocoder = new google.maps.Geocoder();
|
||||
geocoder.region = "de";
|
||||
geocoder.geocode( {"address": "{{ route.event.location }}" }, function(results, status) {
|
||||
if (status == google.maps.GeocoderStatus.OK) {
|
||||
map.setMapTypeId( google.maps.MapTypeId.HYBRID );
|
||||
map.setZoom( 15 );
|
||||
// Set CSS for the control interior
|
||||
var controlText = document.createElement('div');
|
||||
controlText.style.fontFamily = 'Arial,sans-serif';
|
||||
controlText.style.fontSize = '12px';
|
||||
controlText.style.paddingLeft = '4px';
|
||||
controlText.style.paddingRight = '4px';
|
||||
controlText.innerHTML = 'Konzert Info anzeigen';
|
||||
controlUI.appendChild(controlText);
|
||||
|
||||
map.setCenter( results[0].geometry.location );
|
||||
}
|
||||
});
|
||||
{% else %}
|
||||
var loc = new google.maps.LatLng( {{ route.event.map_location }} );
|
||||
map.setMapTypeId( google.maps.MapTypeId.HYBRID );
|
||||
map.setZoom( 20 );
|
||||
google.maps.event.addDomListener(controlUI, 'click', function() {
|
||||
$("#map_box").show();
|
||||
});
|
||||
}
|
||||
|
||||
map.setCenter( loc );
|
||||
|
||||
{% endif %}
|
||||
});
|
||||
}
|
||||
function ShowTargetControl(controlDiv, map) {
|
||||
// Set CSS styles for the DIV containing the control
|
||||
// Setting padding to 5 px will offset the control
|
||||
// from the edge of the map
|
||||
controlDiv.style.paddingTop = '6px';
|
||||
controlDiv.style.paddingRight = '6px';
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var m = $("#map")[0];
|
||||
// Set CSS for the control border
|
||||
var controlUI = document.createElement('div');
|
||||
controlUI.style.backgroundColor = 'white';
|
||||
controlUI.style.borderStyle = 'solid';
|
||||
controlUI.style.borderWidth = '1px';
|
||||
controlUI.style.cursor = 'pointer';
|
||||
controlUI.style.textAlign = 'center';
|
||||
controlUI.title = 'Zum Zielpunkt springen';
|
||||
controlDiv.appendChild(controlUI);
|
||||
|
||||
var myOptions = {
|
||||
zoom: 10,
|
||||
mapTypeId: google.maps.MapTypeId.ROAD,
|
||||
zoomControl: false,
|
||||
panControl: false,
|
||||
streetViewControl: false,
|
||||
scrollwheel: false
|
||||
}
|
||||
var directionsService = new google.maps.DirectionsService();
|
||||
var directionsDisplay = new google.maps.DirectionsRenderer();
|
||||
var map = new google.maps.Map(m, myOptions);
|
||||
|
||||
directionsDisplay.setMap( map );
|
||||
|
||||
var request = {
|
||||
origin: "{{route.origin}}",
|
||||
destination: "{{route.destination}}",
|
||||
travelMode: google.maps.DirectionsTravelMode.DRIVING
|
||||
}
|
||||
// Set CSS for the control interior
|
||||
var controlText = document.createElement('div');
|
||||
controlText.style.fontFamily = 'Arial,sans-serif';
|
||||
controlText.style.fontSize = '12px';
|
||||
controlText.style.paddingLeft = '4px';
|
||||
controlText.style.paddingRight = '4px';
|
||||
controlText.innerHTML = 'Konzertort anzeigen';
|
||||
controlUI.appendChild(controlText);
|
||||
|
||||
directionsService.route(request, function(response, status) {
|
||||
if (status == google.maps.DirectionsStatus.OK) {
|
||||
directionsDisplay.setDirections(response);
|
||||
|
||||
var leg = response.routes[0].legs[0];
|
||||
google.maps.event.addDomListener(controlUI, 'click', function()
|
||||
{
|
||||
{% if not route.event.map_location %}
|
||||
geocoder = new google.maps.Geocoder();
|
||||
geocoder.region = "de";
|
||||
geocoder.geocode( {"address": "{{ route.event.location }}" }, function(results, status) {
|
||||
if (status == google.maps.GeocoderStatus.OK) {
|
||||
map.setMapTypeId( google.maps.MapTypeId.HYBRID );
|
||||
map.setZoom( 15 );
|
||||
|
||||
$("#route_duration").html( leg.duration.text );
|
||||
$("#route_distance").html( leg.distance.text ) ;
|
||||
}
|
||||
});
|
||||
|
||||
map.setCenter( results[0].geometry.location );
|
||||
}
|
||||
});
|
||||
{% else %}
|
||||
var loc = new google.maps.LatLng( {{ route.event.map_location }} );
|
||||
map.setMapTypeId( google.maps.MapTypeId.HYBRID );
|
||||
map.setZoom( 20 );
|
||||
|
||||
var showInfoControlDiv = document.createElement('div');
|
||||
var showInfoControl = new OpenWindowControl(showInfoControlDiv, map);
|
||||
showInfoControlDiv.index = 1;
|
||||
map.controls[google.maps.ControlPosition.TOP_RIGHT].push( showInfoControlDiv );
|
||||
map.setCenter( loc );
|
||||
|
||||
var showTargetControlDiv = document.createElement('div');
|
||||
var showTargetControl = new ShowTargetControl(showTargetControlDiv, map);
|
||||
{% endif %}
|
||||
});
|
||||
}
|
||||
|
||||
showTargetControlDiv.index = 2;
|
||||
map.controls[ google.maps.ControlPosition.TOP_RIGHT ].push(showTargetControlDiv);
|
||||
|
||||
|
||||
|
||||
$("#map_box a").click( function() {
|
||||
$("#map_box").hide();
|
||||
map.setOptions( { scrollwheel: true } );
|
||||
});
|
||||
}
|
||||
);
|
||||
$(document).ready(function() {
|
||||
|
||||
var m = $("#map")[0];
|
||||
|
||||
var myOptions = {
|
||||
zoom: 10,
|
||||
mapTypeId: google.maps.MapTypeId.ROAD,
|
||||
zoomControl: false,
|
||||
panControl: false,
|
||||
streetViewControl: false,
|
||||
scrollwheel: false
|
||||
}
|
||||
var directionsService = new google.maps.DirectionsService();
|
||||
var directionsDisplay = new google.maps.DirectionsRenderer();
|
||||
var map = new google.maps.Map(m, myOptions);
|
||||
|
||||
directionsDisplay.setMap( map );
|
||||
|
||||
var request = {
|
||||
origin: "{{route.origin}}",
|
||||
destination: "{{route.destination}}",
|
||||
travelMode: google.maps.DirectionsTravelMode.DRIVING
|
||||
}
|
||||
|
||||
directionsService.route(request, function(response, status) {
|
||||
if (status == google.maps.DirectionsStatus.OK) {
|
||||
directionsDisplay.setDirections(response);
|
||||
|
||||
var leg = response.routes[0].legs[0];
|
||||
|
||||
$("#route_duration").html( leg.duration.text );
|
||||
$("#route_distance").html( leg.distance.text ) ;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var showInfoControlDiv = document.createElement('div');
|
||||
var showInfoControl = new OpenWindowControl(showInfoControlDiv, map);
|
||||
showInfoControlDiv.index = 1;
|
||||
map.controls[google.maps.ControlPosition.TOP_RIGHT].push( showInfoControlDiv );
|
||||
|
||||
var showTargetControlDiv = document.createElement('div');
|
||||
var showTargetControl = new ShowTargetControl(showTargetControlDiv, map);
|
||||
|
||||
showTargetControlDiv.index = 2;
|
||||
map.controls[ google.maps.ControlPosition.TOP_RIGHT ].push(showTargetControlDiv);
|
||||
|
||||
|
||||
|
||||
$("#map_box a").click( function() {
|
||||
$("#map_box").hide();
|
||||
map.setOptions( { scrollwheel: true } );
|
||||
});
|
||||
}
|
||||
);
|
||||
</script>
|
||||
{% endaddtoblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="concert_route">
|
||||
<div id="map"></div>
|
||||
<div id="map"></div>
|
||||
<div id="map_box" class="row map">
|
||||
<div class="container">
|
||||
<div id="route_info_box" class="span5 box_wrapp">
|
||||
<div class="box_cont">
|
||||
<div class="head">
|
||||
<h4>Nächstes Konzert</h4>
|
||||
|
||||
<!--
|
||||
<h4>Nächstes Konzert</h4>
|
||||
|
||||
<!--
|
||||
</div>
|
||||
Nächstes Konzert ist in <br> <em>{{route.event.location}}</em> <br> am {{route.event.date | date:"SHORT_DATE_FORMAT" }} um {{route.event.time | time:"H:i" }} Uhr <br/>
|
||||
{% if route.event.meeting_time %} Treffpunkt ist um {{ route.event.meeting_time | time:"H:i" }} Uhr <br/> {% endif %}
|
||||
|
||||
|
||||
<table>
|
||||
<tr> <td> Fahrzeit:</td> <td> <span id="route_duration"></span> </td> </tr>
|
||||
<tr> <td> Strecke: </td> <td> <span id="route_distance"></span> </td> </tr>
|
||||
</table>
|
||||
-->
|
||||
<table class="table table-striped table-condensed">
|
||||
<tr><td>Ort: </td> <td> {{route.event.location}} </td> </tr>
|
||||
<tr><td>Datum: </td> <td> {{route.event.date | date:"D, d.m.y" }} </td> </tr>
|
||||
<tr><td>Uhrzeit: </td> <td> {{route.event.time | time:"H:i" }} Uhr </td> </tr>
|
||||
{% if route.event.meeting_time %} <tr><td>Treffen um: </td> <td> {{route.event.meeting_time | time:"H:i" }} Uhr </td> </tr> {% endif %}
|
||||
<tr> <td> Fahrzeit:</td> <td> <span id="route_duration"></span> </td> </tr>
|
||||
<tr> <td> Strecke: </td> <td> <span id="route_distance"></span> </td> </tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<a class="btn" >Schliessen</a>
|
||||
<table class="table table-striped table-condensed">
|
||||
<tr>
|
||||
<td>Ort:</td>
|
||||
<td>{{route.event.location}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Datum:</td>
|
||||
<td>{{route.event.date | date:"D, d.m.y" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uhrzeit:</td>
|
||||
<td>{{route.event.time | time:"H:i" }} Uhr</td>
|
||||
</tr>
|
||||
{% if route.event.meeting_time %}
|
||||
<tr>
|
||||
<td>Treffen um:</td>
|
||||
<td>
|
||||
{{route.event.meeting_time | time:"H:i" }}
|
||||
Uhr
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>Fahrzeit:</td>
|
||||
<td><span id="route_duration"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Strecke:</td>
|
||||
<td><span id="route_distance"></span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a class="btn">Schliessen</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endif %}
|
||||
@@ -1,15 +1,21 @@
|
||||
from django.conf.urls import url
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from eventplanner.views import events_grid, eventplanning, event_api, EventUpdate, EventCreate, deleteEvent
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = "eventplanner"
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', eventplanning),
|
||||
url(r'^grid$', events_grid),
|
||||
url(r'^planning$', eventplanning),
|
||||
url(r'^(?P<pk>\d+)$', permission_required('eventplanner.change_event')(EventUpdate.as_view())),
|
||||
url(r'^add$', permission_required('eventplanner.add_event')(EventCreate.as_view())),
|
||||
url(r'^(?P<pk>\d+)/delete$', permission_required('eventplanner.delete_event')(deleteEvent)),
|
||||
url(r'^api/', event_api, name="event_api"),
|
||||
url(r'^api/(\w+)/$', event_api, name="event_api_per_user"),
|
||||
url(r'^api/(\w+)/(\d+)$', event_api, name="event_api_per_user_event"),
|
||||
path("", views.eventplanning, name="eventplanning"),
|
||||
path("grid/", views.events_grid, name="events_grid"),
|
||||
path("delete/<int:pk>/", views.deleteEvent, name="delete_event"),
|
||||
path("event/<int:pk>/", views.EventUpdate.as_view(), name="event_update"),
|
||||
path("event/create/", views.EventCreate.as_view(), name="event_create"),
|
||||
# API endpoints
|
||||
path("api/", views.event_api, name="event_api"),
|
||||
path("api/<str:username>/", views.event_api, name="event_api_user"),
|
||||
path(
|
||||
"api/<str:username>/<int:eventId>/",
|
||||
views.event_api,
|
||||
name="event_api_user_event",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpResponse
|
||||
from django.forms.models import ModelForm
|
||||
from django.forms import TextInput, TimeInput
|
||||
|
||||
from .models import Event, EventParticipation
|
||||
|
||||
from .serializers import ParticipationSerializer
|
||||
|
||||
import datetime
|
||||
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Submit
|
||||
from django.forms import TextInput
|
||||
from django.forms.models import ModelForm
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
|
||||
from location_field.widgets import LocationWidget
|
||||
|
||||
from .models import Event, EventParticipation
|
||||
from .serializers import ParticipationSerializer
|
||||
|
||||
# ---------------------------------------- API ---------------------------------------------------------
|
||||
|
||||
|
||||
@api_view(['GET', 'PUT'])
|
||||
@api_view(["GET", "PUT"])
|
||||
def event_api(request, username=None, eventId=None):
|
||||
try:
|
||||
participationQs = EventParticipation.objects.filter(event__date__gte=datetime.date.today())
|
||||
participationQs = EventParticipation.objects.filter(
|
||||
event__date__gte=datetime.date.today()
|
||||
)
|
||||
if username:
|
||||
participationQs = EventParticipation.objects.filter(user__username=username)
|
||||
if eventId:
|
||||
@@ -31,58 +32,70 @@ def event_api(request, username=None, eventId=None):
|
||||
except EventParticipation.DoesNotExist:
|
||||
return HttpResponse(status=404)
|
||||
|
||||
if request.method == 'GET':
|
||||
serializer = ParticipationSerializer(participationQs)
|
||||
if request.method == "GET":
|
||||
serializer = ParticipationSerializer(participationQs, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
elif request.method == 'PUT':
|
||||
serializer = ParticipationSerializer(participationQs, data=request.DATA, many=True)
|
||||
elif request.method == "PUT":
|
||||
serializer = ParticipationSerializer(data=request.data, many=True)
|
||||
if serializer.is_valid():
|
||||
for serializedObject in serializer.object:
|
||||
if not (EventParticipation.isMember(request.user) or EventParticipation.isAdmin(request.user)):
|
||||
for item in serializer.validated_data:
|
||||
event = item.get("event")
|
||||
user_obj = item.get("user")
|
||||
if not (
|
||||
EventParticipation.isMember(request.user)
|
||||
or EventParticipation.isAdmin(request.user)
|
||||
):
|
||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||
if serializedObject.user != request.user:
|
||||
if user_obj != request.user:
|
||||
if not EventParticipation.isAdmin(request.user):
|
||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
serializer.save()
|
||||
return Response(serializer.data)
|
||||
else:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
# ------------------------------------ Normal Views ----------------------------------------------------
|
||||
|
||||
|
||||
def eventplanning(request):
|
||||
"""
|
||||
View for a specific user, to edit his events
|
||||
View for a specific user, to edit his events
|
||||
"""
|
||||
# non-members see the grid - but cannot edit anything
|
||||
if not EventParticipation.isMember(request.user):
|
||||
return events_grid(request)
|
||||
|
||||
# All events in the future sorted by date
|
||||
all_future_events = list(Event.objects.filter(date__gte=datetime.date.today()).order_by('date'))
|
||||
all_future_events = list(
|
||||
Event.objects.filter(date__gte=datetime.date.today()).order_by("date")
|
||||
)
|
||||
|
||||
for e in all_future_events:
|
||||
e.participation = EventParticipation.get_or_create(event=e, user=request.user)
|
||||
|
||||
context = {'events': all_future_events}
|
||||
return render(request, 'eventplanner/eventplanning_view.html', context)
|
||||
context = {"events": all_future_events}
|
||||
return render(request, "eventplanner/eventplanning_view.html", context)
|
||||
|
||||
|
||||
def events_grid(request):
|
||||
usernames = [u.username for u in EventParticipation.members()]
|
||||
|
||||
all_future_events = list(Event.objects.filter(date__gte=datetime.date.today()).order_by('date'))
|
||||
all_future_events = list(
|
||||
Event.objects.filter(date__gte=datetime.date.today()).order_by("date")
|
||||
)
|
||||
|
||||
for e in all_future_events:
|
||||
e.participation = [EventParticipation.get_or_create(event=e, user=u) for u in EventParticipation.members()]
|
||||
e.participation = [
|
||||
EventParticipation.get_or_create(event=e, user=u)
|
||||
for u in EventParticipation.members()
|
||||
]
|
||||
|
||||
context = {'events': all_future_events,
|
||||
'usernames': usernames}
|
||||
context = {"events": all_future_events, "usernames": usernames}
|
||||
|
||||
return render(request, 'eventplanner/events_grid.html', context)
|
||||
return render(request, "eventplanner/events_grid.html", context)
|
||||
|
||||
|
||||
def deleteEvent(request, pk):
|
||||
@@ -93,27 +106,30 @@ def deleteEvent(request, pk):
|
||||
# ------------------------------------ Detail Views ----------------------------------------------------
|
||||
|
||||
|
||||
from django.views.generic.edit import UpdateView, CreateView
|
||||
|
||||
from location_field.widgets import LocationWidget
|
||||
|
||||
|
||||
class EventForm(ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = 'form-horizontal'
|
||||
self.helper.add_input(Submit('submit', 'Speichern'))
|
||||
super(EventForm, self).__init__(*args, **kwargs)
|
||||
self.helper.form_class = "form-horizontal"
|
||||
self.helper.add_input(Submit("submit", "Speichern"))
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ['type', 'short_desc', 'date', 'end_date', 'time', 'meeting_time', 'location', 'map_location',
|
||||
'desc', ]
|
||||
fields = [
|
||||
"type",
|
||||
"short_desc",
|
||||
"date",
|
||||
"end_date",
|
||||
"time",
|
||||
"meeting_time",
|
||||
"location",
|
||||
"map_location",
|
||||
"desc",
|
||||
]
|
||||
|
||||
widgets = {
|
||||
'time': TimeInput(format='%H:%M'),
|
||||
'location': TextInput(),
|
||||
'map_location': LocationWidget(),
|
||||
"location": TextInput(),
|
||||
"map_location": LocationWidget(),
|
||||
}
|
||||
|
||||
|
||||
@@ -121,11 +137,11 @@ class EventUpdate(UpdateView):
|
||||
form_class = EventForm
|
||||
model = Event
|
||||
template_name_suffix = "_update_form"
|
||||
success_url = '.'
|
||||
success_url = "."
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||
context['viewtype'] = "update"
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["viewtype"] = "update"
|
||||
return context
|
||||
|
||||
|
||||
@@ -133,9 +149,9 @@ class EventCreate(CreateView):
|
||||
form_class = EventForm
|
||||
model = Event
|
||||
template_name_suffix = "_update_form"
|
||||
success_url = '.'
|
||||
success_url = "."
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CreateView, self).get_context_data(**kwargs)
|
||||
context['viewtype'] = "create"
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["viewtype"] = "create"
|
||||
return context
|
||||
|
||||
Reference in New Issue
Block a user