"""This module holds the views for the ``trainings`` app."""

import codecs
import io
import os
import zipfile

from subprocess import call
from time import sleep

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404, redirect, render_to_response
from django.template import RequestContext
from django.template.loader import render_to_string
from django.utils.formats import get_format
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from django.views import generic

from django_filters.views import FilterView
from waffle import switch_is_active

from mtp.discrete_apps.lockable.utils import _no_fields_locking

from mtp.apps.accounts.models import Group
from mtp.apps.common.views_utils import (
    LoginRequiredMixin,
    OrderableListMixin,
    RestrictToSuperuserOrTrainerMixin,
    SuccessMessageMixin,
    SyncableDeleteView,
)
from mtp.apps.documents.models import Document, Note
from mtp.apps.evaluations.models import Evaluation
from mtp.apps.eventlog.helpers import event_log
from mtp.apps.eventlog.models import ACTION_CHOICES as EVENT_ACTIONS
from mtp.apps.metaplans.models import Metaplan
from mtp.apps.qrcodes.views import (
    ObjectQRCodeDownloadDetailView,
    ObjectQRCodeDownloadListView,
    QRCodeDownloadMixin,
)
from mtp.apps.roleplays.models import Roleplay
from mtp.apps.stats.utils import export_evaluation_stat_xls, export_workshop_stat_xls
from mtp.apps.surveys.models import Pool
from mtp.apps.trainings.qrcodes import ActivityQRCode
from mtp.apps.workshops.models import Workshop

from .filters import TrainingFilterSet, TrainingSessionFilter
from .forms import (
    TrainingForm,
    TrainingSessionForm,
)
from .models import (
    Training,
    TrainingActivity,
    TrainingClientOptions,
    TrainingSession,
)


class TrainingBaseView(RestrictToSuperuserOrTrainerMixin):
    """Base view for all views related to the trainings.

    Define the queryset to use, depending on the user, and set the
    ``current_main_section``in the context.

    """

    def get_context_data(self, **kwargs):
        """Add ``current_main_section`` to the context."""

        if switch_is_active('enable_surveys'):
            user_can_have_survey = bool(Pool.non_deleted.count_valid_for_user(self.request.user))
        else:
            user_can_have_survey = False

        return super().get_context_data(
            current_main_section='trainings',
            user_can_have_survey=user_can_have_survey,
            **kwargs
        )

    def get_training_queryset_for_current_user(self):
        """Get all ``Training`` instances the current user is allowed to see.

        The Queryset is filtered accordingly to the the training's ``trainer_groups``,
        and the ``owner`` of the training.

        Returns
        -------
        Queryset of ``mtp.apps.trainings.models.Training``
            A lazy queryset with the wanted trainings.

        See Also
        --------
        ``mtp.apps.trainings.managers.TrainingQueryset``

        """

        return Training.objects.non_deleted().available_for_user(
            self.request.user
        ).annotate_is_duplicable()


class TrainingEditBase(SuccessMessageMixin, TrainingBaseView):
    """Base view to edit a given instance of ``mtp.apps.trainings.models.Training`` instance."""

    model = Training
    form_class = TrainingForm
    pk_url_kwarg = 'training_uuid'

    def get_form_kwargs(self):
        """Return the form kwargs accordingly to the context.

        See Also
        --------
        ``mtp.apps.trainings.forms.TrainingForm.__init__`` method.

        """
        kwargs = super().get_form_kwargs()
        kwargs['current_user'] = self.request.user

        return kwargs

    def get_context_data(self, **kwargs):
        """Add ``current_sub_section`` to the context."""

        return super().get_context_data(
            current_sub_section='general_informations',
            **kwargs
        )

    def get_success_url(self):
        """Get the url where to go back when an action is done on a training.

        It actually returns the url to list of the trainings.

        """

        return self.model.get_list_url()

    def get_queryset(self):
        """The user may edit only the trainings he own."""

        return super().get_training_queryset_for_current_user().owned_by(
            self.request.user
        )


class TrainingCreateView(TrainingEditBase, generic.CreateView):
    """The view to create a ``mtp.apps.training.models.Training`` instance.

    Notes
    -----
    The ``owner`` is automatically set when saving the instance.

    """

    success_message = _("Your training was successfully created.")

    def form_valid(self, form):
        """Set the ``owner``, save the training and redirect the user.

        Parameters
        ----------
        form : instance of ``django.forms.models.ModelForm``
            The validated form to save.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the list url of the training.

        Notes
        -----
        This method doesn't call ``super`` but implements all the necessary
        stuff (saving and redirecting)

        """

        training = form.save(commit=False)
        training.owner = self.request.user
        training.save()

        self.send_message(form.cleaned_data)

        return HttpResponseRedirect(training.get_edit_url())


