Files
django-dav-events/dav_events/models.py

556 lines
22 KiB
Python

# -*- coding: utf-8 -*-
import datetime
import logging
import os
import re
import uuid
from babel.dates import format_date
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.db import models
from django.template.loader import get_template
from django.utils import timezone
from django.utils.translation import get_language, ugettext, ugettext_lazy as _
from django_countries.fields import CountryField
from . import choices
from . import config
from . import emails
from .utils import get_users_by_role
logger = logging.getLogger(__name__)
def get_ghost_user():
return get_user_model().objects.get_or_create(username='-deleted-')[0]
class Event(models.Model):
# Metadata
owner = models.ForeignKey(settings.AUTH_USER_MODEL,
null=True,
on_delete=models.SET(get_ghost_user),
related_name='events')
created_at = models.DateTimeField(auto_now_add=True)
accepted = models.BooleanField(default=False)
accepted_at = models.DateTimeField(blank=True,
null=True)
accepted_by = models.ForeignKey(settings.AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET(get_ghost_user),
related_name='+')
number = models.CharField(unique=True,
max_length=config.NUMBER_MAX_LENGTH,
blank=True,
null=True,
default=None)
published = models.BooleanField(default=False)
published_at = models.DateTimeField(blank=True,
null=True)
published_by = models.ForeignKey(settings.AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET(get_ghost_user),
related_name='+')
# DescriptionForm
title = models.CharField(max_length=config.TITLE_MAX_LENGTH)
description = models.TextField()
# ModeForm
mode = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.MODE_CHOICES)
sport = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.SPORT_CHOICES)
ski_lift = models.BooleanField(default=False)
level = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.LEVEL_CHOICES)
first_day = models.DateField()
alt_first_day = models.DateField(blank=True,
null=True)
last_day = models.DateField(blank=True,
null=True)
alt_last_day = models.DateField(blank=True,
null=True)
# TrainingForm
course_topic_1 = models.TextField(blank=True)
course_topic_2 = models.TextField(blank=True)
course_topic_3 = models.TextField(blank=True)
course_topic_4 = models.TextField(blank=True)
course_topic_5 = models.TextField(blank=True)
course_topic_6 = models.TextField(blank=True)
course_goal_1 = models.TextField(blank=True)
course_goal_2 = models.TextField(blank=True)
course_goal_3 = models.TextField(blank=True)
course_goal_4 = models.TextField(blank=True)
course_goal_5 = models.TextField(blank=True)
course_goal_6 = models.TextField(blank=True)
# LocationForm
country = CountryField(countries=choices.CountryChoiceSet)
terrain = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.TERRAIN_CHOICES)
location = models.CharField(max_length=config.LOCATION_MAX_LENGTH,
blank=True)
# ApproachForm
transport = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.TRANSPORT_CHOICES)
transport_other = models.CharField(max_length=config.TRANSPORT_OTHER_MAX_LENGTH,
blank=True)
meeting_point = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.MEETING_POINT_CHOICES)
meeting_point_other = models.CharField(max_length=config.MEETING_POINT_OTHER_MAX_LENGTH,
blank=True)
meeting_time = models.TimeField(blank=True,
null=True)
arrival_previous_day = models.BooleanField(default=False)
return_time = models.TimeField(blank=True,
null=True)
basecamp = models.CharField(max_length=config.BASECAMP_MAX_LENGTH,
blank=True)
accommodation = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.ACCOMMODATION_CHOICES)
accommodation_other = models.CharField(max_length=config.ACCOMMODATION_OTHER_MAX_LENGTH,
blank=True)
meals = models.CharField(max_length=choices.CHOICE_FIELD_MAX_LENGTH,
choices=choices.MEALS_CHOICES)
meals_other = models.CharField(max_length=config.MEALS_OTHER_MAX_LENGTH,
blank=True)
# RequirementsForm
requirements = models.TextField(blank=True)
equipment = models.TextField(blank=True)
pre_meeting_1 = models.DateTimeField(blank=True,
null=True)
pre_meeting_2 = models.DateTimeField(blank=True,
null=True)
# RegistrationForm
min_participants = models.IntegerField(default=0)
max_participants = models.IntegerField(default=0)
deadline = models.DateField(blank=True,
null=True)
registration_howto = models.TextField(blank=True)
# TrainerForm
trainer_firstname = models.CharField(max_length=config.TRAINER_NAME_MAX_LENGTH,
blank=True)
trainer_familyname = models.CharField(max_length=config.TRAINER_NAME_MAX_LENGTH,
blank=True)
trainer_email = models.EmailField(blank=True)
trainer_phone = models.CharField(max_length=config.PHONE_NUMBER_MAX_LENGTH,
blank=True)
trainer_2_fullname = models.CharField(max_length=config.TRAINER_NAME_MAX_LENGTH,
blank=True)
trainer_2_email = models.EmailField(blank=True)
trainer_2_phone = models.CharField(max_length=config.PHONE_NUMBER_MAX_LENGTH,
blank=True)
trainer_3_fullname = models.CharField(max_length=config.TRAINER_NAME_MAX_LENGTH,
blank=True)
trainer_3_email = models.EmailField(blank=True)
trainer_3_phone = models.CharField(max_length=config.PHONE_NUMBER_MAX_LENGTH,
blank=True)
# ChargesForm
charge = models.FloatField(default=0)
additional_costs = models.CharField(max_length=config.ADDITIONAL_COSTS_MAX_LENGTH,
blank=True)
class Meta:
verbose_name = _(u'Veranstaltung')
verbose_name_plural = _(u'Veranstaltungen')
ordering = ['first_day']
default_permissions = ('view', 'accept', 'edit', 'delete')
def __unicode__(self):
return u'{number} - {title} ({date})'.format(number=self.get_number(),
title=self.title,
date=self.get_short_date())
def get_absolute_url(self):
return reverse('dav_events:event_detail', kwargs={'pk': self.pk})
def save(self, **kwargs):
creating = False
if not self.id:
user_model = get_user_model()
try:
owner = user_model.objects.get(username=self.trainer_email.lower())
except user_model.DoesNotExist:
owner = user_model(username=self.trainer_email.lower(),
first_name=self.trainer_firstname,
last_name=self.trainer_familyname,
email=self.trainer_email,
)
owner.save()
logger.info('Owner created: %s', owner.username)
self.owner = owner
creating = True
super(Event, self).save(**kwargs)
if creating:
logger.info('Event created: %s', self)
managers = get_users_by_role('manage_all')
managers += get_users_by_role('manage_{}'.format(self.sport.lower()))
for user in managers:
action = OneClickAction(command='EA')
action.parameters = '{event},{user}'.format(event=self.id, user=user.id)
action.save()
email = emails.EventToAcceptMail(recipient=user, event=self, accept_action=action)
email.send()
email = emails.NewEventMail(recipient=self.owner, event=self)
email.send()
def accept(self, user=None):
if not self.accepted:
if not self.number:
self.number = self.get_next_number()
self.accepted = True
self.accepted_at = timezone.now()
if user:
self.accepted_by = user
else:
logger.warning('Event.accept(): no user given! (Event: %s)', self.event)
self.save()
logger.info('Event is accepted: %s', self)
publishers = get_users_by_role('publish_incremental')
for user in publishers:
action = OneClickAction(command='EP')
action.parameters = '{event},{user}'.format(event=self.id, user=user.id)
action.save()
email = emails.EventToPublishMail(recipient=user, event=self, set_published_action=action)
email.send()
email = emails.EventAcceptedMail(recipient=self.owner, event=self)
email.send()
return self.number
else:
return None
def set_published(self, user=None):
if not self.accepted:
logger.warning('Event.set_published(): event is not accepted yet! (Event: %s)', self.event)
if not self.published:
self.published = True
self.published_at = timezone.now()
if user:
self.published_by = user
else:
logger.warning('Event.set_published(): no user given! (Event: %s)', self.event)
self.save()
logger.info('Event is published: %s', self)
def get_status(self):
now = datetime.date.today()
if self.alt_last_day:
if self.alt_last_day < now:
return 'expired'
elif self.last_day:
if self.last_day < now:
return 'expired'
elif self.alt_first_day and self.alt_first_day < now:
return 'expired'
elif self.first_day and self.first_day < now:
return 'expired'
if self.published:
return 'published'
elif self.accepted:
return 'accepted'
elif self.owner:
return 'submitted'
logger.debug("here")
return 'draft'
def get_next_number(self):
counter = 0
year = self.first_day.year
year_begin = datetime.date(year, 1, 1)
year_end = datetime.date(year, 12, 31)
qs = Event.objects.filter(number__isnull=False,
sport=self.sport,
first_day__gte=year_begin,
first_day__lte=year_end).order_by('-number')
last = qs.first()
if last:
match = re.match(r'^(?P<sport>[A-Z])(?P<count>[0-9][0-9]*)/(?P<year>[0-9][0-9]*)', last.number)
if match:
gd = match.groupdict()
counter = int(gd['count'])
counter += 1
n = '%s%02d/%d' % (self.sport, counter, year % 100)
return n
def get_number(self):
if self.accepted and self.number:
return self.number
else:
return '%s**/%d' % (self.sport, self.first_day.year % 100)
def get_numeric_date(self, begin_date=None, end_date=None):
if begin_date is None:
begin_date = self.first_day
if end_date is None:
end_date = self.last_day
if not end_date:
r = begin_date.strftime('%d.%m.%Y')
else:
begin = begin_date.strftime('%d.%m.%Y')
end = end_date.strftime('%d.%m.%Y')
r = u'{begin} - {end}'.format(begin=begin, end=end)
return r
def get_short_date(self, begin_date=None, end_date=None):
if begin_date is None:
begin_date = self.first_day
if end_date is None:
end_date = self.last_day
lang = get_language()[0:2]
if not end_date:
r = format_date(begin_date, 'EEEE, d. MMMM yyyy', locale=lang)
else:
end_format = 'EEEE, d. MMMM yyyy'
begin_format = 'EEEE, d.'
if begin_date.month != end_date.month:
begin_format += ' MMMM'
if begin_date.year != end_date.year:
begin_format += ' yyyy'
begin = format_date(begin_date, begin_format, locale=lang)
end = format_date(end_date, end_format, locale=lang)
r = u'{begin} - {end}'.format(begin=begin, end=end)
return r
def get_alt_short_date(self):
if self.alt_first_day:
return self.get_short_date(self.alt_first_day, self.alt_last_day)
else:
return None
def get_template_context(self):
context = {
'event': self,
'status': self.get_status(),
'number': self.get_number(),
'mode': self.mode,
'sport': self.sport,
'title': self.title,
'first_day': self.first_day,
'last_day': self.last_day,
'short_date': self.get_short_date(),
'alt_first_day': self.alt_first_day,
'alt_last_day': self.alt_last_day,
'alt_short_date': self.get_alt_short_date(),
'description': self.description,
'course_topic_1': self.course_topic_1,
'course_topic_2': self.course_topic_2,
'course_topic_3': self.course_topic_3,
'course_topic_4': self.course_topic_4,
'course_topic_5': self.course_topic_5,
'course_topic_6': self.course_topic_6,
'course_goal_1': self.course_goal_1,
'course_goal_2': self.course_goal_2,
'course_goal_3': self.course_goal_3,
'course_goal_4': self.course_goal_4,
'course_goal_5': self.course_goal_5,
'course_goal_6': self.course_goal_6,
'country': self.country,
'location': self.location,
'basecamp': self.basecamp,
'accommodation': self.accommodation,
'accommodation_other': self.accommodation_other,
'meals': self.meals,
'meals_other': self.meals_other,
'transport': self.transport,
'transport_other': self.transport_other,
'meeting_time': self.meeting_time,
'meeting_point': self.meeting_point,
'meeting_point_other': self.meeting_point_other,
'requirements': self.requirements,
'equipment': self.equipment,
'pre_meeting_1': self.pre_meeting_1,
'pre_meeting_2': self.pre_meeting_2,
'deadline': self.deadline,
'min_participants': self.min_participants,
'max_participants': self.max_participants,
'charge': self.charge,
'additional_costs': self.additional_costs,
'trainer_firstname': self.trainer_firstname,
'trainer_familyname': self.trainer_familyname,
'trainer_email': self.trainer_email,
'trainer_phone': self.trainer_phone,
'trainer_2_fullname': self.trainer_2_fullname,
'trainer_2_email': self.trainer_2_email,
'trainer_2_phone': self.trainer_2_phone,
'trainer_3_fullname': self.trainer_3_fullname,
'trainer_3_email': self.trainer_3_email,
'trainer_3_phone': self.trainer_3_phone,
'registration_howto': self.registration_howto,
}
return context
def render_as_text(self):
template_name = os.path.join('dav_events', 'event.txt')
template = get_template(template_name)
return template.render(self.get_template_context())
def render_as_html(self):
template_name = os.path.join('dav_events', 'event.html')
template = get_template(template_name)
return template.render(self.get_template_context())
class OneClickAction(models.Model):
COMMANDS = (
('EA', 'accept event'),
('EP', 'report publishing of an event'),
('EL', 'login and go to event list')
)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
allow_repeat = models.BooleanField(default=False)
done = models.BooleanField(default=False)
done_at = models.DateTimeField(blank=True,
null=True)
command = models.CharField(max_length=2, choices=COMMANDS)
parameters = models.TextField(blank=True)
class Meta:
verbose_name = _(u'One-Click-Action')
def __unicode__(self):
s = u'{command}({parameters}) - {description}'.format(description=self.get_command_display(),
command=self.command,
parameters=self.parameters)
if self.done and not self.allow_repeat:
s += u' - done'
return s
def get_absolute_url(self):
return reverse('dav_events:action_run', kwargs={'pk': self.pk})
def run(self):
result = {}
if self.done and not self.allow_repeat:
result['context'] = {
'status': 'warning',
'message': ugettext(u'Diese Aktion hast du bereits ausgeführt.'),
}
return result
logger.info('OneClickAction.run(): %s(%s)', self.command, self.parameters)
if self.command == 'EA':
text = u''
try:
event_id, user_id = self.parameters.split(',')
event = Event.objects.get(id=event_id)
user = get_user_model().objects.get(id=user_id)
number = event.accept(user)
if number:
status = 'success'
message = ugettext(u'Veranstaltung freigegeben.')
text = unicode(event)
else:
status = 'info'
message = (ugettext(u'Veranstaltung wurde bereits von %(fullname)s freigegeben.') %
{'fullname': event.accepted_by.get_full_name()})
text = unicode(event)
text += u'\n'
text += (ugettext(u'Freigegeben am: %(date)s') %
{'date': event.accepted_at.strftime('%d.%m.%Y %H:%M:%S')})
self.done = True
self.done_at = timezone.now()
self.save()
except Exception as e:
status = 'danger'
message = str(e)
logger.error('OneClickAction.run(): %s(%s): %s', self.command, self.parameters, message)
result['context'] = {
'status': status,
'message': message,
'text': text,
}
elif self.command == 'EP':
text = u''
try:
event_id, user_id = self.parameters.split(',')
event = Event.objects.get(id=event_id)
user = get_user_model().objects.get(id=user_id)
if event.published:
status = 'info'
message = (ugettext(u'Veröffentlichung wurde bereits von %(fullname)s bestätigt.') %
{'fullname': event.published_by.get_full_name()})
text = unicode(event)
text += u'\n'
text += (ugettext(u'Bestätigt am: %(date)s') %
{'date': event.published_at.strftime('%d.%m.%Y %H:%M:%S')})
else:
event.set_published(user)
status = 'success'
message = ugettext(u'Veröffentlichung registriert.')
text = unicode(event)
self.done = True
self.done_at = timezone.now()
self.save()
except Exception as e:
status = 'danger'
message = str(e)
logger.error('OneClickAction.run(): %s(%s): %s', self.command, self.parameters, message)
result['context'] = {
'status': status,
'message': message,
'text': text,
}
elif self.command == 'EL':
try:
user_id = self.parameters
user = get_user_model().objects.get(id=user_id)
url = reverse('dav_events:event_list')
result['location'] = url
result['login'] = user
except Exception as e:
message = str(e)
logger.error('OneClickAction.run(): %s(%s): %s', self.command, self.parameters, message)
result['context'] = {
'status': 'danger',
'message': message,
}
else:
result['context'] = {
'status': 'danger',
'message': ugettext(u'Invalid Command. Code on fire!'),
}
return result