class Distance(TimestampMixin, models.Model):
    competition = models.ForeignKey('core.Competition')
    name = models.CharField(max_length=100)
    distance_text = models.CharField(max_length=50, blank=True)
    distance_m = models.IntegerField(blank=True, null=True, help_text='Distance in meters')
    can_have_teams = models.BooleanField(default=True)
    have_results = models.BooleanField(default=True)

    profile_price = models.DecimalField(max_digits=20, decimal_places=2, blank=True, null=True)
    kind = models.CharField(max_length=2, blank=True)  # We need kind to map distances between competitions

    class Meta:
        order_with_respect_to = 'competition'

    def __str__(self):
        return self.name

    def get_participants(self, competition_ids):
        return self.participant_set.filter(competition_id__in=competition_ids)

    def get_teams(self):
        return self.team_set.all() class Competition(MPTTModel):
    KINDS = XChoices((0, 'velo', _('Velo')),
                    (1, 'cross_country', _('Cross Country')), )

    name = models.CharField(max_length=100)
    alias = AutoSlugField(populate_from='name', always_update=True)
    short_name = models.CharField(max_length=50, blank=True)

    created_by = models.ForeignKey('core.User', related_name='created_%(class)s_set', null=True, blank=True)
    modified_by = models.ForeignKey('core.User', related_name='modified_%(class)s_set', null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    place_name = models.CharField(max_length=50, blank=True)
    competition_date = models.DateField(blank=True, null=True)
    competition_date_till = models.DateField(blank=True, null=True)

    kind = models.SmallIntegerField(choices=KINDS, default=KINDS.velo)

    complex_payment_enddate = models.DateTimeField(blank=True, null=True)
    complex_payment_hideon = models.DateTimeField(blank=True, null=True)
    complex_discount = models.SmallIntegerField(default=0)

    bill_series = models.CharField(max_length=20, blank=True, default='B')
    payment_channel = models.CharField(max_length=20, default='LKDF')

    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')

    # class path that will do processing tasks. Example for result creation.
    processing_class = models.CharField(max_length=100, blank=True)

    legacy_id = models.IntegerField(blank=True, null=True)

    sitetree = models.ForeignKey('sitetree.TreeItem', blank=True, null=True, on_delete=models.SET_NULL)

    is_in_menu = models.BooleanField(default=False)

    skin = models.CharField(max_length=50, blank=True)
    logo = ThumbnailerImageField(upload_to=_get_logo_upload_path, blank=True,)
    logo_svg = models.FileField(upload_to=_get_logo_upload_path, blank=True)

    apply_image = ThumbnailerImageField(upload_to=_get_logo_upload_path, blank=True, )

    params = JSONField(blank=True, null=True)

    sms_text = models.CharField(max_length=255, blank=True)

    map_url = models.URLField(blank=True)

    frontpage_ordering = models.PositiveSmallIntegerField(default=0)

    description = models.TextField(blank=True)

    use_official_team_results = models.BooleanField(default=True)

    is_individual = models.BooleanField(default=False, help_text="If the stage should have individual start list")

    class MPTTMeta:
        order_insertion_by = ['competition_date', 'id']

    def __str__(self):
        return self.name

    @property
    def params_dict(self):
        if isinstance(self.params, dict):
            return self.params
        return json.loads(self.params)

    def get_ids(self):
        if self.level == 2:
            return self.id, self.parent_id
        else:
            return self.id,

    def get_all_children_ids(self):
        if self.level == 2:
            return tuple(obj.id for obj in Competition.objects.filter(parent_id=self.parent_id))
        elif self.get_root().id == 1:
            return tuple(obj.id for obj in Competition.objects.filter(parent_id=self.id))
        else:
            return self.id,

    def get_distances(self):
        return Distance.objects.filter(competition_id__in=self.get_ids()).select_related('competition')

    def get_insurances(self):
        insurances = Insurance.objects.filter(competition_id__in=self.get_ids())
        if self.level == 1 and self.get_root().id == 1:
            insurances = insurances.filter(in_complex=True)
        return insurances.select_related('competition')

    @property
    def get_full_name(self):
        if self.level == 2:
            return '%s - %s' % (self.parent.name, self.name)
        else:
            return self.name

    def get_logo(self):
        if self.level == 2:
            return self.parent.logo
        else:
            return self.logo

    @property
    def competition_header(self):
        if not self.skin:
            return None
        return "core/competition_header/%s.html" % self.skin

    @property
    def is_past_due(self):
        if not self.competition_date and self.competition_date_till:
            if datetime.date.today() < self.competition_date_till:
                return False
            else:
                return True

        if datetime.date.today() >= self.competition_date:
            return True
        return False

    def get_random_image(self):
        from velo.gallery.models import Photo
        photo = Photo.objects.filter(album__competition__tree_id=self.tree_id, is_featured=False, album__gallery_date__year__in=[timezone.now().year, timezone.now().year - 1]).order_by('?').first()
        if photo:
            return photo
        else:
            return Photo.objects.get(id=73105)

    @property
    def is_application_active(self):
        if (self.complex_payment_enddate and self.complex_payment_enddate > timezone.now()) or self.price_set.active().exists():
            return True
        return False

    def get_absolute_url(self):
        return reverse('competition:competition', args=[self.id]) def get_total(competition, distance_id, year, insurance_id=None):
    participant_fee = get_participant_fee(competition, distance_id, year)
    if not participant_fee:
        return None
    if insurance_id:
        insurance_fee = get_insurance_fee(competition, insurance_id)
        ret = dict(participant_fee)
        ret.update(insurance_fee)
        ret.update({'total': participant_fee.get('entry_fee') + insurance_fee.get('insurance_fee')})
        return ret
    else:
        participant_fee.update({'total': participant_fee.get('entry_fee')})
        return participant_fee class ApplicationCreateForm(RequestKwargModelFormMixin, forms.ModelForm):
    class Meta:
        model = Application
        fields = ('competition',)
        widgets = {
            'competition': CompetitionWidget,
        }

    def __init__(self, *args, **kwargs):
        super(ApplicationCreateForm, self).__init__(*args, **kwargs)

        now = timezone.now()
        competitions = Competition.objects.filter(Q(complex_payment_enddate__gt=now) | Q(price__end_registering__gt=now,
                                                                                         price__start_registering__lte=now)).defer("params").distinct().order_by(
            'complex_payment_enddate', 'competition_date')

        if not self.request.GET.get('all', None):
            competitions = competitions.exclude(complex_payment_hideon__lt=now)

        self.fields['competition'].choices = [(c.id, c) for c in competitions]

    def save(self, commit=True):
        object = super(ApplicationCreateForm, self).save(commit=False)
        if self.request.user.is_authenticated():
            object.email = self.request.user.email
            object.created_by = self.request.user
        if commit:
            object.save()
        return object class ApplicationUpdateForm(GetClassNameMixin, CleanEmailMixin, RequestKwargModelFormMixin, forms.ModelForm):
    email2 = forms.EmailField(label=_('E-mail confirmation'),
                              widget=forms.TextInput(attrs={
                                  "class": "input-field if--50 if--dark js-placeholder-up",
                                  "data-rule-required": True,
                                  "data-rule-email": True,
                                  "data-rule-equalto": "#id_email",
                                  "data-msg-required": _("This field is required."),
                                  "data-msg-email": _("Please enter valid email address!"),
                                  "data-msg-equalto": _("Emails doesn't match"),
                              }))

    error_css_class = 'error'

    class Meta:
        model = Application
        fields = ('email', 'can_send_newsletters')
        widgets = {
            'email': forms.TextInput(attrs={
                'class': "input-field if--50 if--dark  js-placeholder-up",
                "data-rule-required": True,
                "data-rule-email": True,
                "data-msg-required": _("This field is required."),
                "data-msg-email": _("Please enter valid email address!"),
            }),
        }

    def clean_email2(self):
        email1 = self.cleaned_data.get('email', '')
        email2 = self.cleaned_data.get('email2', '')
        if email1 and email2 and email1 != email2:
            raise forms.ValidationError(
                _("The two email fields didn't match."),
                code='email_mismatch',
            )
        return email2

    def __init__(self, *args, **kwargs):
        super(ApplicationUpdateForm, self).__init__(*args, **kwargs)
        self.fields['email'].required = True
        self.fields['email2'].initial = self.instance.email

    def save(self, commit=True):
        if self.cleaned_data.get('can_send_newsletters') and settings.MAIN_LIST_ID and commit:
            subscribe.delay(self.cleaned_data.get('email', ''))
        return super().save(commit) class ParticipantInlineForm(RequestKwargModelFormMixin, forms.ModelForm):
    insurance = forms.ChoiceField(required=False, choices=(), label=_('Insurance'))
    application = None
    error_css_class = 'error'

    class Meta:
        model = Participant
        fields = ('insurance', 't_shirt_size',
            'distance', 'first_name', 'last_name', 'country', 'ssn', 'birthday', 'gender', 'phone_number',
            "bike_brand", "bike_brand2",
            'team_name', 'email', 'city', 'survey_answer1')
        widgets = {
            'birthday': SplitDateWidget,
        }

    def has_changed(self):
        if not self.instance.price:
            return True
        return super().has_changed()

    def clean_ssn(self):
        insurance = self.cleaned_data.get('insurance', "")
        if not insurance:
            return ''
        if self.cleaned_data.get('country') == 'LV':
            return self.cleaned_data.get('ssn', '').replace('-', '').replace(' ', '')
        else:
            return self.cleaned_data.get('ssn', '')

    def clean_insurance(self):
        insurance = self.cleaned_data.get('insurance', "")
        if insurance != "":
            return self.application.competition.get_insurances().filter(status=Insurance.STATUS_ACTIVE).get(
                id=insurance)
        return None

    def clean_birthday(self):
        return self.cleaned_data.get('birthday')

    def clean_team_name(self):
        return self.cleaned_data.get('team_name').strip()

    # def clean_bike_brand(self):
    #     return self.cleaned_data.get('bike_brand')

    def clean_first_name(self):
        return self.cleaned_data.get('first_name').title()

    def clean_last_name(self):
        return self.cleaned_data.get('last_name').title()

    def clean_bike_brand2(self):
        if not hasattr(self.cleaned_data.get('bike_brand'), "title"):
            return ""
        elif self.cleaned_data.get('bike_brand').title == 'Cits':
            return self.cleaned_data.get('bike_brand2')
        else:
            if self.cleaned_data.get('bike_brand') is not None:
                return self.cleaned_data.get('bike_brand')
            else:
                return ''

    def clean(self):

        cleaned_data = self.cleaned_data

        ssn = cleaned_data.get('ssn', '')
        country = cleaned_data.get('country')
        birthday = cleaned_data.get('birthday', '')
        distance = cleaned_data.get('distance', '')
        insurance = cleaned_data.get('insurance', None)

        if insurance:
            if country == 'LV':
                try:
                    if not ssn or not len(ssn) == 11:
                        self._errors.update({'ssn': [_("Invalid Social Security Number."), ]})
                    checksum = 1
                    for i in range(10):
                        checksum -= int(ssn[i]) * int("01060307091005080402"[i * 2:i * 2 + 2])
                    if not int(checksum - math.floor(checksum / 11) * 11) == int(ssn[10]):
                        self._errors.update({'ssn': [_("Invalid Social Security Number."), ]})
                except:
                    self._errors.update({'ssn': [_("Invalid Social Security Number."), ]})
            elif not ssn:
                self._errors.update({'ssn': [_("SSN is required."), ]})

        if birthday and distance and self.instance.application.payment_status == self.application.PAY_STATUS.not_payed:
            total = get_total(self.instance.application.competition, distance.id, birthday.year,
                              insurance.id if insurance else None)
            if not total:
                self._errors.update({'distance': [_("This distance not available for this participant."), ]})

        return cleaned_data

    def save(self, commit=True):
        obj = super(ParticipantInlineForm, self).save(commit=False)

        obj.competition = self.application.competition
        obj.insurance = self.cleaned_data.get('insurance')

        if obj.birthday and obj.distance and self.application.payment_status == self.application.PAY_STATUS.not_payed:
            total = get_total(obj.competition, obj.distance_id, obj.birthday.year, obj.insurance_id)
            if total:
                obj.price = total.get('price_obj', None)
            else:
                obj.price = None

        if commit:
            obj.save()

        return obj

    def __init__(self, *args, **kwargs):
        self.application = kwargs.pop('application', None)
        super(ParticipantInlineForm, self).__init__(*args, **kwargs)

        competition = self.application.competition

        distances = competition.get_distances()
        insurances = competition.get_insurances().filter(status=Insurance.STATUS_ACTIVE)

        if insurances:
            self.fields['insurance'].choices = [('', _("Select Insurance"))] + [(insurance.id, str(insurance)) for
                                                                                insurance
                                                                                in insurances]

            if self.instance.insurance_id:
                self.fields['insurance'].initial = self.instance.insurance_id
            elif not self.fields['insurance'].initial and insurances[0].price == 0.0:
                self.fields['insurance'].initial = insurances[0].id

            self.fields['insurance'].help_text = mark_safe("<a href='#modal-insurance-%i'>%s</a>" % (insurances[0].insurance_company_id, _('Insurance terms')))

        else:
            self.fields['insurance'].widget = forms.HiddenInput()

        if competition.id in (79, 80):
            self.fields['survey_answer1'].widget = forms.Select(choices=[('', '??'), ('Yes', _('Yes')), ('No', _('No'))])
            self.fields['survey_answer1'].label = _("Want to participate in Roc d'Azur draw?")
            self.fields['survey_answer1'].help_text = mark_safe("<a href='#modal-rock-d-azur'>%s</a>" % _('More information'))
        else:
            self.fields['survey_answer1'].widget = forms.HiddenInput()
            self.fields['survey_answer1'].label = ""

        if competition.pk == 1198:
            self.fields['t_shirt_size'].label += " (+30€)"
            self.fields['t_shirt_size'].help_text = mark_safe(
                "<a href='#modal-tshirt-size'>{}</a>".format(_('T-shirt design')))
        else:
            self.fields['t_shirt_size'].widget = forms.HiddenInput()
            self.fields['t_shirt_size'].label = ""
        self.fields['t_shirt_size'].required = False

        self.fields['distance'].choices = [('', _("Select Distance"))] + [(distance.id, str(distance)) for distance in
                                                                          distances]

        if len(distances) == 1:
            self.fields['distance'].initial = distances[0].id

        if get_language() == 'lv':
            self.fields['country'].initial = 'LV'

        self.fields['team_name'].widget.attrs.update({'class': 'team-typeahead'})
        self.fields['team_name'].initial = competition.params_dict.get('default_team', "")
        self.fields['team_name'].help_text = competition.params_dict.get('default_team_help_%s' % get_language(),
                                                                         competition.params_dict.get(
                                                                             'default_team_help', ""))

        for field_name in ['distance', 'first_name', 'last_name', 'gender', 'country', 'birthday']:
            self.fields[field_name].required = True

        self.fields['gender'].choices = Participant.GENDER_CHOICES
        self.fields['gender'].choices.insert(0, ('', _("Select Gender")), )

        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.include_media = False
        self.helper.template = "registration/form/participant_inline.html"
        self.helper.layout = Layout() class ParticipantInlineRestrictedForm(ParticipantInlineForm):
    def __init__(self, *args, **kwargs):
        super(ParticipantInlineRestrictedForm, self).__init__(*args, **kwargs)

        ro_fields = ('first_name', 'last_name', 'distance', 'insurance', 'birthday', 'ssn', 'country')

        for field in ro_fields:
            if field in self.fields:
                self.fields[field].widget.attrs['readonly'] = True

        self.helper.template = "registration/form/participant_inline.html"

    def clean_first_name(self):
        return self.instance.first_name

    def clean_last_name(self):
        return self.instance.last_name

    def clean_distance(self):
        return self.instance.distance

    def clean_insurance(self):
        return self.instance.insurance

    def clean_birthday(self):
        return self.instance.birthday

    def clean_ssn(self):
        return self.instance.ssn

    def clean_country(self):
        return self.instance.country class ParticipantInlineFullyRestrictedForm(ParticipantInlineRestrictedForm):
    def __init__(self, *args, **kwargs):
        super(ParticipantInlineFullyRestrictedForm, self).__init__(*args, **kwargs)
        ro_fields = ('gender', 'team_name', 'phone_number', 'email')

        for field in ro_fields:
            if field in self.fields:
                self.fields[field].widget.attrs['readonly'] = True

    def clean_gender(self):
        return self.instance.gender

    def clean_team_name(self):
        return self.instance.team_name

    def clean_phone_number(self):
        return self.instance.phone_number

    def clean_email(self):
        return self.instance.email class CompanyApplicationCreateForm(GetClassNameMixin, CleanEmailMixin, RequestKwargModelFormMixin, forms.ModelForm):
    change_public_url = forms.BooleanField(label=_("Reset public URL?"), help_text=_(
        'If you reset URL, then nobody will be able to access using previous URL.'), required=False)

    class Meta:
        model = CompanyApplication
        fields = ('competition', 'team_name', 'email', 'description',)

    def save(self, *args, **kwargs):
        if not self.instance.id:
            self.instance.created_by = self.request.user
            self.instance.status = 1  # Active
        self.instance.modified_by = self.request.user

        if self.cleaned_data.get('change_public_url', False):
            self.instance.code = str(uuid.uuid4())

        return super(CompanyApplicationCreateForm, self).save(*args, **kwargs)

    def clean_competition(self):
        competition = self.cleaned_data.get('competition')
        if self.instance.id:
            return self.instance.competition
        else:
            return competition

    def __init__(self, *args, **kwargs):
        super(CompanyApplicationCreateForm, self).__init__(*args, **kwargs)

        self.fields['email'].initial = self.request.user.email

        competitions = Competition.objects.filter(is_in_menu=True).exclude(competition_date__lt=timezone.now())
        self.fields['competition'].choices = [(c.id, c.get_full_name) for c in competitions]

        if self.instance.id:
            competitions = competitions.filter(id=self.instance.competition_id)
            if not competitions:
                c = self.instance.competition
                self.fields['competition'].choices += [(c.id, c.get_full_name), ]
            self.fields['competition'].widget.attrs['readonly'] = True
        else:
            self.fields['change_public_url'].widget = forms.HiddenInput()

        self.helper = FormHelper()
        self.helper.form_tag = True
        self.helper.include_media = False
        self.helper.layout = Layout(
            Row(
                Column(
                    'competition',
                    'email',
                    'team_name',
                    'description',
                    css_class='col-sm-6'
                ),
                Column(
                    'change_public_url',
                    css_class='col-sm-6'
                ),
            ),
            Row(
                Column(Submit('submit', _('Save')), css_class='col-sm-2'),
            ),
        ) class CompanyParticipantInlineForm(RequestKwargModelFormMixin, forms.ModelForm):
    application = None

    class Meta:
        model = CompanyParticipant
        fields = (
            'distance', 'first_name', 'last_name', 'country', 'ssn', 'birthday', 'gender', 'phone_number',
            'bike_brand', "bike_brand2",
            'email')
        widgets = {
            'birthday': SplitDateWidget,
        }

    def clean_ssn(self):
        if self.cleaned_data.get('country') == 'LV':
            return self.cleaned_data.get('ssn', '').replace('-', '').replace(' ', '')
        else:
            return ''

    def clean_birthday(self):
        return self.cleaned_data.get('birthday')

    # def clean_bike_brand(self):
    #     return self.cleaned_data.get('bike_brand')

    def clean_bike_brand2(self):
        if not hasattr(self.cleaned_data.get('bike_brand'), "title"):
            return ""
        elif self.cleaned_data.get('bike_brand').title == 'Cits':
            return self.cleaned_data.get('bike_brand2')
        else:
            if self.cleaned_data.get('bike_brand') is not None:
                return self.cleaned_data.get('bike_brand')
            else:
                return ''

    def clean_first_name(self):
        return self.cleaned_data.get('first_name').title()

    def clean_last_name(self):
        return self.cleaned_data.get('last_name').title()

    def clean(self):
        cleaned_data = self.cleaned_data

        birthday = cleaned_data.get('birthday', None)
        distance = cleaned_data.get('distance', None)

        if birthday and distance:

            competition = self.instance.application.competition
            if competition.complex_payment_enddate:
                competition = competition.get_children().filter(competition_date__gte=timezone.now())
                if competition:
                    competition = competition[0]

            prices = competition.price_set.filter(till_year__gte=birthday.year,
                                                  from_year__lte=birthday.year, distance=distance)
            if not prices:
                self._errors.update({'distance': [_("This distance not available for this participant."), ]})

        return cleaned_data

    def __init__(self, *args, **kwargs):
        self.application = kwargs.pop('application', None)
        super(CompanyParticipantInlineForm, self).__init__(*args, **kwargs)

        competition = self.application.competition

        distances = competition.get_distances()

        self.fields['distance'].choices = [('', '------')] + [(distance.id, str(distance)) for distance in
                                                              distances]

        if get_language() == 'lv':
            self.fields['country'].initial = 'LV'

        self.fields['country'].required = True
        self.fields['gender'].required = True
        self.fields['birthday'].required = True
        self.fields['distance'].required = True
        self.fields['first_name'].required = True
        self.fields['last_name'].required = True
        self.fields['gender'].required = True
        self.fields['country'].required = True

        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.include_media = False
        self.helper.template = "registration/form/participant_inline.html"
        self.helper.layout = Layout() class CompanyApplicationEmptyForm(GetClassNameMixin, CleanEmailMixin, RequestKwargModelFormMixin, forms.ModelForm):
    class Meta:
        model = CompanyApplication
        fields = ()

    def get_app_label(self):
        return "registration/application" class Participant(TimestampMixin, models.Model):
    GENDER_CHOICES = (
        ('M', _('Male')),
        ('F', _('Female')),
    )
    TSHIRT_SIZE_CHOICES = (
        ('XXS', _('UNISEX XXS')), ('XS', _('UNISEX XS')), ('S', _('UNISEX S')), ('M', _('UNISEX M')),
        ('L', _('UNISEX L')), ('XL', _('UNISEX XL')), ('XXL', _('UNISEX XXL')), ('XXL', _('UNISEX XXL'))
    )
    application = models.ForeignKey(Application, blank=True, null=True)
    competition = models.ForeignKey('core.Competition')
    distance = models.ForeignKey('core.Distance', blank=True, null=True)
    price = models.ForeignKey('payment.Price', blank=True, null=True)

    code_short = models.CharField(max_length=20, blank=True, db_index=True)

    company_participant = models.ForeignKey('registration.CompanyParticipant', blank=True, null=True)

    discount_amount = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)

    insurance = models.ForeignKey('core.Insurance', blank=True, null=True)

    team = models.ForeignKey('team.Team', blank=True, null=True)  # If participant is in official team, then this is filled
    team_name = models.CharField(_('Team'), max_length=50, blank=True)  # If participant is not within official team, then he can fill this field
    team_name_slug = models.SlugField(blank=True)

    is_participating = models.BooleanField(_('Is Participating'), default=False)
    is_paying = models.BooleanField(_('Is Paying'), default=True)
    is_competing = models.BooleanField(_('Is Competing'), default=True)  # in case this cyclist is participating for fun, then is_competing should be set to false.
    is_shown_public = models.BooleanField(_('Is Shown Publicly'), default=True)  # if participant doesn't want to be seen publicly in website, this should be set to false.

    first_name = models.CharField(_('First Name'), max_length=60, blank=True)
    last_name = models.CharField(_('Last Name'), max_length=60, blank=True)
    birthday = models.DateField(_('Birthday'), blank=True, null=True)
    is_only_year = models.BooleanField(default=False)
    slug = models.SlugField(blank=True)

    gender = models.CharField(_('Gender'), max_length=1, choices=GENDER_CHOICES, blank=True)

    ssn = models.CharField(_('Social Security Number'), max_length=12, blank=True)
    ssn_hashed = models.CharField(_('Social Security Number Hashed'), max_length=44, blank=True)

    phone_number = models.CharField(_('Phone Number'), max_length=60, blank=True, help_text=_("Result will be sent to this phone number"))
    email = models.EmailField(_('Email'), blank=True)

    send_email = models.BooleanField(_('Send Email'), default=True)
    send_sms = models.BooleanField(_('Send SMS'), default=True)

    country = CountryField(_('Country'), blank=True, null=True)
    city = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.city}, verbose_name=_('City'), blank=True, null=True)

    bike_brand = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.bike_brand}, verbose_name=_('Bike Brand'), blank=True, null=True)
    bike_brand2 = models.CharField(_('Bike Brand'), max_length=20, blank=True, help_text=_("Other bike brand"))

    occupation = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.occupation}, verbose_name=_('Occupation'), blank=True, null=True)
    where_heard = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.heard}, verbose_name=_('Where Heard'), blank=True, null=True)

    group = models.CharField(_('Group'), max_length=50, blank=True, help_text=_('Assigned automatically'))  # moved group away from results

    registrant = models.ForeignKey('core.User', blank=True, null=True, verbose_name=_('Registrant'))
    legacy_id = models.IntegerField(blank=True, null=True)

    full_name = models.CharField(_('Full Name'), max_length=120, blank=True)

    primary_number = models.ForeignKey('registration.Number', blank=True, null=True)

    is_temporary = models.BooleanField(default=False)
    is_sent_number_email = models.BooleanField(default=False)
    is_sent_number_sms = models.BooleanField(default=False)

    registration_dt = models.DateTimeField(_("Registration Date"), blank=True, null=True)

    comment = models.TextField(blank=True)

    total_entry_fee = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    total_insurance_fee = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    final_price = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)

    survey_answer1 = models.CharField(max_length=20, blank=True)

    t_shirt_size = models.CharField(_('T-Shirt size'), max_length=3, choices=TSHIRT_SIZE_CHOICES, blank=False, null=True)

    _competition_class = None

    class Meta:
        ordering = ('distance', 'created')
        verbose_name = _('participant')
        verbose_name_plural = _('participants')

    def __str__(self):
        return '%s %s - %s %s' % (self.first_name, self.last_name, self.competition, self.distance)

    def get_competition_class(self):
        if not self._competition_class:
            class_ = load_class(self.competition.processing_class)
            self._competition_class = class_(self.competition.id)
        return self._competition_class

    def set_slug(self):
        if self.birthday:
            try:
                self.slug = CustomSlug.objects.get(first_name=self.first_name, last_name=self.last_name, birthday=self.birthday).slug
            except CustomSlug.DoesNotExist:
                self.slug = slugify('%s-%s-%i' % (self.first_name, self.last_name, self.birthday.year), only_ascii=True)
        else:
            self.slug = slugify('%s-%s' % (self.first_name, self.last_name), only_ascii=True)

    def set_group(self):
        if not self.group and self.is_participating:
            self.group = self.get_competition_class().assign_group(self.distance_id, self.gender, self.birthday, participant=self)

    def save(self, *args, **kwargs):
        prev_participant = None
        if self.pk:
            prev_participant = Participant.objects.get(id=self.pk)

        self.full_name = '%s %s' % (self.first_name, self.last_name)  # Full name always should be based on first_name and last_name fields

        self.team_name_slug = slugify(self.team_name.replace(' ', ''))

        # In case first name, last name or birthday is changed, then slug changes as well.
        old_slug = self.slug
        self.set_slug()
        if old_slug != self.slug and self.is_participating:
            Log.objects.create(content_object=self, action="Changed SLUG", message="Changing on number model", params={'old_slug':old_slug, 'new_slug': self.slug})
            self.numbers(slug=old_slug).update(participant_slug=self.slug)  # We update number slugs to match new slug

            if self.team:
                team = self.team
                self.team = None  # Remove connection to existing team member.

                # Because of slug change connection to team is disconnected.
                # After disconnection team points are recalculated on every stage team have participated.
                try:
                    member = team.member_set.get(slug=old_slug)
                    for appl in member.memberapplication_set.all():
                        class_ = load_class(appl.competition.processing_class)
                        processing_class = class_(competition=appl.competition)
                        processing_class.recalculate_team_result(team=team)
                        appl.participant = None
                        appl.participant_unpaid = None
                        appl.participant_potential = None
                        appl.save()
                except:
                    pass

            sebs = self.primary_sebstandings_set.filter(competition_id__in=self.competition.get_ids())
            if sebs:
                sebs.update(participant_slug=self.slug)
                # Check if there are double standings that should be merged
                from velo.results.tasks import merge_standings
                merge_standings.delay(self.competition.parent_id if self.competition.level == 2 else self.competition_id)

        self.set_group()

        if not self.primary_number and self.is_participating:
            numbers = self.numbers().order_by('-number')
            if numbers:
                self.primary_number = numbers[0]

        if not self.registration_dt:
            self.registration_dt = timezone.now()

        # Recalculate totals. # TODO: This should be done when creating payment, not on any save.
        if self.application:
            if self.application.payment_status == Application.PAY_STATUS.not_payed or not self.application.discount_code:
                recalculate_participant(self, commit=False)

        obj = super(Participant, self).save(*args, **kwargs)

        if not self.code_short and self.id:
            hashids = Hashids(salt=settings.SECRET_KEY, min_length=7, alphabet=settings.HASH_ALPHABET)
            self.code_short = hashids.encode(self.id)
            self.save()

        if self.is_participating and (not prev_participant or not prev_participant.is_participating):
            from velo.results.tasks import master_update_helper_result_table
            master_update_helper_result_table.delay(participant_id=self.id)

        if old_slug != self.slug and self.is_participating:
            from velo.team.utils import match_participant_to_applied
            match_participant_to_applied(self)

        return obj

    def numbers(self, slug=None):
        self.set_group()
        if not slug:
            slug = self.slug
        group = self.get_competition_class().get_group_for_number_search(self.distance_id, self.gender, self.birthday)
        number_queryset = Number.objects.filter(participant_slug=slug, competition_id__in=self.competition.get_ids(), distance=self.distance, group=group)

        return number_queryset class Application(TimestampMixin, models.Model):
    PAY_STATUS = XChoices((0, 'not_payed', _("Haven't Payed")),
                          (10, 'waiting', _('Waiting for Payment')),
                          (20, 'payed', _('Payed')),
                          (30, 'never', _('Won\'t pay')),
                          (-10, 'cancelled', _('Cancelled')), )

    competition = models.ForeignKey('core.Competition', verbose_name=_('Competition'))
    payment_status = models.SmallIntegerField(_('Payment Status'), default=PAY_STATUS.not_payed, choices=PAY_STATUS)
    discount_code = models.ForeignKey('payment.DiscountCode', blank=True, null=True)

    email = models.EmailField(blank=True)
    can_send_newsletters = models.BooleanField(_("I want to receive velo.lv news in email."), default=True)

    code = models.CharField(max_length=50, default=uuid.uuid4, unique=True)
    legacy_id = models.IntegerField(blank=True, null=True)

    company_name = models.CharField(_('Company name / Full Name'), max_length=100, blank=True)
    company_vat = models.CharField(_('VAT Number'), max_length=100, blank=True)
    company_regnr = models.CharField(_('Company number / SSN'), max_length=100, blank=True)
    company_address = models.CharField(_('Address'), max_length=100, blank=True)
    company_juridical_address = models.CharField(_('Juridical Address'), max_length=100, blank=True)
    invoice_show_names = models.BooleanField(_('Show participant names in invoice'), default=True)

    external_invoice_code = models.CharField(_('Invoice code'), max_length=100, blank=True)  # invoice code from e-rekins used to allow downloading invoice from velo.lv
    external_invoice_nr = models.CharField(_('Invoice Number'), max_length=20, blank=True)  # invoice number from e-rekins used in card payment
    invoice = models.ForeignKey('payment.Invoice', null=True, blank=True)

    donation = models.DecimalField(_('Donation'), max_digits=20, decimal_places=2, default=0.0)

    # Totals
    total_discount = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    total_entry_fee = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    total_insurance_fee = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    final_price = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)

    params = JSONField(default={})

    language = models.CharField(max_length=10, choices=LANGUAGES, default='lv')

    payment_set = GenericRelation(Payment)

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

    def set_final_price(self):
        if self.discount_code:
            _class = load_class(self.discount_code.campaign.discount_kind)
            discount = _class(application=self)
            price = discount.get_final_price_for_application()
            if isinstance(price, float):
                self.final_price = price + float(self.donation)
                return
            else:
                self.discount_code = None

        self.final_price = float(self.total_entry_fee) + float(self.total_insurance_fee) + float(self.donation)

    @property
    def competition_name(self):
        if self.competition.level == 2:
            return '%s - %s' % (self.competition.parent, self.competition)
        else:
            return str(self.competition) class CompanyApplication(StatusMixin, TimestampMixin, models.Model):
    competition = models.ForeignKey('core.Competition')
    code = models.CharField(max_length=50, default=uuid.uuid4, unique=True)
    team_name = models.CharField(max_length=50)

    email = models.EmailField(help_text=_("You will receive payment confirmation and information about start numbers."))
    description = models.TextField(_('Description'), blank=True) class CompanyParticipant(TimestampMixin, models.Model):
    GENDER_CHOICES = (
        ('M', _('Male')),
        ('F', _('Female')),
    )
    application = models.ForeignKey(CompanyApplication, related_name='participant_set')
    team_member = models.ForeignKey('team.Member', blank=True, null=True)
    is_participating = models.BooleanField(_('Is Participating'), default=False)

    distance = models.ForeignKey('core.Distance', blank=True, null=True)
    first_name = models.CharField(_('First Name'), max_length=60, blank=True)
    last_name = models.CharField(_('Last Name'), max_length=60, blank=True)
    birthday = models.DateField(_('Birthday'), blank=True, null=True)
    gender = models.CharField(_('Gender'), max_length=1, choices=GENDER_CHOICES, blank=True)
    ssn = models.CharField(_('Social Security Number'), max_length=12, blank=True)
    phone_number = models.CharField(_('Phone Number'), max_length=60, blank=True, help_text=_("Result will be sent to this phone number"))
    email = models.EmailField(_('Email'), blank=True)
    country = CountryField(_('Country'), blank=True, null=True)
    bike_brand2 = models.CharField(_('Bike Brand'), max_length=20, blank=True, help_text=_("Other bike brand"))
    bike_brand = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.bike_brand}, verbose_name=_('Bike Brand'), blank=True, null=True)


    slug = models.SlugField(blank=True)

    def set_slug(self):
        if self.birthday:
            try:
                self.slug = CustomSlug.objects.get(first_name=self.first_name, last_name=self.last_name, birthday=self.birthday).slug
            except CustomSlug.DoesNotExist:
                self.slug = slugify('%s-%s-%i' % (self.first_name, self.last_name, self.birthday.year), only_ascii=True)
        else:
            self.slug = slugify('%s-%s' % (self.first_name, self.last_name), only_ascii=True)

    def save(self, *args, **kwargs):
        self.set_slug()
        return super(CompanyParticipant, self).save(*args, **kwargs) class Team(StatusMixin, TimestampMixin, models.Model):
    distance = models.ForeignKey('core.Distance')
    title = models.CharField(_('Title'), max_length=100)
    slug = AutoSlugField(populate_from='title', always_update=True)

    description = models.TextField(_('Description'), blank=True)
    img = models.ImageField(_('Image'), upload_to=get_team_upload, blank=True)
    shirt_image = models.ImageField(_('Shirt Image'), upload_to=get_team_upload, blank=True)
    country = CountryField(_('Country'))

    contact_person = models.CharField(_('Contact Person'), max_length=100, blank=True)
    email = models.EmailField(_('Email'), blank=True)
    phone_number = models.CharField(_('Phone Number'), max_length=50, blank=True)
    show_contact_info = models.BooleanField(_("Show contact information in public team profile"), default=True)

    management_info = models.TextField(_('Management Info'), blank=True)

    is_featured = models.BooleanField(default=False)

    owner = models.ForeignKey('core.User')

    legacy_id = models.IntegerField(blank=True, null=True)

    # This is for invoice
    final_price = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    company_name = models.CharField(_('Company name / Full Name'), max_length=100, blank=True)
    company_vat = models.CharField(_('VAT Number'), max_length=100, blank=True)
    company_regnr = models.CharField(_('Company number / SSN'), max_length=100, blank=True)
    company_address = models.CharField(_('Address'), max_length=100, blank=True)
    company_juridical_address = models.CharField(_('Juridical Address'), max_length=100, blank=True)
    external_invoice_code = models.CharField(_('Invoice code'), max_length=100, blank=True)  # invoice code from e-rekins used to allow downloading invoice from velo.lv
    external_invoice_nr = models.CharField(_('Invoice Number'), max_length=20, blank=True)  # invoice number from e-rekins used in card payment
    invoice = models.ForeignKey('payment.Invoice', null=True, blank=True)
    is_w = models.BooleanField(_('Women team'), default=False, )

    class Meta:
        ordering = ('distance', '-is_featured', 'title')

    def __str__(self):
        return self.title

    @property
    def competition(self):
        return self.distance.competition class Member(StatusMixin, models.Model):
    GENDER_CHOICES = (
        ('M', _('Male')),
        ('F', _('Female')),
    )
    team = models.ForeignKey(Team)
    first_name = models.CharField(_('First Name'), max_length=60)
    last_name = models.CharField(_('Last Name'), max_length=60)
    birthday = models.DateField(_('Birthday'))
    slug = models.SlugField()
    ssn = models.CharField(_('SSN'), max_length=12, blank=True)
    phone_number = models.CharField(_('Phone Number'), max_length=60, blank=True,
                                    help_text=_("Result will be sent to this phone number"))

    gender = models.CharField(_('Gender'), max_length=1, choices=GENDER_CHOICES, blank=True)

    country = CountryField(_('Country'))
    license_nr = models.CharField(_('License NR'), max_length=50, blank=True)

    image = ThumbnailerImageField(_("Image"), blank=True, upload_to=get_team_upload)

    def __str__(self):
        return '%s %s %s' % (self.first_name, self.last_name, self.birthday)

    def save(self, *args, **kwargs):
        try:
            self.slug = CustomSlug.objects.get(first_name=self.first_name, last_name=self.last_name,
                                               birthday=self.birthday).slug
        except CustomSlug.DoesNotExist:
            self.slug = slugify('%s-%s-%i' % (self.first_name, self.last_name, self.birthday.year), only_ascii=True)
        return super(Member, self).save(*args, **kwargs)

    @property
    def full_name(self):
        return '%s %s' % (self.first_name, self.last_name) def load_class(full_class_string):
    """
    dynamically load a class from a string
    """

    class_data = full_class_string.split(".")
    module_path = ".".join(class_data[:-1])
    class_str = class_data[-1]

    module = importlib.import_module(module_path)
    # Finally, we retrieve the Class
    return getattr(module, class_str)