class TrainingUpdateView(TrainingEditBase, generic.UpdateView):
    """The view to update a ``mtp.apps.training.models.Training`` instance."""

    success_message = _("Your training was successfully updated.")


class TrainingPublishView(TrainingEditBase, generic.UpdateView):
    """The view to publish a ``mtp.apps.training.models.Training`` instance.

    The ``parent_version.version_state`` is then set to ``ARCHIVED``, if it exists,
    and is owned by the current user.

    """

    template_name_suffix = '_publish'
    success_message = _("Your training was successfully published.")

    def get_queryset(self):
        """Filter the trainings that can be published."""

        return super().get_queryset().filter_is_publishable()

    def post(self, request, *args, **kwargs):
        """Publish the training, archive the parent, and redirect to the list of training.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the created draft instance.

        """

        training = self.get_object()

        # Publish the training
        training.publish()

        # Archive the parent if it exists and is owned by the current user
        if training.parent_version_id and training.parent_version.owner_id == self.request.user.id:
            with _no_fields_locking(training.parent_version):
                training.parent_version.archive()

        self.send_message()

        return HttpResponseRedirect(self.get_success_url())


class TrainingCreateDraftView(TrainingEditBase, generic.UpdateView):
    """The view to create a new draft from a ``mtp.apps.training.models.Training``instance."""

    template_name_suffix = '_draft'
    success_message = _("Your draft is currently being created.")

    def get_queryset(self):
        """Filter the trainings that can be drafted."""

        return super().get_queryset().filter_is_draftable()

    def get_context_data(self, **kwargs):
        """Check if the draft creation and add in the context the check and reason if not."""

        context = super().get_context_data(**kwargs)

        training = self.object

        allowed, reason = training.is_new_draft_allowed()

        context['create_draft_allowed'] = allowed

        if not allowed:
            context['create_draft_not_allowed_reason'] = str(reason)

        return context

    def post(self, request, *args, **kwargs):
        """Create a draft from a ``mtp.apps.training.models.Training`` instance.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the created draft instance.

        Notes
        -----
        In the ``get`` method we stop the user if a new draft is being created.
        We don't do it in this ``post`` method, by passing ``skip_in_progress=True``, because it
        really should happen very rarely, when the user submits the form twice. So we simply
        ignore and do nothing if a draft creation is already in progress, and give the user its
        success message.

        """

        self.get_object().create_draft(
            async=True,
            skip_in_progress=True,
        )

        self.send_message()

        return HttpResponseRedirect(self.get_success_url())


class TrainingDuplicateView(TrainingEditBase, generic.UpdateView):
    """The view to duplicate a given ``mtp.apps.training.models.Training`` instance."""

    template_name_suffix = '_duplicate'
    success_message = _("Your training is currently being duplicated.")

    def get_queryset(self):
        """Filter the trainings that can be duplicated."""

        return super().get_queryset().filter_is_duplicable()

    def get_context_data(self, **kwargs):
        """Get the potential duplicates of the current training."""

        to_duplicate = self.get_object().get_members_to_duplicate(replace_published=True)

        return super().get_context_data(
            total_duplicates_nb=len(to_duplicate),
            **kwargs
        )

    def post(self, request, *args, **kwargs):
        """Duplicate the training, and redirect the user.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the url of the list of trainings.

        """

        self.get_object().duplicate_to_members(replace_published=True, async=True)

        self.send_message()

        return HttpResponseRedirect(self.get_success_url())


class TrainingDeleteView(TrainingEditBase, SyncableDeleteView):
    """View to soft-delete a ``Training`` instance, with confirmation.

    A training can only be soft-deleted if he is in the ``DRAFT`` version_state
    (so we are sure it's not used by any ``TrainingSession`` instances).

    """

    success_message = _("Your training was successfully deleted.")

    def get_queryset(self):
        """Filter the trainings that can be soft-deleted."""
        return super().get_queryset().filter_is_draft()


