# Kod źródłowy modułu tasks.models

    from django.core.validators import MaxValueValidator, MinValueValidator
    from django.db import models
    from django.db.models import QuerySet
    from django.db.models.aggregates import Sum, Avg
    from django.db.models.expressions import Case, When, F
    from django.db.models.fields import IntegerField
    from django.db.models.manager import Manager
    from django.template.defaultfilters import slugify
    from django.utils.translation import gettext as _
    from django_extensions.db.models import TimeStampedModel
    from embed_video.fields import EmbedVideoField
    from exercises.models import Exercise
    from mailing.mail_service import EmailSender
    from popojutrze import settings
    from tasks.constants import ApprenticeTaskAnswerStatus
    from utils.general import normalize_polish_chars, get_base_url_from_request

    class PathManager(models.Manager):
        def active(self):
            return self.all().order_by('position')

    class Path(models.Model):
        name = models.CharField(max_length=70, verbose_name=_('nazwa'))
        is_path_blocking = models.BooleanField(default=True, verbose_name=_('czy blokująca?'))
        image = models.ImageField(null=True, blank=True, upload_to='path_images/', verbose_name=_('grafika'))
        position = models.PositiveIntegerField(default=0, blank=True, verbose_name=_('pozycja'))

        objects = PathManager()

        def slug(self):
            return slugify(normalize_polish_chars(self.name))

        class Meta:
            ordering = ('position',)
            verbose_name = _('ścieżka')
            verbose_name_plural = _('ścieżki')

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

    class TaskQuerySet(QuerySet):
        def active(self):
            return self.filter(is_active=True).order_by('position')

    class TaskManager(Manager.from_queryset(TaskQuerySet)):
        pass

    class Task(models.Model):
        path = models.ForeignKey('Path', related_name='tasks', verbose_name=_('ścieżka'))
        is_active = models.BooleanField(default=True, verbose_name=_('czy aktywne?'))
        title = models.CharField(blank=True, max_length=70, verbose_name=_('tytuł'))
        subtitle = models.CharField(blank=True, max_length=70, verbose_name=_('podtytuł'))
        header = models.CharField(blank=True, max_length=70, verbose_name=_('nagłówek'))
        image = models.ImageField(null=True, blank=True, upload_to='path_images/', verbose_name=_('grafika'))
        yt_video = EmbedVideoField(blank=True, null=True, verbose_name=_('video'))
        description = models.TextField(blank=True, verbose_name=_('opis'))
        instruction = models.TextField(blank=True, verbose_name=_('instrukcja'))
        articles = models.ManyToManyField('articles.Article', related_name='tasks', blank=True, verbose_name=_('artykuły'))
        mark_criteria = models.ManyToManyField('marks.MarkCriteria', related_name='tasks', through='TaskMarkCriteria',
                                               blank=True, verbose_name=_('kryteria_oceny'))
        exercises = models.ManyToManyField('exercises.Exercise', through='ExerciseTaskPosition', blank=True,
                                           related_name='tasks', verbose_name=_('ćwiczenia'))
        position = models.PositiveIntegerField(default=0, blank=True, verbose_name=_('pozycja'))

        objects = TaskManager()

        class Meta:
            ordering = ('position',)
            verbose_name = _('zadanie')
            verbose_name_plural = _('zadania')

        def slug(self):
            return slugify(normalize_polish_chars(self.title))

        def get_in_path_position(self):
            return self.path.tasks.active().filter(position__lte=self.position).count()

        def all_exercises_count(self):
            return Exercise.objects.from_task_count(self)

        def done_exercises_count(self, apprentice):
            return Exercise.objects.from_task(self).annotated_with_done_by_apprentice_count(apprentice)

        def all_exercises_done(self, apprentice):
            return self.all_exercises_count() == self.done_exercises_count(apprentice)

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

    class ExerciseTaskPositionManager(models.Manager):
        def active(self):
            return self.filter(exercise__is_active=True).order_by('position')

        def annotated_with_done_by_apprentice(self, apprentice):
            return self.active().annotate(
                done=Sum(Case(When(exercise__apprentices=apprentice, then=1), default=0,
                              output_field=IntegerField())))

    class ExerciseTaskPosition(models.Model):
        exercise = models.ForeignKey('exercises.Exercise', related_name='exercise_task_positions',
                                     verbose_name=_('cwiczenie'))
        task = models.ForeignKey('tasks.Task', related_name='exercise_task_positions', verbose_name=_('zadanie'))
        position = models.PositiveIntegerField(default=0, blank=True, verbose_name=_('pozycja'))

        objects = ExerciseTaskPositionManager()

        class Meta:
            ordering = ('position',)
            verbose_name = _('ćwiczenie w zadaniu')
            verbose_name_plural = _('ćwiczenia w zadaniach')

        def next(self):
            return ExerciseTaskPosition.objects.active().filter(position__gt=self.position, task=self.task).order_by(
                'position').first()

        def previous(self):
            return ExerciseTaskPosition.objects.active().filter(position__lt=self.position, task=self.task).order_by(
                '-position').first()

        def __str__(self):
            return '{} - {}: {}'.format(self.task, self.exercise, self.position)

    class TaskMarkCriteriaManager(models.Manager):
        # Założenie jest takie, że traktujemy ten apprentice_video jako video_2 w task_answer
        def from_apprentice_video_with_likes(self, apprentice_video):
            return apprentice_video.task_answer_as_2.task.task_mark_criteria.all().annotate(likes=Sum(
                Case(When(apprentice_scores__apprentice_video=apprentice_video, then=1), defult=0,
                     output_field=IntegerField())))

    class TaskMarkCriteria(models.Model):
        task = models.ForeignKey('Task', related_name='task_mark_criteria', verbose_name=_('zadanie'))
        mark_criteria = models.ForeignKey('marks.MarkCriteria', related_name='task_mark_criteria',
                                          verbose_name=_('kryterium oceny'))

        objects = TaskMarkCriteriaManager()

        class Meta:
            verbose_name = _('kryterium oceny w zadaniu')
            verbose_name_plural = _('kryteraia ocen w zdaniach')

        def __str__(self):
            return '{} in {} '.format(self.mark_criteria, self.task)

    class MentorTaskMarkCriteriaApprenticeVideoValueQuerySet(QuerySet):
        def group_averages(self):
            return self.values('task_mark_criteria__mark_criteria__group__name').annotate(group_name=F(
                'task_mark_criteria__mark_criteria__group__name')).annotate(group_avg=Avg('value'))

        def average(self):
            return self.aggregate(average=Avg('value')).get('average')

    class MentorTaskMarkCriteriaApprenticeVideoValueManager(
        Manager.from_queryset(MentorTaskMarkCriteriaApprenticeVideoValueQuerySet)):
        pass

    class MentorTaskMarkCriteriaApprenticeVideoValue(TimeStampedModel):
        mentor = models.ForeignKey('users.Mentor', related_name='mentor_scores', verbose_name=_('mentor'))
        apprentice_video = models.ForeignKey('ApprenticeVideo', related_name='mentor_scores',
                                             verbose_name=_('video do zadania'))
        task_mark_criteria = models.ForeignKey('TaskMarkCriteria', related_name='mentor_scores',
                                               verbose_name=_('kryterium oceny'))
        value = models.FloatField(verbose_name=_('wartość'),
                                  validators=[MaxValueValidator(10.0), MinValueValidator(0.0)])

        objects = MentorTaskMarkCriteriaApprenticeVideoValueManager()

        class Meta:
            verbose_name = _('ocena filmu przez mentora w kryterium')
            verbose_name_plural = _('oceny filmu przez mentora w kryterium')

        def __str__(self):
            return '{} - {} / {} / {}'.format(self.mentor, self.apprentice_video, self.task_mark_criteria, self.value)

    class MentorApprenticeVideoOpinion(TimeStampedModel):
        mentor = models.ForeignKey('users.Mentor', related_name='mentor_text_score', verbose_name=_('mentor'))
        apprentice_video = models.OneToOneField('ApprenticeVideo', related_name='mentor_text_score',
                                                verbose_name=_('video do zadania'))
        opinion = models.TextField(verbose_name=_('opinia'))

        class Meta:
            verbose_name = _('ocena tekstowa filmu przez mentora')
            verbose_name_plural = _('oceny tekstowe filmów przez mentora')

        def __str__(self):
            return '{} - {} / {}'.format(self.mentor, self.apprentice_video, self.opinion)

    class ApprenticeTaskMarkCriteriaApprenticeVideo(TimeStampedModel):
        apprentice = models.ForeignKey('users.Apprentice', related_name='apprentice_scores', verbose_name=_('uczeń'))
        apprentice_video = models.ForeignKey('ApprenticeVideo', related_name='apprentice_scores',
                                             verbose_name=_('video do zadania'))
        task_mark_criteria = models.ForeignKey('TaskMarkCriteria', related_name='apprentice_scores',
                                               verbose_name=_('kryterium oceny'))

        class Meta:
            verbose_name = _('ocena filmu przez innego ucznia w kryterium')
            verbose_name_plural = _('oceny filmu przez innego ucznia w kryterium')

        def __str__(self):
            return '{} - {} / {}'.format(self.apprentice, self.apprentice_video, self.task_mark_criteria)

    class ApprenticeTaskAnswerQuerySet(QuerySet):
        def completed(self):
            return self.filter(video_1__isnull=False, video_2__isnull=False)

        def by_apprentice(self, apprentice):
            return self.filter(apprentice=apprentice)

    class ApprenticeTaskAnswerManager(Manager.from_queryset(ApprenticeTaskAnswerQuerySet)):
        def own(self, apprentice):
            return self.completed().filter(apprentice=apprentice)

        def others(self, apprentice):
            return self.completed().exclude(apprentice=apprentice)

        def random_from_task_answer(self, task_answer, max_quant):
            return self.completed().filter(task=task_answer.task).exclude(id=task_answer.id).order_by('?')[:max_quant]

        def from_path(self, path):
            return self.filter(task__path=path)

    class ApprenticeTaskAnswer(TimeStampedModel):
        task = models.ForeignKey('Task', related_name='answers', verbose_name=_('zadanie'))
        apprentice = models.ForeignKey('users.Apprentice', related_name='answers', verbose_name=_('uczeń'))
        custom_title = models.CharField(max_length=50, blank=True, verbose_name=_('dodatkowy tytuł'))
        video_1 = models.OneToOneField('ApprenticeVideo', related_name='task_answer_as_1', blank=True, null=True,
                                       verbose_name=_('pierwsze video'))
        video_2 = models.OneToOneField('ApprenticeVideo', related_name='task_answer_as_2', blank=True, null=True,
                                       verbose_name=_('drugie video'))
        notification = models.BooleanField(default=False, verbose_name=_('powiadomienie o ocenie'))

        objects = ApprenticeTaskAnswerManager()

        def get_task_process(self):
            return int((float(self.get_status())) / (float(len(ApprenticeTaskAnswerStatus.names)) - 1) * 100)

        def get_status(self):
            if not self.video_1:
                return ApprenticeTaskAnswerStatus.FIRST_UPLOAD
            if not hasattr(self.video_1, 'mentor_text_score') and self.video_1.mentor_scores.count() == 0:
                return ApprenticeTaskAnswerStatus.BEFORE_MENTOR_RATE
            if self.task.exercises.active().count() != self.task.exercises.annotated_with_done_by_apprentice_count(
                    self.apprentice):
                return ApprenticeTaskAnswerStatus.BEFORE_EXERCISES_DONE
            if not self.video_2:
                return ApprenticeTaskAnswerStatus.SECOND_UPLOAD
            return ApprenticeTaskAnswerStatus.COMPLETED

        class Meta:
            verbose_name = _('odpowiedź ucznia do zadania')
            verbose_name_plural = _('odpowiedzi uczniów do zadań')

        def slug(self):
            return self.task.slug()

        def save(self, **kwargs):
            if self.pk:
                old = ApprenticeTaskAnswer.objects.get(id=self.pk)
                if old.notification is False and self.notification is True:
                    EmailSender.send_mentor_score_email(self.apprentice.email, {'BASE_SITE_URL': settings.BASE_SITE_URL})
            super(ApprenticeTaskAnswer, self).save(**kwargs)

        def __str__(self):
            return '{} - {}'.format(self.apprentice, self.task)

    class ApprenticeVideo(TimeStampedModel):
        yt_video = EmbedVideoField(blank=True, verbose_name=_('link youtube'))
        reflection = models.TextField(blank=True, verbose_name=_('refleksja'))
        apprentices = models.ManyToManyField('users.Apprentice', blank=True, related_name='liked_apprentice_videos',
                                             verbose_name=_('ocenione przez'))

        class Meta:
            verbose_name = _('video do zadania')
            verbose_name_plural = _('video do zadania')

        def get_likes(self):
            return TaskMarkCriteria.objects.from_apprentice_video_with_likes(self)

        def __str__(self):
            try:
                task_answer_as_1 = self.task_answer_as_1
            except ApprenticeTaskAnswer.DoesNotExist:
                task_answer_as_1 = ''
            try:
                task_answer_as_2 = self.task_answer_as_2
            except ApprenticeTaskAnswer.DoesNotExist:
                task_answer_as_2 = ''

            return '{} - 1:<{}> / 2:<{}>'.format(self.yt_video, task_answer_as_1, task_answer_as_2)