eventoL/eventoL

View on GitHub
eventol/manager/models.py

Summary

Maintainability
F
1 wk
Test Coverage
B
80%
# pylint: disable=arguments-differ
# pylint: disable=too-many-lines

import datetime
import itertools
import json
import logging
import re

from uuid import uuid4
from random import SystemRandom
from string import digits, ascii_lowercase, ascii_uppercase

from ckeditor.fields import RichTextField
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.postgres.fields import JSONField
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.formats import date_format
from django.utils.translation import ugettext_lazy as _, ugettext_noop as _noop
from forms_builder.forms.models import AbstractField, AbstractForm
from image_cropping import ImageCropField, ImageRatioField

from vote.models import VoteModel
from manager.utils.report import count_by
from manager.utils.slug import get_unique_slug

logger = logging.getLogger('eventol')


def validate_url(url):
    if not re.match('^[a-zA-Z0-9-_]+$', url):
        raise ValidationError(_('URL can only contain letters or numbers'))


def generate_ticket_code():
    chars = digits + ascii_lowercase + ascii_uppercase
    length = 21
    return ''.join([SystemRandom().choice(chars) for _ in range(length)])


class EventManager(models.Manager):
    def get_queryset(self):
        today = timezone.localdate()
        return super() \
            .get_queryset() \
            .annotate(attendees_count=models.Count('attendee', distinct=True)) \
            .annotate(last_date=models.Max('eventdate__date')) \
            .annotate(activity_proposal_is_open=models.Case(
                models.When(models.Q(limit_proposal_date__gte=today), then=True),
                default=False,
                output_field=models.BooleanField()
            )) \
            .annotate(registration_is_open=models.Case(
                models.When(models.Q(last_date__gte=today), then=True),
                default=False,
                output_field=models.BooleanField()
            ))

    @staticmethod
    def get_event_by_user(user, tag_slug=None):
        if user.is_authenticated():
            event_users = EventUser.objects.filter(user=user)
            event_ids = [event_user.event.pk for event_user in list(event_users)]
            queryset = Event.objects.filter(pk__in=event_ids)
            if tag_slug:
                queryset = queryset.filter(tags__slug=tag_slug)
        else:
            queryset = Event.objects.none()
        return queryset

    @staticmethod
    def get_event_private_data():
        events = []
        for event in Event.objects.all():
            organizers = Organizer.objects.filter(event_user__event=event)
            users = map(lambda organizer: organizer.event_user.user, organizers)
            full_names = [user.get_full_name() for user in users]
            events.append({
                'organizers': ','.join(full_names),
                'email': event.email,
                'id': event.id
            })
        return events


class EventTag(models.Model):
    """A Event grouper"""
    name = models.CharField(_('EventTag Name'), max_length=50, unique=True,
                            help_text=_("This name will be used as a slug"))
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    background = models.ImageField(
        null=True, blank=True,
        help_text=_("A image to show in the background of"))
    logo_header = models.ImageField(
        null=True, blank=True,
        help_text=_("This logo will be shown in the right corner of the page"))
    logo_landing = models.ImageField(
        null=True, blank=True,
        help_text=_("Logo to show in the center of the page"))
    message = models.TextField(max_length=280, null=True, blank=True,
                               help_text=_("A message to show in the center of the page"))
    slug = models.SlugField(_('URL'), max_length=100,
                            help_text=_('For example: flisol-caba'), unique=True)
    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        """
        Override default save

        it will add the slug field using slugify.
        """
        if not self.slug:
            self.slug = get_unique_slug(self, 'name', 'slug')
        super().save(*args, **kwargs)


class CustomForm(AbstractForm):
    def published(self, for_user=None):
        return True

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['title']
        verbose_name = _('Custom Form')
        verbose_name_plural = _('Custom Forms')


class CustomField(AbstractField):
    form = models.ForeignKey(CustomForm, related_name='fields', on_delete=models.CASCADE)
    order = models.IntegerField(_('Order'), null=False, blank=False)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        fields_after = self.form.fields.filter(order__gte=self.order)
        fields_after.update(order=models.F("order") - 1)
        super().delete(*args, **kwargs)

    def __str__(self):
        return '{0}: {1} ({2})'.format(self.form, self.label, self.slug)

    class Meta:
        ordering = ['form', 'order']
        verbose_name = _('Custom Field')
        verbose_name_plural = _('Custom Fields')
        unique_together = ('form', 'slug',)