from django.contrib import messages
from django.contrib.auth import logout
from django.core.urlresolvers import reverse
from django.db.models import Q, Count, Sum, Max
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.views.generic import CreateView, ListView, View, DetailView, TemplateView, UpdateView
from django.utils.translation import ugettext_lazy as _, get_language
from django.conf import settings
from django_tables2 import SingleTableView
from extra_views import UpdateWithInlinesView, NamedFormsetsMixin, InlineFormSet
from braces.views import JsonRequestResponseMixin, JSONResponseMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from extra_views.advanced import BaseUpdateWithInlinesView
from velo.core.formsets import CustomBaseInlineFormSet, OnlyAddBaseInlineFormSet
from velo.core.models import Distance, Competition
from velo.payment.utils import get_total
from velo.registration.forms import ApplicationCreateForm, ApplicationUpdateForm, ParticipantInlineForm, \
    ParticipantInlineRestrictedForm, ParticipantInlineFullyRestrictedForm, CompanyApplicationCreateForm, \
    CompanyParticipantInlineForm, CompanyApplicationEmptyForm
from velo.registration.models import Participant, Application, CompanyApplication, CompanyParticipant
from velo.registration.tables import ParticipantTable, ApplicationTable, CompanyParticipantTable, \
    CompanyApplicationTable
