# -*- 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[A-Z])(?P[0-9][0-9]*)/(?P[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