class Event(models.Model):
    objects = EventManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    name = models.CharField(_('Event Name'), max_length=50)
    abstract = models.TextField(_('Abstract'), max_length=250,
                                help_text=_('Idea of the event \
                                            (one or two sentences)'))
    limit_proposal_date = models.DateField(_('Limit Proposals Date'),
                                           help_text=_('Limit date to submit talk proposals'))
    registration_closed = models.BooleanField(
        default=False, help_text=_("set it to True to force the registration to be closed"))

    tags = models.ManyToManyField(
        EventTag, blank=True, help_text=_("Select tags to show this event in the EventTag landing"))
    event_slug = models.SlugField(_('URL'), max_length=100,
                                  help_text=_('For example: flisol-caba'), unique=True)
    customForm = models.ForeignKey(CustomForm, verbose_name=_noop('Custom form'),
                                   blank=True, null=True)
    cname = models.CharField(_('CNAME'), max_length=50, blank=True, null=True,
                             help_text=_('For example: flisol-caba'),
                             validators=[validate_url])
    registration_code = models.UUIDField(
        default=uuid4,
        editable=False,
        unique=True,
        verbose_name=_('code'),
        help_text=_('Code validator for in-place event self-registration'),
    )
    external_url = models.URLField(_('External URL'), blank=True, null=True, default=None,
                                   help_text=_('http://www.my-awesome-event.com'))
    email = models.EmailField(verbose_name=_('Email'))
    event_information = RichTextField(verbose_name=_('Event Info'),
                                      help_text=_('Event Info HTML'),
                                      blank=True, null=True)
    schedule_confirmed = models.BooleanField(_('Schedule Confirmed'), default=False)
    use_installations = models.BooleanField(_('Use Installations'), default=True)
    use_installers = models.BooleanField(_('Use Installers'), default=True)
    use_collaborators = models.BooleanField(_('Use Collaborators'), default=True)
    use_proposals = models.BooleanField(_('Use Proposals'), default=True)
    use_talks = models.BooleanField(_('Use Talks'), default=True)
    is_flisol = models.BooleanField(_('Is FLISoL'), default=False)
    use_schedule = models.BooleanField(_('Use Schedule'), default=True)
    place = models.TextField(_('Place'), null=True, blank=True)
    image = ImageCropField(upload_to='images_thumbnails',
                           verbose_name=_('Image'), blank=True, null=True)
    cropping = ImageRatioField('image', '700x450', size_warning=True,
                               verbose_name=_('Cropping'), free_crop=True,
                               help_text=_('The image must be 700x450 px. You can crop it here.'))
    activities_proposal_form_text = RichTextField(
        verbose_name=_('Activity proposal form text'),
        help_text=_("A message to show in the activities proposal form"),
        blank=True, null=True
    )
    template = models.FileField(_('Template'),
                                upload_to='templates', blank=True, null=True,
                                help_text=_('Custom template HTML for event index page'))
    css_custom = models.FileField(_('Custom CSS'),
                                  upload_to='custom_css', blank=True, null=True,
                                  help_text=_('Custom CSS file for event page'))

    @classmethod
    def get_fields_dependencies(cls):
        return {
            'use_proposals': ['limit_proposal_date', 'activities_proposal_form_text'],
            'use_talks': ['use_proposals'],
            'use_installations': ['use_installers']
        }

    @property
    def location(self):
        try:
            place = json.loads(self.place)
            components = place['address_components']
            components = filter(
                lambda componet: 'political' in componet['types'],
                components
            )
            components = map(
                lambda componet: componet['long_name'],
                components
            )
            return components
        except json.JSONDecodeError as error:
            logger.error(error)
        except:
            pass
        return []

    @property
    def report(self):
        event_user = EventUser.objects.get_counts_by_event(self)
        collaborator = Collaborator.objects.get_counts_by_event(self)
        organizer = Organizer.objects.get_counts_by_event(self)
        attendee = Attendee.objects.get_counts_by_event(self)
        installer = Installer.objects.get_counts_by_event(self)
        activity = Activity.objects.get_counts_by_event(self)
        installation = Installation.objects.get_counts_by_event(self)
        speakers = []
        for talk in Activity.objects.filter(event=self, status='2'):
            speakers.append(talk.speakers_names.split(','))
        speakers_count = len(set(itertools.chain.from_iterable(speakers)))
        return {
            'event_user': event_user,
            'collaborator': collaborator,
            'organizer': organizer,
            'attendee': attendee,
            'installer': installer,
            'activity': activity,
            'installation': installation,
            'speakers': speakers_count
        }

    def get_absolute_url(self):
        if self.external_url:
            return self.external_url
        return reverse('index', kwargs={'event_slug': self.event_slug})

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']
        verbose_name = _('Event')
        verbose_name_plural = _('Events')

    def save(self, *args, **kwargs):
        """
        Override default save

        it will add the slug field using slugify.
        """
        if not self.event_slug:
            self.event_slug = get_unique_slug(self, 'name', 'slug')
        super().save(*args, **kwargs)