from velo.team.models import Team, Member
from velo.velo.mixins.forms import GetClassNameMixin
from velo.velo.mixins.views import SetCompetitionContextMixin, RequestFormKwargsMixin, NeverCacheMixin
from velo.velo.utils import load_class




class ParticipantList(SetCompetitionContextMixin, SingleTableView):
    model = Participant
    table_class = ParticipantTable
    template_name = 'registration/participant_list.html'
    paginate_by = 100

    def get_table_class(self):
        return self.get_competition_class().get_startlist_table_class(self.distance)

    def get(self, *args, **kwargs):
        self.set_competition(kwargs.get('pk'))
        self.set_distances()  # Based on self.competition
        self.set_distance(self.request.GET.get('distance', None))

        return super().get(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        try:
            context.update({'groups': self.get_competition_class().groups.get(self.distance.id, ())})
        except:
            pass
        return context

    def get_queryset(self):
        queryset = super().get_queryset()
        queryset = queryset.filter(is_participating=True)

        if self.distance:
            queryset = queryset.filter(distance=self.distance)

        search = self.request.GET.get('search', None)
        if search:
            search_slug = slugify(search)
            queryset = queryset.filter(
                Q(slug__icontains=search_slug) | Q(primary_number__number__icontains=search_slug) | Q(
                    team_name__icontains=search.upper())).filter(is_shown_public=True)

        if self.request.GET.get('group', None):
            queryset = queryset.filter(group=self.request.GET.get('group', None))

        if self.competition.is_individual:
            queryset = queryset.filter(competition=self.competition)
        else:
            queryset = queryset.filter(competition_id__in=self.competition.get_ids())

        queryset = queryset.select_related('competition', 'distance', 'team', 'primary_number')

        if self.competition.id >= 38:
            queryset = queryset.filter(helperresults__competition_id=self.competition.id)
            queryset = queryset.extra(
                select={
                    'calculated_total': 'results_helperresults.calculated_total',
                    'passage_assigned': 'results_helperresults.passage_assigned',
                },
            )

        return queryset


class CompanyApplicationCreate(NeverCacheMixin, LoginRequiredMixin, RequestFormKwargsMixin, CreateView):
    template_name = 'registration/company_application_create.html'
    model = CompanyApplication
    form_class = CompanyApplicationCreateForm

    def get_success_url(self):
        return reverse('companyapplication', kwargs={'slug': self.object.code})


class CompanyApplicationUpdate(NeverCacheMixin, LoginRequiredMixin, RequestFormKwargsMixin, UpdateView):
    template_name = 'registration/company_application_create.html'
    model = CompanyApplication
    form_class = CompanyApplicationCreateForm
    slug_field = 'code'

    def get_queryset(self):
        queryset = super(CompanyApplicationUpdate, self).get_queryset()
        queryset = queryset.filter(created_by=self.request.user)
        return queryset

    def get_success_url(self):
        return reverse('companyapplication', kwargs={'slug': self.object.code})


class CompanyApplicationDetail(NeverCacheMixin, LoginRequiredMixin, SingleTableView):

    model = CompanyParticipant