class Log(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
    object_id = models.PositiveIntegerField(null=True, blank=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    user = models.ForeignKey(User, null=True, blank=True)
    action = models.CharField(max_length=50, blank=True)
    message = models.CharField(max_length=255, blank=True)
    params = JSONField(blank=True, null=True)

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def set_message(self, message, commit=True):
        self.message = message
        if commit:
            self.save()

    def set_object(self, obj, commit=True):
        self.content_object = obj
        if commit:
            self.save() 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]) class SMS(models.Model):
    phone_number = models.CharField(max_length=20)
    text = models.CharField(max_length=160)
    is_processed = models.BooleanField(default=False)
    response = models.TextField(blank=True)
    discount_code = models.ForeignKey('payment.DiscountCode', blank=True, null=True)
    send_out_at = models.DateTimeField()
    status = models.CharField(max_length=50, blank=True)

    class Meta:
        permissions = (
            ('can_update_marketing', 'Can update marketing'),
        ) def send_smses():
    smses = SMS.objects.filter(is_processed=False)[:100]
    for sms in smses:
        sms.response = send_sms_text2reach(sms.phone_number, sms.text)
        sms.is_processed = True
        sms.save() def send_sms_text2reach(number, msg):
    text2reach_api_key = getattr(settings, 'TEXT2REACH_BULK_API_KEY')

    msg = unicodedata.normalize('NFKD', msg).encode('ascii', 'ignore').decode('ascii')

    # Remove + from number as Text2Reach phone number can not start with +
    if str(number).startswith('+'):
        number = number[1:]

    if len(str(number)) == 8:
        number = "371%s" % number

    if settings.ENVIRONMENT_NAME in ('INT', 'DEV'):
        msg = number + ': ' + msg
        number = settings.TEXT2REACH_DEV_NUMBER

    url_params = {
        "api_key": text2reach_api_key,
        "phone": number,
        "from": "velo.lv",
        "message": msg,
        "unicode": 'false',

    }
    sms_url = 'https://api.text2reach.com/sms/send?%s' % urlencode(url_params)

    if settings.DEBUG:
        logger.info('Sent SMS %s to %s' % (msg, number))
        return '0001'

    try:
        r = requests.get(sms_url)
    except RequestException as e:
        # Log error
        logger.error('Error sending SMS to {0} using Text2Reach with error msg {1}'.format(
            number, str(e)
        ))
        return ''

    # If the message cannot be sent, the return value for msg_id
    # will contain a negative integer. Response code is also 200 in case of error
    if r.status_code != 200 or not r.text.isnumeric() or int(r.text) < 0:
        # Log error
        logger.error('Error sending SMS to {0} using Text2Reach with error code {1}'.format(
            number, r.text
        ))
        return ''
    return r.content class Number(StatusMixin, TimestampMixin, models.Model):
    competition = models.ForeignKey('core.Competition')
    distance = models.ForeignKey('core.Distance')
    number = models.IntegerField()
    number_text = models.CharField(max_length=50, blank=True)
    participant_slug = models.SlugField(blank=True)
    group = models.CharField(_('Group'), max_length=50, blank=True)

    def __str__(self):
        if not self.group:
            return str(self.number)
        else:
            if self.number > 1000:
                return '%s - %i' % (self.group, int(str(self.number)[1:]))
            else:
                return '%s - %i' % (self.group, self.number)

    class Meta:
        unique_together = ('competition', 'number', 'group')
        ordering = ('distance', 'group', 'number', ) 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 Result(models.Model):
    STATUSES = (
        ('DSQ', 'DSQ'),
        ('DNS', 'DNS'),
        ('DNF', 'DNF'),
    )
    competition = models.ForeignKey('core.Competition')
    participant = models.ForeignKey('registration.Participant')
    number = models.ForeignKey('registration.Number')

    time = models.TimeField(_('Time'), blank=True, null=True)

    zero_time = models.TimeField(_('Time'), blank=True, null=True)  # not used for SEB
    chip_time = models.TimeField(_('Time'), blank=True, null=True)  # not used for SEB

    # lap_count = models.IntegerField(default=0)  # not used for SEB
    # loses_group = models.TimeField(_('Loses Group'), blank=True, null=True)
    # loses_distance = models.TimeField(_('Loses Distance'), blank=True, null=True)

    avg_speed = models.FloatField(_('Average Speed'), blank=True, null=True)

    result_group = models.IntegerField(_('Result Group'), blank=True, null=True)
    result_distance = models.IntegerField(_('Result Distance'), blank=True, null=True)

    points_group = models.IntegerField(_('Points Group'), default=0)
    points_distance = models.IntegerField(_('Points Distance'), default=0)

    status = models.CharField(_('Status'), max_length=20, choices=STATUSES, blank=True)

    standings_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
    standings_object_id = models.PositiveIntegerField(null=True, blank=True)
    standings_object = GenericForeignKey('standings_content_type', 'standings_object_id')

    leader = models.ForeignKey('results.Leader', blank=True, null=True)

    _competition_class = None

    class Meta:
        unique_together = (('competition', 'participant', 'number', ), )

    # def set_loses_distance(self):
    #     r = Result.objects.filter(competition=self.competition, number__distance=self.number.distance).order_by('time')
    #     if r:
    #         zero_time = datetime.datetime.combine(datetime.date.today(), datetime.time(0, 0, 0, 0))
    #         delta = datetime.datetime.combine(datetime.date.today(), r[0].time) - zero_time
    #         self.loses_distance = datetime.datetime.combine(datetime.date.today(), self.time) - delta
    #     else:
    #         self.loses_distance = '00:00:00'
    #
    # def set_loses_group(self):
    #     r = Result.objects.filter(competition=self.competition, number__distance=self.number.distance, participant__group=self.participant.group).order_by('time')
    #     if r:
    #         zero_time = datetime.datetime.combine(datetime.date.today(), datetime.time(0, 0, 0, 0))
    #         delta = datetime.datetime.combine(datetime.date.today(), r[0].time) - zero_time
    #         self.loses_group = datetime.datetime.combine(datetime.date.today(), self.time) - delta
    #     else:
    #         self.loses_group = '00:00:00'

    def set_avg_speed(self):
        avg_speed = self.avg_speed
        if self.time:
            admin = DistanceAdmin.objects.get(competition=self.competition, distance=self.participant.distance)
            seconds = datetime.timedelta(hours=self.time.hour, minutes=self.time.minute, seconds=self.time.second).seconds
            if admin.distance_actual:
                self.avg_speed = round((float(admin.distance_actual) / float(seconds))*3.6, 1)
            if avg_speed != self.avg_speed:
                return True
        return False

    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_points_distance(self):
        points_distance = self.points_distance
        self.points_distance = self.get_competition_class().calculate_points_distance(self)
        if points_distance != self.points_distance:
            return True
        return False

    def set_points_group(self):
        points_group = self.points_group
        self.points_group = self.get_competition_class().calculate_points_group(self)
        if points_group != self.points_group:
            return True
        return False

    def set_all(self):
        # self.set_loses_distance()
        # self.set_loses_group()
        avg = self.set_avg_speed()
        pd = self.set_points_distance()
        pg = self.set_points_group()
        if avg or pd or pg:
            return True  # if any of variables is updated, then return true
        return False class UrlSync(PeriodicTask):
    KINDS = (
        ('FINISH', 'FINISH'),
        ('MIDDLE', 'MIDDLE'),
    )
    competition = models.ForeignKey('core.Competition')
    # expires = models.DateTimeField()
    url = models.CharField(max_length=255)
    current_line = models.IntegerField(default=0)
    kind = models.CharField(max_length=30, default='FINISH')
    index = models.IntegerField(blank=True, null=True)

    def __str__(self):
        return "#%s %s" % (self.id, self.kind)

    def save(self, *args, **kwargs):
        prev_urlsync = UrlSync.objects.get(id=self.id)
        self.name = "Sync_%f" % time.time()
        self.task = "velo.results.tasks.fetch_results"
        self.crontab, created = CrontabSchedule.objects.get_or_create(minute='*', hour='*', day_of_week="*", day_of_month="*", month_of_year="*")
        super(UrlSync, self).save(*args, **kwargs)
        self.args = "[%i]" % self.id
        if prev_urlsync.enabled != self.enabled:
            transaction.on_commit(lambda: restart_celerybeat())
        return super(UrlSync, self).save(*args, **kwargs) class ChipScan(models.Model):
    competition = models.ForeignKey('core.Competition')
    nr_text = models.CharField(max_length=20)
    time_text = models.CharField(max_length=20)
    nr = models.ForeignKey('registration.Number', blank=True, null=True)
    time = models.TimeField(blank=True, null=True)

    is_processed = models.BooleanField(default=False)
    is_blocked = models.BooleanField(default=False)

    url_sync = models.ForeignKey(UrlSync, blank=True, null=True) class HelperResults(TimestampMixin, models.Model):
    """
    This is helper table to calculate number and passage assigning for participants.
    """
    competition = models.ForeignKey('core.Competition')
    participant = models.ForeignKey('registration.Participant')

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
    object_id = models.PositiveIntegerField(null=True, blank=True)
    result_used = GenericForeignKey('content_type', 'object_id')

    calculated_total = models.FloatField(blank=True, null=True, db_index=True)

    passage_assigned = models.IntegerField(blank=True, null=True, db_index=True)

    is_manual = models.BooleanField(default=False)  # Manually added records will not be overwritten

    matches_slug = models.SlugField(blank=True) 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 celery.task import task