class EventDate(models.Model):
    event = models.ForeignKey(Event, verbose_name=_noop('Event'),
                              blank=True, null=True)
    date = models.DateField(_('Date'), help_text=_('When will your event be?'))

    def __str__(self):
        return '{} - {}'.format(self.event, self.date)

    class Meta:
        verbose_name = _('Event Date')
        verbose_name_plural = _('Event Dates')


class ContactMessage(models.Model):
    name = models.CharField(max_length=255, verbose_name=_('Name'))
    email = models.EmailField(verbose_name=_('Email'))
    message = models.TextField(verbose_name=_('Message'))
    event = models.ForeignKey(Event, verbose_name=_noop('Event'),
                              blank=True, null=True)

    def __str__(self):
        return _noop(
            'Message received from: {name}\n'
            'User email: {email}\n\n'
            '{message}'
        ).format(
            name=self.name,
            email=self.email,
            message=self.message
        )

    class Meta:
        verbose_name = _('Contact Message')
        verbose_name_plural = _('Contact Messages')


class ContactType(models.Model):
    """
    For example:
        Name: Facebook
        Icon Class: fa-facebook-square
    """
    validator_choices = (
        ('1', _('Validate URL')),
        ('2', _('Validate Email')),
        ('3', _('Don\'t validate'))
    )
    name = models.CharField(_('Name'), unique=True, max_length=200)
    icon_class = models.CharField(_('Icon Class'), max_length=200)
    validate = models.CharField(_('Level'), choices=validator_choices,
                                max_length=10,
                                help_text=_('Type of field validation'))

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('Contact Type')
        verbose_name_plural = _('Contact Types')


class Contact(models.Model):
    type = models.ForeignKey(ContactType, verbose_name=_('Contact Type'))
    url = models.CharField(_noop('Direccion'),
                           help_text=_('i.e. https://twitter.com/flisol'),
                           max_length=200)
    text = models.CharField(_('Text'), max_length=200,
                            help_text=_('i.e. @Flisol'))
    event = models.ForeignKey(Event, verbose_name=_noop('Event'),
                              related_name='contacts', blank=True, null=False)

    def __str__(self):
        return '{} - {} - {}'.format(self.event, self.type, self.text)

    class Meta:
        verbose_name = _('Contact')
        verbose_name_plural = _('Contacts')


class Ticket(models.Model):
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    sent = models.BooleanField(_('Sent'), default=False)
    code = models.CharField(
        max_length=21,
        default=generate_ticket_code,
        editable=False,
        unique=True,
        verbose_name=_('number'),
        help_text=_('Unique identifier for the ticket'),
    )

    def __str__(self):
        return self.code