class TrainingListView(TrainingBaseView, FilterView):
    """View to list the ``Training`` instances the current user is allowed to see."""

    paginate_by = 15
    template_name = 'cms/trainings/training_list.html'
    filterset_class = TrainingFilterSet

    def get_filterset_kwargs(self, filterset_class):
        """Set the additional kwargs for the filter form

        Parameters
        ----------
        filterset_class: object
            The filter form class to use

        Returns
        -------
        dict: A dict with the updated kwargs

        """

        kwargs = super().get_filterset_kwargs(filterset_class)

        kwargs.update({
            'business_unit_queryset': self.request.user.mgroups.non_deleted().business_units(),
            'job_profile_queryset': self.request.user.mgroups.non_deleted().job_profiles()
        })

        return kwargs

    def get_queryset(self):
        """Filter the training list according to the current user"""

        return super().get_training_queryset_for_current_user()


class TrainingOptionsEditView(SuccessMessageMixin, TrainingBaseView, generic.UpdateView):
    """View to edit a ``TrainingClientOptions`` instance"""

    pk_url_kwarg = 'options_uuid'
    model = TrainingClientOptions
    fields = [
        'activate_show_results',
        'activate_activity_quickcreation',
        'activate_qrcode_launcher'
    ]
    success_message = _('The options was successfully updated.')

    @cached_property
    def training(self):
        """Try to get the current selected training from the url.

        Raises
        ------
        ``django.http.Http404``
            If the training doesn't exists.

        Returns
        -------
        ``Training`` instance

        """

        return get_object_or_404(
            super().get_training_queryset_for_current_user().filter_is_editable(),
            pk=self.kwargs['training_uuid']
        )

    def get_queryset(self):
        """Return the queryset of options the current user can edit"""

        return TrainingClientOptions.objects.filter(
            training=self.training
        )

    def get_success_url(self):
        return self.training.get_activity_list_url()


class TrainingActivityBase(TrainingBaseView):
    """Base view to manage the training activity

    Define the queryset to use, depending on the user and set the
    ``current_sub_section``in the context.

    """

    pk_url_kwarg = 'activity_uuid'
    model = TrainingActivity

    @cached_property
    def training(self):
        """Try to get the current selected training from the url.

        Raises
        ------
        ``django.http.Http404``
            If the training doesn't exists.

        Returns
        -------
        ``Training`` instance

        """

        return get_object_or_404(
            super().get_training_queryset_for_current_user(),
            pk=self.kwargs['training_uuid']
        )

    def get_queryset(self):
        """Filter the activities for the current ``Training`` instance."""

        return TrainingActivity.objects.non_deleted().for_training(self.training).order_by(
            'sort_order'
        )

    def get_context_data(self, **kwargs):
        """Add the ``current_sub_section`` and the current ``Training`` to the context."""

        return super().get_context_data(
            current_sub_section='activities',
            training=self.training,
            **kwargs
        )

    def get_success_url(self):
        """Get the url where to go back when an action is done on a activity.

        It actually returns the url of the activity list.

        """

        return self.training.get_activity_list_url()


class TrainingActivityListView(OrderableListMixin, TrainingActivityBase, generic.ListView):
    """View to list and sort the ``Activity`` instances"""

    template_name = 'cms/trainings/trainingactivity_list.html'


class TrainingActivityEditBase(TrainingActivityBase, SuccessMessageMixin):
    """Base view for the edition of the ``Activity`` instances."""

    def get_queryset(self):
        """Filter the activity that can be edited.

        The activity can be edited only if they belong to a ``DRAFT`` training.

        Returns
        -------
        instance of mtp.apps.trainings.managers.TrainingActivityQuerySet
            The list of activities which can be edited

        """
        return super().get_queryset().editable()


class TrainingActivityDeleteView(TrainingActivityEditBase, SyncableDeleteView):
    """View to soft delete a ``TrainingActivity."""

    template_name = 'cms/trainings/trainingactivity_delete.html'
    success_message = _('Your activity was successfully deleted.')


class TrainingActivityDuplicateView(TrainingActivityEditBase, generic.UpdateView):
    """View to create a duplicate of a given ``Activity`` instance, with confirmation."""

    fields = []
    template_name = 'cms/trainings/trainingactivity_duplicate.html'
    success_message = _('Your activity was successfully duplicated.')

    def post(self, request, *args, **kwargs):
        """Duplicate the activity, and redirect the user.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the url of the list of activities.

        """

        activity = self.get_object()
        activity.duplicate()

        self.send_message()

        return HttpResponseRedirect(self.get_success_url())