from celery.schedules import crontab
from celery.task import periodic_task
from django.conf import settings
from io import StringIO
from urllib.parse import urlencode
from django.core.cache import cache
from django.db.models import Count
from velo.core.models import Log, Competition
from velo.core.tasks import LogErrorsTask
from velo.core.utils import restart_gunicorn
from velo.marketing.models import SMS
from velo.marketing.utils import send_smses, send_sms_text2reach
from velo.registration.models import Number, Participant
from velo.results.models import Result, UrlSync, ChipScan, HelperResults
from velo.velo.utils import load_class
from django.utils import timezone
from django.db import connection
    from velo.registration.competition_classes import Seb2014
import datetime
import requests
import uuid
import traceback





@task(base=LogErrorsTask)
def temp_url_sync_task(urlsync_id):
    obj = UrlSync.objects.get(id=urlsync_id)
    _class = Seb2014(obj.competition.id)

    fetch_results(2)
    _class.assign_result_place()
    _class.recalculate_all_standings()
    send_smses()

    temp_url_sync_task.apply_async((urlsync_id, ), countdown=10)


@task(base=LogErrorsTask)
def send_test_sms():
    send_sms_text2reach(settings.TEXT2REACH_DEV_NUMBER, 'tests %s' % str(uuid.uuid4()))
    return True