class EventUserManager(models.Manager):
    @staticmethod
    def get_event_user(instance):
        if hasattr(instance, 'event_user'):
            return instance.event_user
        return instance

    @staticmethod
    def get_counts(event_users):
        confirmed = EventUserAttendanceDate.objects \
            .filter(event_user__in=event_users) \
            .order_by('event_user') \
            .distinct() \
            .count()
        total = len(event_users)
        return {
            'total': total,
            'confirmed': confirmed,
            'not_confirmed': total - confirmed
        }

    def get_counts_by_event(self, event):
        model = self.model
        if hasattr(model, 'event_user'):
            queryset = model.objects.filter(event_user__event=event)
        else:
            queryset = model.objects.filter(event=event)
        event_users = self.get_event_users(queryset)
        return self.get_counts(event_users)

    def get_event_users(self, queryset):
        return [self.get_event_user(instance) for instance in queryset]


class EventUser(models.Model):
    objects = EventUserManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    user = models.ForeignKey(User, verbose_name=_('User'),
                             blank=True, null=True)
    event = models.ForeignKey(Event, verbose_name=_('Event'))
    ticket = models.ForeignKey(Ticket, verbose_name=_('Ticket'),
                               blank=True, null=True)

    def __str__(self):
        if self.user:
            return '{} at event:{}'.format(self.user.username, self.event)
        return '{}'.format(self.event)

    def get_ticket_data(self):
        if self.ticket is None:
            ticket = Ticket()
            ticket.save()
            self.ticket = ticket
            self.save()
        date = self.event.eventdate_set.order_by('date').first().date
        return {'first_name': self.user.first_name,
                'last_name': self.user.last_name,
                'nickname': self.user.username,
                'email': self.user.email, 'event': self.event,
                'event_date': date, 'ticket': self.ticket}

    def attended(self):
        return EventUserAttendanceDate.objects.filter(event_user=self).exists()

    def attended_today(self):
        return EventUserAttendanceDate.objects.filter(
            event_user=self, date__date=timezone.localdate()).exists()

    class Meta:
        unique_together = (('event', 'user'),)
        verbose_name = _('Event User')
        verbose_name_plural = _('Event Users')


class EventUserAttendanceDate(models.Model):
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    event_user = models.ForeignKey(EventUser, verbose_name=_noop('Event User'),
                                   blank=False, null=False)
    date = models.DateTimeField(_('Date'),
                                help_text=_('The date of the attendance'),
                                auto_now_add=True)
    attendance_mode_choices = (
        ('1', _('Qr autoregistration')),
        ('2', _('Qr ticket')),
        ('3', _('Previous registration')),
        ('4', _('unregistred'))
    )
    mode = models.CharField(_('Mode'), choices=attendance_mode_choices,
                            max_length=200, blank=True, null=True,
                            help_text=_('Attendance mode'))

    def __str__(self):
        return '{} - {}'.format(self.event_user, self.date)


class Collaborator(models.Model):
    objects = EventUserManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    event_user = models.ForeignKey(EventUser, verbose_name=_('Event User'),
                                   blank=True, null=True)
    assignation = models.CharField(_('Assignation'), max_length=200,
                                   blank=True, null=True,
                                   help_text=_('Anything you can help with \
                                               (i.e. Talks, Coffee…)'))
    time_availability = models.CharField(_('Time Availability'),
                                         max_length=200, blank=True,
                                         null=True,
                                         help_text=_('Time period in which you can \
                                                     help during the event. i.e. \
                                                     "All the event", "Morning", \
                                                     "Afternoon", …'))
    phone = models.CharField(_('Phone'), max_length=200, blank=True, null=True)
    address = models.CharField(_('Address'), max_length=200,
                               blank=True, null=True)
    additional_info = models.CharField(_('Additional Info'), max_length=200,
                                       blank=True, null=True,
                                       help_text=_('Additional info you consider relevant'))

    class Meta:
        verbose_name = _('Collaborator')
        verbose_name_plural = _('Collaborators')

    def __str__(self):
        return str(self.event_user)


class Organizer(models.Model):
    """Event organizer"""
    objects = EventUserManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    event_user = models.ForeignKey(EventUser, verbose_name=_('Event User'),
                                   blank=True, null=True)

    class Meta:
        verbose_name = _('Organizer')
        verbose_name_plural = _('Organizers')

    def __str__(self):
        return str(self.event_user)