class TrainingSessionBaseView(LoginRequiredMixin):
    """Base view for all views related to the sessions.

    Define the queryset to use, depending on the user and set the
    ``current_main_section``in the context.

    """

    model = TrainingSession
    pk_url_kwarg = 'session_uuid'

    def get_context_data(self, **kwargs):
        """Add the ``current_main_section`` and add another queryset for trainings.

        The ``Training`` QuerySet is there to allow filtering the session.

        """

        return super().get_context_data(
            current_main_section='sessions',
            **kwargs
        )

    def get_training_queryset_for_current_user(self):
        """Get all ``Training`` instances to filter the ``TrainingSession`` list.

        The Queryset is filtered accordingly to the user's type or user's group:

        - superusers and trainers:
            Can see all the non-deleted, ``PUBLISHED`` trainings they own.
        - trainees:
            Can see only the training in which there is a session they were added to.

        Returns
        -------
        Queryset of ``mtp.apps.trainings.models.Training``
            A lazy queryset with the wanted trainings.

        Raises
        ------
        ``django.http.Http404``
            if the user is neither a superuser, a trainer, or a trainee

        """

        training_qs = Training.objects.non_deleted()

        if self.request.user.is_superuser or self.request.user.is_trainer():
            return training_qs.available_for_user(self.request.user).exclude_is_draft()

        if self.request.user.is_trainee():
            return training_qs.for_trainee(self.request.user).exclude_is_draft()

        raise Http404

    def get_queryset(self):
        """Get all ``TrainingSession`` instances the current user is allowed to see.

        The Queryset is filtered accordingly to the user's type or user's group,
        see ``visible_by_user`` method on ``mtp.apps.trainings.models.TrainingSession``.

        Returns
        -------
        Queryset of ``mtp.apps.trainings.models.TrainingSession``
            A lazy queryset with the wanted training sessions.

        """

        return TrainingSession.objects.non_deleted().visible_by_user(
            self.request.user
        ).select_related('trainer', 'training')


class TrainingSessionList(TrainingSessionBaseView, FilterView):
    """View to list the ``TrainingSession`` instances the current user is allowed to see."""

    template_name = 'cms/trainings/trainingsession_list.html'
    filterset_class = TrainingSessionFilter
    paginate_by = 10

    @property
    def selected_training(self):
        """Get the selected training in filter.

        Returns
        -------
        Training
            The selected training or None.

        """

        # We fetch selected trainings pks in filter
        try:
            selected_trainings_pks = self.filterset.data.getlist('training')
        except AttributeError:  # No training is selected
            return None

        # If only one training is selected, we return it else None
        if len(selected_trainings_pks) == 1:
            return Training.objects.get(pk=selected_trainings_pks[0])

        return None

    def get_context_data(self, **kwargs):
        """Add the ``DATE_INPUT_FORMAT`` and the selected training to the context."""

        return super().get_context_data(
            DATE_INPUT_FORMAT=get_format('DATE_INPUT_FORMATS')[2].replace('%', '').replace(
                'M',
                'i'
            ),
            selected_training=self.selected_training,
            **kwargs
        )

    def get_filterset_kwargs(self, filterset_class):
        """Set the additional kwargs for the filter form

        Parameters
        ----------
        filterset_class: object
            The filter form class to use

        Returns
        -------
        dict: A dict with the updated kwargs

        """

        kwargs = super().get_filterset_kwargs(filterset_class)

        kwargs.update({
            'trainings_queryset': self.get_training_queryset_for_current_user(),
            'user': self.request.user
        })

        return kwargs


class TrainingSessionEditBase(SuccessMessageMixin, RestrictToSuperuserOrTrainerMixin,
                              TrainingSessionBaseView):
    """Base view to manage ``TrainingSession`` instances."""

    form_class = TrainingSessionForm

    def get_form_kwargs(self):
        """Return the form kwargs accordingly to the context.

        If the training session is not new, remove the ``training`` field on the form.
        If the user is not a superuser, remove the ``trainer`` field on the form.

        See Also
        --------
        ``mtp.apps.trainings.forms.TrainingSessionForm.__init__`` method.

        """

        kwargs = super().get_form_kwargs()
        kwargs['remove_training_field'] = bool(getattr(self, 'object', None))
        kwargs['remove_trainer_field'] = not self.request.user.is_superuser
        kwargs['current_user'] = self.request.user

        return kwargs

    def get_context_data(self, **kwargs):
        """Add the ``DATETIME_INPUT_FORMAT`` constant to the context."""

        # TODO: Refactor this.
        return super().get_context_data(
            DATETIME_INPUT_FORMAT=get_format('DATETIME_INPUT_FORMATS')[2].replace(
                '%', '').replace('M', 'i'),
            **kwargs
        )

    def get_success_url(self):
        """Return the url be redirect to after the save.

        If we got a current training instance, we redirect filtered list of sessions
        of this training, if not, simply redirect to the unfiltered list.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the url of the list of training sessions.

        """

        return TrainingSession.get_list_url()

    def form_valid(self, form):
        """Set the trainer if applicable, save the training session and redirect the user.

        Parameters
        ----------
        form : instance of ``django.forms.models.ModelForm``
            The validated form to save.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the list of training session.

        Notes
        -----
        This method doesn't call ``super`` but implements all the necessary stuff (saving and
        redirecting)

        """

        if not self.request.user.is_superuser:
            self.object = form.save(commit=False)
            self.object.trainer = self.request.user
            self.object.save()
            form.save_m2m()  # Necessary to save the session users
        else:
            form.save()

        self.send_message(form.cleaned_data)

        return HttpResponseRedirect(self.get_success_url())