@task(base=LogErrorsTask)
def create_result_sms(result_id, sms_text=None):
    send_out = timezone.now()
    result = Result.objects.select_related('competition', 'participant').get(id=result_id)

    if not sms_text:
        sms_text = result.competition.sms_text
    #distance_result = Result.objects.filter(competition=result.competition, number__distance=result.number.distance, time__lte=result.time).exclude(time=None).exclude(participant__is_competing=False).count()
    #group_result = Result.objects.filter(competition=result.competition, participant__group=result.participant.group, time__lte=result.time).exclude(time=None).exclude(participant__is_competing=False).count()

    sms_text = sms_text % {
        'number': result.number.number,
        'time': str(result.time.replace(microsecond=0)),
        'group_result': result.result_group,
        'group': result.participant.group,
        'distance_result': result.result_distance,
    }

    phone_numbers = result.participant.phone_number.replace('+371', '').replace(' ', '').replace('00371', '').replace('+', '')
    phone_number = phone_numbers.split(';')

    for number in phone_number:
        if len(number) == 0:
            print('NO NUMBER')
            continue
        if number[0:3] == '371':
            number = number[3:]
        if len(number) == 8 and number[0] != '2':
            print('Not sending to %s' % number)
            continue
        elif len(number) == 8:
            number = '371%s' % number

        if len(number) < 8:
            print('TOO SHORT NUMBER')
            continue
        print('Sending to %s' % number)
        print(sms_text)

        SMS.objects.create(send_out_at=send_out, phone_number=number, text=sms_text)

    return True


@task(base=LogErrorsTask)
def process_chip_result(_id):
    scan = ChipScan.objects.select_related('competition').get(id=_id)

    class_ = load_class(scan.competition.processing_class)
    processing_class = class_(scan.competition.id)

    if not scan.is_processed:
        if scan.competition.processing_class:
            processing_class.process_chip_result(scan.id)

@task(base=LogErrorsTask)
def recalculate_standing_for_result(competition_id, _id):
    competition = Competition.objects.get(id=competition_id)
    result = Result.objects.get(id=_id)

    class_ = load_class(competition.processing_class)
    processing_class = class_(competition.id)

    processing_class.recalculate_standing_for_result(result)


@task(base=LogErrorsTask)
def fetch_results(_id):
    url_data = UrlSync.objects.get(id=_id)

    processed_line = url_data.current_line

    class_ = load_class(url_data.competition.processing_class)
    processing_class = class_(url_data.competition.id)
    try:
        resp = requests.get(url_data.url)

        if resp.status_code != 200:
            return

        buf = StringIO(resp.text)
        file_lines = tuple(buf.readlines())
        lines_to_process = file_lines[processed_line:]
        for line in lines_to_process:
            print(line)
            if len(line.strip()) == 0:
                print('empty line')
                continue
            number, time_text = line.strip().split(',')
            if number == '0':
                print('skipping 0 number')
                continue
            scan, created = ChipScan.objects.get_or_create(competition=url_data.competition, nr_text=number, time_text=time_text, url_sync=url_data)
            scan.time = time_text
            try:

                scan.nr = Number.objects.get(competition_id__in=url_data.competition.get_ids(), number=number, group='')