class Reviewer(models.Model):
    """User that collaborates with activities review"""
    objects = EventUserManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    event_user = models.ForeignKey(EventUser, verbose_name=_('Event User'),
                                   blank=True, null=True)

    def __str__(self):
        return str(self.event_user)


class AttendeeManager(EventUserManager):
    @staticmethod
    def get_attendees(queryset):
        return [inst for inst in queryset if not inst.event_user]

    #pylint: disable=arguments-differ
    def get_counts(self, queryset):
        event_users = self.get_event_users(
            queryset.filter(event_user__isnull=False))
        confirmed_with_event_user = AttendeeAttendanceDate.objects \
            .filter(attendee__event_user__in=event_users) \
            .order_by('event_user') \
            .distinct() \
            .count()
        total_with_event_user = len(event_users)
        attendees = self.get_attendees(queryset)
        confirmed = AttendeeAttendanceDate.objects \
            .filter(
                attendee__in=attendees, attendee__event_user__isnull=True) \
            .order_by('attendees') \
            .distinct() \
            .count()
        total = len(attendees)
        return {
            'with_event_user': {
                'total': total_with_event_user,
                'confirmed': confirmed_with_event_user,
                'not_confirmed':
                    total_with_event_user - confirmed_with_event_user
            },
            'without_event_user': {
                'total': total,
                'confirmed': confirmed,
                'not_confirmed': total - confirmed
            }
        }

    def get_counts_by_event(self, event):
        queryset = Attendee.objects.filter(event=event)
        return self.get_counts(queryset)


class Attendee(models.Model):
    objects = AttendeeManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    first_name = models.CharField(_('First Name'), max_length=200, blank=True, null=True)
    last_name = models.CharField(_('Last Name'), max_length=200, blank=True, null=True)
    nickname = models.CharField(_('Nickname'), max_length=200, blank=True, null=True)
    email = models.EmailField(_('Email'))
    event = models.ForeignKey(Event, verbose_name=_('Event'))
    ticket = models.ForeignKey(Ticket, verbose_name=_('Ticket'), blank=True, null=True)
    is_installing = models.BooleanField(_('Is going to install?'), default=False)
    additional_info = models.CharField(_('Additional Info'), max_length=200, blank=True, null=True,
                                       help_text=_('Additional info you consider \
                                                   relevant to the organizers'))
    email_confirmed = models.BooleanField(_('Email confirmed?'), default=False)
    email_token = models.CharField(_('Confirmation Token'), max_length=200, blank=True, null=True)
    registration_date = models.DateTimeField(_('Registration Date'), blank=True, null=True)
    event_user = models.ForeignKey(
        EventUser, verbose_name=_noop('Event User'), blank=True, null=True)
    customFields = JSONField(default=dict)

    class Meta:
        verbose_name = _('Attendee')
        verbose_name_plural = _('Attendees')
        unique_together = (('event', 'email'),)

    def __str__(self):
        if self.event_user:
            return str(self.event_user)
        return '{} - {} {} - {} - {}'.format(self.event, self.first_name, self.last_name,
                                             self.nickname, self.email)

    def get_ticket_data(self):
        if self.ticket is None:
            ticket = Ticket()
            ticket.save()
            self.ticket = ticket
            self.save()

        date = self.event.eventdate_set.order_by('date').first().date
        return {'first_name': self.first_name, 'last_name': self.last_name,
                'nickname': self.nickname, 'email': self.email,
                'event': self.event, 'event_date': date, 'ticket': self.ticket}

    def attended(self):
        return AttendeeAttendanceDate.objects.filter(attendee=self).exists()

    def attended_today(self):
        return AttendeeAttendanceDate.objects.filter(
            attendee=self, date__date=timezone.localdate()).exists()