class TrainingSessionCreate(TrainingSessionEditBase, generic.CreateView):
    """The view to create a ``TrainingSession`` instance."""

    success_message = _("Your session was successfully created.")


class TrainingSessionUpdate(TrainingSessionEditBase, generic.UpdateView):
    """The view to update a ``TrainingSession`` instance."""

    success_message = _("Your session was successfully updated.")


class TrainingSessionDelete(TrainingSessionEditBase, SyncableDeleteView):
    """The view to soft-delete a ``TrainingSession`` instance."""

    success_message = _("Your session was successfully deleted.")


class TrainingSessionWhosWho(TrainingSessionBaseView, generic.DetailView):
    """The view to render a ``TrainingSession`` users list."""

    template_name = "cms/trainings/whoswho.html"

    def users(self):
        """Get the users from the ``TrainingSession`` who belongs to the "whoswho" group."""

        group = Group.objects.get(name='whoswho')
        return self.object.users.filter(mgroups=group).order_by('last_name', 'first_name')

    def get_context_data(self, **kwargs):
        """Add the list of users who belongs to the "whoshwo" group to the context."""

        return super().get_context_data(
            users=self.users,
            **kwargs
        )


class TrainingSessionClose(TrainingSessionEditBase, generic.UpdateView):
    """The view close a ``mtp.apps.training.models.TrainingSession`` instance."""

    template_name_suffix = '_close'
    success_message = _("Your training session was successfully closed.")

    def post(self, request, *args, **kwargs):
        """Close the training session, and redirect to the list of training session.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the training session list.

        """

        training_session = self.get_object()
        training_session.close(async=True)

        self.send_message()

        return HttpResponseRedirect(self.get_success_url())


class TrainingSessionPodium(TrainingSessionEditBase, generic.DetailView):
    """The view to render a "podium" of the session's trainees"""

    template_name_suffix = '_podium'


@login_required
def session_users_import(request):
    if request.method == 'POST':
        pass
    else:
        pass

    return render_to_response('trainings/session_import.html',
        locals(), context_instance=RequestContext(request))


@login_required
def session_trainee_content(request, session_uuid, trainee_uuid):
    session = get_object_or_404(TrainingSession, pk=session_uuid)
    trainee = get_object_or_404(get_user_model(), pk=trainee_uuid)

    # 1. Fetch all elements to download.
    documents = Document.objects.filter(
        hidden=False,
        is_deleted=False,
        trainer=False,
        training=session.training,
        user=session.trainer,
    )

    metaplans = Metaplan.objects.filter(training_session=session)

    try:
        free_note = trainee.note_set.get(document=None, training_session=session)
    except Note.DoesNotExist:
        free_note = None

    # 2. Zip all these elements.
    zip_buffer = io.BytesIO()
    zf = zipfile.ZipFile(zip_buffer, 'w')

    # Add annotated .pdf documents.
    for document in documents:
        file_path = _generate_document(document, trainee)
        file_path = os.path.join(settings.MEDIA_ROOT, file_path)
        zf.write(file_path, '%s.%s' % (
            document.name,
            os.path.basename(file_path).rsplit('.', 1)[1]
        ))

    # Add metaplans.
    for metaplan in metaplans:
        _html = render_to_string('cms/metaplans/metaplan_export.html', {'metaplan': metaplan})
        metaplan_title = metaplan.title.replace(' ', '_')[:25]
        zf.writestr('metaplan/%s.html' % (metaplan_title), _html.encode('utf-8'))

    # Add notes.
    if free_note:
        zf.writestr('notes.txt', free_note.text.encode('utf-8'))

    zf.close()

    # 3. Send the .zip file.
    zip_data = zip_buffer.getvalue()
    zip_filename = '%s-%s.zip' % (session.training.name, trainee.username)
    zip_filename = zip_filename.replace(' ', '-')

    resp = HttpResponse(zip_data)
    resp['Content-Type'] = 'application/x-zip-compressed'
    resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename

    # Log downloads for all the documents contained in the .zip file.
    for document in documents:
        event_log(subject=document, sender=request.user, action=EVENT_ACTIONS.DOCUMENT_DOWNLOAD)

    return resp