class AttendeeAttendanceDate(models.Model):
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    attendee = models.ForeignKey(Attendee, verbose_name=_noop('Attendee'),
                                 blank=False, null=False)
    date = models.DateTimeField(_('Date'),
                                help_text=_('The date of the attendance'),
                                auto_now_add=True)
    attendance_mode_choices = (
        ('1', _('Qr autoregistration')),
        ('2', _('Qr ticket')),
        ('3', _('Previous registration')),
        ('4', _('unregistred'))
    )
    mode = models.CharField(_('Mode'), choices=attendance_mode_choices,
                            max_length=200, blank=True, null=True,
                            help_text=_('Attendance mode'))

    def __str__(self):
        return '{} - {}'.format(self.attendee, self.date)


class InstallationMessage(models.Model):
    event = models.ForeignKey(Event, verbose_name=_noop('Event'))
    message = RichTextField(verbose_name=_('Message Body'), help_text=_(
        'Email message HTML Body'), blank=True, null=True)
    contact_email = models.EmailField(verbose_name=_('Contact Email'))

    class Meta:
        verbose_name = _('Post-install Email')
        verbose_name_plural = _('Post-install Emails')

    def __str__(self):
        return str(self.event)


class Installer(models.Model):
    objects = EventUserManager()
    installer_choices = (
        ('1', _('Beginner')),
        ('2', _('Medium')),
        ('3', _('Advanced')),
        ('4', _('Super Hacker'))
    )
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    event_user = models.ForeignKey(EventUser, verbose_name=_('Event User'),
                                   blank=True, null=True)
    level = models.CharField(_('Level'), choices=installer_choices,
                             max_length=200,
                             help_text=_('Knowledge level for an installation'))

    class Meta:
        verbose_name = _('Installer')
        verbose_name_plural = _('Installers')

    def __str__(self):
        return str(self.event_user)


class Software(models.Model):
    software_choices = (
        ('OS', _('Operative System')),
        ('AP', _('Application')),
        ('SU', _('Support and Problem Fixing')),
        ('OT', _('Other'))
    )
    name = models.CharField(_('Name'), max_length=200)
    type = models.CharField(_('Type'),
                            choices=software_choices, max_length=200)

    def __str__(self):
        return '{} - {}'.format(self.type, self.name)


class Hardware(models.Model):
    hardware_choices = (
        ('MOB', _('Mobile')),
        ('NOTE', _('Notebook')),
        ('NET', _('Netbook')),
        ('TAB', _('Tablet')),
        ('DES', _('Desktop')),
        ('OTH', _('Other'))
    )
    type = models.CharField(_('Type'), choices=hardware_choices,
                            max_length=200)
    manufacturer = models.CharField(_('Manufacturer'), max_length=200,
                                    blank=True, null=True)
    model = models.CharField(_('Model'), max_length=200, blank=True, null=True)

    def __str__(self):
        return '{}, {}, {}'.format(self.type, self.manufacturer, self.model)


class Room(models.Model):
    event = models.ForeignKey(Event, verbose_name=_noop('Event'),
                              blank=True, null=True)
    name = models.CharField(_('Name'), max_length=200,
                            help_text=_('i.e. Classroom 256'))

    def __str__(self):
        return '{} - {}'.format(self.event, self.name)

    def get_schedule_info(self):
        return {'id': self.pk, 'title': self.name}

    class Meta:
        verbose_name = _('Room')
        verbose_name_plural = _('Rooms')
        ordering = ['name']


class ActivityManager(models.Manager):
    @staticmethod
    def get_counts(queryset):
        level_count = count_by(queryset, lambda activity: activity.level)
        status_count = count_by(queryset, lambda activity: activity.status)
        type_count = count_by(queryset, lambda activity: activity.activity_type)
        total = queryset.count()
        confirmed = queryset.filter(room__isnull=False).count()
        return {
            'level_count': level_count,
            'status_count': status_count,
            'type_count': type_count,
            'confirmed': confirmed,
            'not_confirmed': total - confirmed,
            'total': total
        }

    @staticmethod
    def get_activities_report(event):
        activities = Activity.objects.filter(event=event, is_dummy=False)
        return activities.values(
            'id', 'title', 'abstract', 'long_description',
            'activity_type', 'labels', 'level', 'additional_info',
            'speakers_names', 'owner__user__username', 'owner__user__first_name',
            'owner__user__last_name', 'owner__user__email', 'speaker_bio'
        )

    def get_counts_by_event(self, event):
        queryset = Activity.objects.filter(event=event)
        return self.get_counts(queryset)


class ActivityType(models.Model):
    """User created type of activities"""
    name = models.CharField(max_length=60, help_text=_("Kind of activity"))

    def __str__(self):
        return self.name


class Activity(VoteModel, models.Model):
    objects = ActivityManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    event = models.ForeignKey(Event, verbose_name=_('Event'))
    owner = models.ForeignKey(
        EventUser, help_text=_("Speaker or the person in charge of the activity"))
    title = models.CharField(_('Title'), max_length=100,
                             blank=False, null=False)
    long_description = models.TextField(_('Long Description'))
    abstract = models.TextField(_('Abstract'),
                                help_text=_('Short idea of the talk (Two or three sentences)'))
    justification = models.TextField(_('Justification'), blank=True, null=True,
                                     help_text=_('Why do you reject this proposal?'))
    room = models.ForeignKey(Room, verbose_name=_('Room'),
                             blank=True, null=True)
    start_date = models.DateTimeField(_('Start Time'), blank=True, null=True)
    end_date = models.DateTimeField(_('End Time'), blank=True, null=True)
    activity_type = models.ForeignKey(ActivityType, verbose_name=_('Activity Type'))
    speakers_names = models.CharField(_('Speakers Names'), max_length=600,
                                      help_text=_("Comma separated speaker names"))
    speaker_bio = models.TextField(
        _('Speaker Bio'), null=True,
        help_text=_('Tell us about you (we will use it as your "bio" in our website)'))
    labels = models.CharField(_('Labels'), max_length=200,
                              help_text=_('Comma separated tags. i.e. Linux, \
                                          Free Software, Devuan'))
    presentation = models.FileField(_('Presentation'),
                                    upload_to='talks', blank=True, null=True,
                                    help_text=_('Material you are going to use \
                                                for the talk (optional, but recommended)'))
    level_choices = (
        ('1', _('Beginner')),
        ('2', _('Medium')),
        ('3', _('Advanced')),
    )
    level = models.CharField(_('Level'), choices=level_choices, max_length=100,
                             help_text=_("Talk's Technical level"))
    additional_info = models.TextField(_('Additional Info'),
                                       blank=True, null=True,
                                       help_text=_('Info you consider relevant \
                                                   to the organizer, special \
                                                   activity requirements, etc.'))

    status_choices = (
        ('1', _('Proposal')),
        ('2', _('Accepted')),
        ('3', _('Rejected')),
    )

    status = models.CharField(_('Status'), choices=status_choices, max_length=20,
                              help_text=_('Activity proposal status'))

    image = ImageCropField(upload_to='images_thumbnails',
                           verbose_name=_('Image'), blank=True, null=True)
    cropping = ImageRatioField('image', '700x450', size_warning=True,
                               verbose_name=_('Cropping'), free_crop=True,
                               help_text=_('The image must be 700x450 px. You can crop it here.'))

    is_dummy = models.BooleanField(_('Is a dummy Activity?'), default=False,
                                   help_text=_('A dummy activity is used for example for coffee \
                                               breaks. We use this to exclude it from the index \
                                               page and other places'))

    def __cmp__(self, other):
        return -1 if self.start_date.time() < other.start_date.time() else 1

    def __str__(self):
        return '{} - {}'.format(self.event, self.title)

    def get_absolute_url(self):
        return reverse('activity_detail', args=(self.event.event_slug, self.pk))

    def get_schedule_info(self):
        schedule_info = {
            'resourceId': self.room.pk,
            'start': self.start_date.isoformat(),
            'end': self.end_date.isoformat(),
            'title': self.title,
            'id': self.pk,
            'url': ""
        }

        if not self.is_dummy:
            schedule_info['url'] = self.get_absolute_url()

        return schedule_info

    def schedule(self):
        if self.start_date and self.end_date:
            date = date_format(self.start_date, format='SHORT_DATE_FORMAT', use_l10n=True)
            return "{} - {} - {}".format(
                self.start_date.strftime("%H:%M"), self.end_date.strftime("%H:%M"), date)
        return _('Schedule not confirmed yet')

    @classmethod
    def check_status(cls, message, error=None, request=None):
        if error:
            raise ValidationError(message)
        if request:
            messages.error(request, message)

    # pylint: disable=too-many-arguments
    @classmethod
    def room_available(cls, request, proposal,
                       event_slug, event_date,
                       error=False):
        activities_room = Activity.objects.filter(
            room=proposal.room, event__event_slug=event_slug, start_date__date=event_date)
        if proposal.start_date == proposal.end_date:
            message = _("The talk couldn't be registered because the schedule is not \
                        available (starts and ends at the same time)")
            cls.check_status(message, error=error, request=request)
            return False
        if proposal.end_date < proposal.start_date:
            message = _("The talk couldn't be registered because the schedule is not \
                        available (starts after it finishes)")
            cls.check_status(message, error=error, request=request)
            return False
        one_second = datetime.timedelta(seconds=1)
        if activities_room.filter(
                end_date__range=(
                    proposal.start_date + one_second, proposal.end_date - one_second)) \
                .exclude(pk=proposal.pk).exists() \
                or activities_room.filter(
                    end_date__gt=proposal.end_date,
                    start_date__lt=proposal.start_date).exclude(pk=proposal.pk).exists() \
                or activities_room.filter(
                    start_date__range=(
                        proposal.start_date + one_second, proposal.end_date - one_second)) \
                    .exclude(pk=proposal.pk).exists() \
                or activities_room.filter(
                    end_date=proposal.end_date,
                    start_date=proposal.start_date).exclude(pk=proposal.pk).exists():
            message = _("The talk couldn't be registered because the \
                            room or the schedule is not available")
            cls.check_status(message, error=error, request=request)
            return False
        return True

    class Meta:
        ordering = ['title']
        verbose_name = _('Activity')
        verbose_name_plural = _('Activities')