@login_required
def session_stats(request, session_uuid):
    if not request.user.is_staff:
        return redirect(settings.ROOT_URL)

    session = get_object_or_404(TrainingSession, pk=session_uuid)
    trainer = session.trainer

    if not request.user.is_superuser and request.user != trainer:
        return redirect(settings.ROOT_URL)

    xls = export_workshop_stat_xls(trainer, session)

    response = HttpResponse(content_type='application/vnd.ms-excel')
    response['Content-Disposition'] = 'attachment; filename=session_%s-%s_%s_export.xlsx' % (
        trainer.first_name, trainer.last_name, session.date.strftime('%Y-%m-%d'))

    xls.seek(0)
    response.write(xls.read())

    return response


@login_required
def session_stats_eval(request, session_uuid):
    if not request.user.is_staff:
        return redirect(settings.ROOT_URL)

    session = get_object_or_404(TrainingSession, pk=session_uuid)
    trainer = session.trainer

    if not request.user.is_superuser and request.user != trainer:
        return redirect(settings.ROOT_URL)

    xls = export_evaluation_stat_xls(trainer, session)

    response = HttpResponse(content_type='application/vnd.ms-excel')
    response['Content-Disposition'] = 'attachment; filename=session_%s-%s_%s_eval_export.xlsx' % (
        trainer.first_name, trainer.last_name, session.date.strftime('%Y-%m-%d'))

    xls.seek(0)
    response.write(xls.read())

    return response


# Helper
# ------

def _generate_document(document, user):
    notes = Note.objects.filter(user=user, document=document)

    if len(notes) == 0:
        return document.doc_file.orig_file.name

    annot = render_to_string('formation/scripts/annotation.txt', locals())
    annot_file_name = os.path.join(settings.ANNOT_SCRIPT_ROOT, 'annot_%s.txt' % (document.id))

    f = codecs.open(annot_file_name, 'w', 'iso-8859-1')
    f.write(annot.encode('ascii', 'ignore'))
    f.flush()
    f.close
    sleep(1)

    output_file_name = '%s-%s.pdf' % (document.id, user.id)
    output_path = os.path.join(settings.ANNOT_DATA_ROOT, output_file_name)
    if not os.path.exists(output_path):
        command = 'gs -o %s -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress %s %s' % (
            output_path,
            os.path.join(settings.MEDIA_ROOT, document.doc_file.orig_file.path),
            annot_file_name,
        )
        call(['cat', '%s' % annot_file_name])
        call(command.split(' '))
        sleep(1)

    return output_path


# pylint: disable=abstract-method
class ActivityQRCodeDownloadMixin(QRCodeDownloadMixin, TrainingActivityBase):
    """Mixin containing specific methods and attributes to download activity launch QRCode."""

    qrcode_class = ActivityQRCode


class ActivityQRCodeDownloadDetailView(ActivityQRCodeDownloadMixin, ObjectQRCodeDownloadDetailView):
    """View to download QrCode able to launch an activity."""


class ActivityQRCodeDownloadListView(ActivityQRCodeDownloadMixin, ObjectQRCodeDownloadListView):
    """View to download training QrCodes able to launch an activity."""

    def get_queryset(self):
        """Get training activities.

        Only Document, Evaluation, Workshop, Roleplay.
        We can't launch Template with a QRCode like BrainstormerTemplate and MetaplanTemplate.

        """
        return super().get_queryset().instance_of(
            Document, Evaluation, Workshop, Roleplay
        )

    def get_filename(self):
        """Get the filename used to name the generated zip file."""
        return str(self.training)


class SessionTokenView(TrainingSessionBaseView, generic.DetailView):
    """View to display the selected session token."""

    template_name = 'trainings/trainingsession_token.html'