class InstallationManager(models.Manager):
    @staticmethod
    def get_counts(queryset):
        hardware_count = count_by(
            queryset, lambda activity: activity.hardware.model)
        software_count = count_by(
            queryset, lambda activity: activity.software.name)
        installer_count = count_by(
            queryset, lambda activity: activity.installer.id)
        total = queryset.count()
        return {
            'hardware_count': hardware_count,
            'software_count': software_count,
            'installer_count': installer_count,
            'total': total
        }

    def get_counts_by_event(self, event):
        queryset = Installation.objects.filter(installer__event=event)
        return self.get_counts(queryset)


class Installation(models.Model):
    objects = InstallationManager()
    created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
    updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
    hardware = models.ForeignKey(Hardware, verbose_name=_('Hardware'),
                                 blank=True, null=True)
    software = models.ForeignKey(Software, verbose_name=_('Software'),
                                 blank=True, null=True)
    attendee = models.ForeignKey(Attendee, verbose_name=_('Attendee'),
                                 help_text=_('The owner of the installed hardware'))
    installer = models.ForeignKey(EventUser, verbose_name=_('Installer'),
                                  related_name='installed_by', blank=True,
                                  null=True)
    notes = models.TextField(_('Notes'), blank=True, null=True,
                             help_text=_('Info or trouble you \
                                         consider relevant to document'))

    def __str__(self):
        return '{}, {}, {}'.format(self.attendee, self.hardware, self.software)

    class Meta:
        verbose_name = _('Installation')
        verbose_name_plural = _('Installations')


class EventolSetting(models.Model):
    background = models.ImageField(
        null=True, blank=True,
        help_text=_("A image to show in the background of"))
    logo_header = models.ImageField(
        null=True, blank=True,
        help_text=_("This logo will be shown in the right corner of the page"))
    logo_landing = models.ImageField(
        null=True, blank=True,
        help_text=_("Logo to show in the center of the page"))
    message = models.TextField(max_length=280, null=True, blank=True,
                               help_text=_("A message to show in the center of the page"))

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj

    def __str__(self):
        return 'eventoL configuration'

    def save(self, *args, **kwargs):
        self.pk = 1
        super(EventolSetting, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        pass

    class Meta:
        verbose_name = _('eventoL setting')
        verbose_name_plural = _('eventoL settings')