uclouvain/osis-dissertation

View on GitHub
views/dissertation.py

Summary

Maintainability
F
1 wk
Test Coverage
##############################################################################
#
#    OSIS stands for Open Student Information System. It's an application
#    designed to manage the core business of higher education institutions,
#    such as universities, faculties, institutes and professional schools.
#    The core business involves the administration of students, teachers,
#    courses, programs and so on.
#
#    Copyright (C) 2015-2019 Université catholique de Louvain (http://www.uclouvain.be)
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    A copy of this license - GNU General Public License - is available
#    at the root of the source code of this program.  If not,
#    see http://www.gnu.org/licenses/.
#
##############################################################################
import json
import time

from dal import autocomplete
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.mixins import UserPassesTestMixin
from django.db.models import Q, F
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.shortcuts import redirect
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from django.views.generic import CreateView
from openpyxl import Workbook
from openpyxl.utils.exceptions import IllegalCharacterError
from rest_framework import status

from base import models as mdl
from base.auth.roles import tutor
from base.models import academic_year
from base.models.academic_year import AcademicYear
from base.models.education_group import EducationGroup
from base.models.person import Person
from base.models.student import Student
from base.utils.cache import cache_filter
from base.views.mixins import AjaxTemplateMixin
from dissertation.forms import ManagerDissertationEditForm, ManagerDissertationRoleForm, \
    ManagerDissertationUpdateForm, AdviserForm, PropositionDissertationFileForm, DissertationFileForm
from dissertation.models import adviser, dissertation, dissertation_role, \
    dissertation_update, offer_proposition, proposition_role
from dissertation.models.adviser import Adviser
from dissertation.models.dissertation import Dissertation
from dissertation.models.dissertation_role import DissertationRole, MAX_DISSERTATION_ROLE_FOR_ONE_DISSERTATION
from dissertation.models.enums import dissertation_role_status
from dissertation.models.enums import dissertation_status
from dissertation.models.enums.dissertation_role_status import DissertationRoleStatus
from dissertation.models.enums.dissertation_status import DISSERTATION_STATUS
from dissertation.models.offer_proposition import OfferProposition
from dissertation.perms import adviser_can_manage, autorized_dissert_promotor_or_manager, check_for_dissert, \
    adviser_is_in_jury
from osis_common.document.xls_build import save_virtual_workbook


def _role_can_be_deleted(dissert, dissert_role):
    promotors_count = dissertation_role.count_by_status_dissertation(DissertationRoleStatus.PROMOTEUR.name, dissert)
    return dissert_role.status != DissertationRoleStatus.PROMOTEUR.name or promotors_count > 1


def new_status_display(dissert, opperation):
    new_status = dissertation.get_next_status(dissert, opperation)
    status_dict = dict(DISSERTATION_STATUS)
    return status_dict[new_status]


#########################
#      GLOBAL VIEW      #
#########################


@login_required
def dissertations(request):
    person = get_object_or_404(Person.objects.select_related('adviser'), pk=request.user.person.pk)

    if mdl.student.find_by_person(person) and not \
            tutor.find_by_person(person) and not \
            adviser.find_by_person(person):
        return redirect('home')

    elif adviser.find_by_person(person):
        count_advisers_pro_request = dissertation_role.count_by_adviser(person.adviser,
                                                                        DissertationRoleStatus.PROMOTEUR.name,
                                                                        dissertation_status.DIR_SUBMIT)

        return render(request, "dissertations.html",
                      {'section': 'dissertations',
                       'person': person,
                       'adviser': person.adviser,
                       'count_advisers_pro_request': count_advisers_pro_request})
    else:

        form = AdviserForm(request.POST or None)
        if form.is_valid():
            adv = Adviser(person=person,
                          available_by_email=False,
                          available_by_phone=False,
                          available_at_office=False)
            adv.save()
            count_advisers_pro_request = dissertation_role.count_by_adviser(adv,
                                                                            DissertationRoleStatus.PROMOTEUR.name,
                                                                            dissertation_status.DIR_SUBMIT)
            return render(request, "dissertations.html",
                          {'section': 'dissertations',
                           'person': person,
                           'adviser': adv,
                           'count_advisers_pro_request': count_advisers_pro_request})
        else:
            return render(request, 'dissertations_welcome.html', {'form': form})


###########################
#      MANAGER VIEWS      #
###########################

@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_detail(request, pk):
    dissert = dissertation.find_by_id(pk)
    if dissert is None:
        return redirect('manager_dissertations_list')
    person = mdl.person.find_by_user(request.user)
    adv = adviser.search_by_person(person)
    count_dissertation_role = dissertation_role.count_by_dissertation(dissert)
    count_proposition_role = proposition_role.count_by_dissertation(dissert)
    proposition_roles = proposition_role.search_by_dissertation(dissert)
    offer_prop = offer_proposition.get_by_dissertation(dissert)

    if offer_prop is None:
        return redirect('manager_dissertations_list')
    dissertation_file_form = DissertationFileForm(
        request.POST or None,
        instance=dissert
    )
    if request.method == 'POST' and dissertation_file_form.is_valid():
        dissertation_file_form.save()
        return redirect('manager_dissertations_detail', pk=dissert.pk)
    proposition_dissertation_file_form = PropositionDissertationFileForm(
        instance=dissert.proposition_dissertation
    )

    if count_proposition_role == 0 and count_dissertation_role == 0:
        justification = "%s %s %s" % (_("Auto add jury"),
                                      DissertationRoleStatus.PROMOTEUR.name,
                                      str(dissert.proposition_dissertation.author))
        dissertation_update.add(request, dissert, dissert.status, justification=justification)
        dissertation_role.add(DissertationRoleStatus.PROMOTEUR.name, dissert.proposition_dissertation.author, dissert)
    elif count_dissertation_role == 0:
        for role in proposition_roles:
            justification = "%s %s %s" % (_("Auto add jury"), role.status, str(role.adviser))
            dissertation_update.add(request, dissert, dissert.status, justification=justification)
            dissertation_role.add(role.status, role.adviser, dissert)

    if dissert.status == dissertation_status.DRAFT:
        jury_manager_visibility = True
        jury_manager_can_edit = False
        jury_manager_message = _("Dissertation status is draft, managers can't edit jury.")
        jury_teacher_visibility = False
        jury_teacher_can_edit = False
        jury_teacher_message = _("Dissertation status is draft, teachers can't edit jury.")
        jury_student_visibility = True
        jury_student_can_edit = offer_prop.student_can_manage_readers
        if jury_student_can_edit:
            jury_student_message = _('Dissertation status is draft, student can manage readers')
        else:
            jury_student_message = _("Dissertation status is draft, student can't manage readers")
    else:
        jury_manager_visibility = True
        jury_manager_can_edit = True
        jury_manager_message = _('Managers can see and edit jury.')
        jury_teacher_visibility = True
        jury_teacher_can_edit = offer_prop.adviser_can_suggest_reader
        if jury_teacher_can_edit:
            jury_teacher_message = _('Teachers can see and edit jury.')
        else:
            jury_teacher_message = _('Teachers can see jury but not edit it.')
        jury_student_visibility = offer_prop.in_periode_jury_visibility
        jury_student_can_edit = False
        if jury_student_visibility:
            jury_student_message = _('Jury is currently visible for the student')
        else:
            jury_student_message = _('Jury is currently invisible for the student')
    dissertation_roles = dissertation_role.search_by_dissertation(dissert)

    promotors_count = dissertation_role.count_by_status_dissertation(DissertationRoleStatus.PROMOTEUR.name, dissert)

    return render(request, 'manager_dissertations_detail.html',
                  {
                      'dissertation': dissert,
                      'adviser': adv,
                      'dissertation_roles': dissertation_roles,
                      'count_dissertation_role': count_dissertation_role,
                      'jury_manager_visibility': jury_manager_visibility,
                      'jury_manager_can_edit': jury_manager_can_edit,
                      'jury_manager_message': jury_manager_message,
                      'jury_teacher_visibility': jury_teacher_visibility,
                      'jury_teacher_can_edit': jury_teacher_can_edit,
                      'jury_teacher_message': jury_teacher_message,
                      'jury_student_visibility': jury_student_visibility,
                      'jury_student_can_edit': jury_student_can_edit,
                      'jury_student_message': jury_student_message,
                      'promotors_count': promotors_count,
                      'dissertation_file': dissertation_file_form.initial['dissertation_file'],
                      'dissertation_file_form': dissertation_file_form,
                      'proposition_dissertation_file':
                          proposition_dissertation_file_form.initial['proposition_dissertation_file'],
                      'proposition_dissertation_file_form': proposition_dissertation_file_form
                  })


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_detail_updates(request, pk):
    dissert = get_object_or_404(Dissertation.objects.prefetch_related('dissertationupdate_set'), pk=pk)
    dissertation_updates = dissert.dissertationupdate_set.all().order_by('created'). \
        select_related('person')
    return render(request, 'manager_dissertations_detail_updates.html',
                  {'dissertation': dissert,
                   'dissertation_updates': dissertation_updates})


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_edit(request, pk):
    dissert = get_object_or_404(Dissertation, pk=pk)
    form = ManagerDissertationEditForm(request.POST or None, instance=dissert, user=request.user)
    if form.is_valid():
        dissert = form.save()
        justification = _("manager has edited the dissertation")
        dissertation_update.add(request, dissert, dissert.status, justification=justification)
        return redirect('manager_dissertations_detail', pk=dissert.pk)
    return render(request, 'manager_dissertations_edit.html',
                  {'form': form,
                   'dissert': dissert})


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_jury_edit(request, pk):
    dissert_role = dissertation_role.find_by_id(pk)
    if dissert_role is None:
        return redirect('dissertations_list')
    if request.method == "POST":
        form = ManagerDissertationRoleForm(request.POST, instance=dissert_role)
        if form.is_valid():
            form.save()
            return redirect('manager_dissertations_detail', pk=dissert_role.dissertation.pk)
    else:
        form = ManagerDissertationRoleForm(instance=dissert_role)
    return render(request, 'manager_dissertations_jury_edit.html', {'form': form})


@require_http_methods(["POST"])
@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_jury_new_ajax(request):
    pk_dissert = request.POST.get("pk_dissertation", '')
    status_choice = request.POST.get("status_choice", '')
    id_adviser_of_dissert_role = request.POST.get("adviser_pk", '')
    if not id_adviser_of_dissert_role or not status_choice or not pk_dissert:
        return HttpResponse(status=status.HTTP_405_METHOD_NOT_ALLOWED)
    else:
        dissert = dissertation.find_by_id(pk_dissert)
        adviser_of_dissert_role = adviser.get_by_id(int(id_adviser_of_dissert_role))
        count_dissertation_role = dissertation_role.count_by_dissertation(dissert)
        person = mdl.person.find_by_user(request.user)
        adv_manager = adviser.search_by_person(person)
        if adviser_can_manage(dissert, adv_manager) \
                and count_dissertation_role < MAX_DISSERTATION_ROLE_FOR_ONE_DISSERTATION and dissert.status != 'DRAFT' \
                and adviser_of_dissert_role is not None and dissert is not None:
            justification = "%s %s %s" % (_("Manager add jury"), status_choice, adviser_of_dissert_role)
            dissertation_update.add(request, dissert, dissert.status, justification=justification)
            dissertation_role.add(status_choice, adviser_of_dissert_role, dissert)
            return HttpResponse(status=status.HTTP_200_OK)
        else:
            return HttpResponse(status=status.HTTP_403_FORBIDDEN)


@login_required
@cache_filter()
@user_passes_test(adviser.is_manager)
def manager_dissertations_list(request):
    disserts = Dissertation.objects.filter(
        education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        active=True).select_related('author__person',
                                    'education_group_year',
                                    'education_group_year__academic_year',
                                    'proposition_dissertation__author__person').distinct()
    offer_props = OfferProposition.objects.filter(
        education_group__facultyadviser__adviser__person__user=request.user).distinct()
    year = timezone.now().year
    academic_year_10y = AcademicYear.objects.filter(year__gte=year - 10, year__lte=year + 1)
    show_validation_commission = offer_proposition.show_validation_commission(offer_props)
    show_evaluation_first_year = offer_proposition.show_evaluation_first_year(offer_props)
    return render(request, 'manager_dissertations_list.html',
                  {'dissertations': disserts,
                   'show_validation_commission': show_validation_commission,
                   'show_evaluation_first_year': show_evaluation_first_year,
                   'academic_year_10y': academic_year_10y,
                   'offer_props': offer_props})


def generate_xls(disserts):
    workbook = Workbook()
    worksheet1 = workbook.active
    worksheet1.title = "dissertations"
    worksheet1.append(['Creation_date',
                       'Student',
                       'Title',
                       'Status',
                       'Program_start',
                       'Start_academic_year',
                       'Defend Year',
                       'Role 1',
                       'Teacher 1',
                       'Role 2',
                       'Teacher 2',
                       'Role 3',
                       'Teacher 3',
                       'Role 4',
                       'Teacher 4',
                       'Description'
                       ])
    for dissert in disserts:
        try:
            line = construct_line(dissert, include_description=True)
            worksheet1.append(line)
        except IllegalCharacterError:
            line = construct_line(dissert, include_description=False)
            worksheet1.append(line)

    return save_virtual_workbook(workbook)


def construct_line(dissert, include_description=True):
    defend_year = dissert.defend_year if dissert.defend_year else '---'
    description = dissert.description.encode('utf8', 'ignore') if dissert.description and include_description else '---'
    title = dissert.title.encode('utf8', 'ignore')

    line = [dissert.creation_date,
            str(dissert.author),
            title,
            dissert.status,
            str(dissert.education_group_year.acronym),
            str(dissert.education_group_year.academic_year),
            defend_year
            ]

    line += get_ordered_roles(dissert)
    line += [description]
    return line


def get_ordered_roles(dissert):
    roles = []
    for role in DissertationRole.objects.filter(dissertation=dissert). \
            order_by('status').select_related('adviser__person'):
        if role.status == DissertationRoleStatus.PROMOTEUR.name:
            roles.insert(0, str(role.adviser))
            roles.insert(0, str(role.status))
        else:
            roles.append(str(role.status))
            roles.append(str(role.adviser))
    for x in range(8 - len(roles)):
        roles += ['---']
    return roles


@login_required
@cache_filter()
@user_passes_test(adviser.is_manager)
def manager_dissertations_search(request):
    terms = request.GET.get('search', '')
    disserts = Dissertation.objects.filter(
        education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        active=True).filter(
        Q(author__person__first_name__icontains=terms) |
        Q(author__person__middle_name__icontains=terms) |
        Q(author__person__last_name__icontains=terms) |
        Q(description__icontains=terms) |
        Q(proposition_dissertation__title__icontains=terms) |
        Q(proposition_dissertation__author__person__first_name__icontains=terms) |
        Q(proposition_dissertation__author__person__middle_name__icontains=terms) |
        Q(proposition_dissertation__author__person__last_name__icontains=terms) |
        Q(status__icontains=terms) |
        Q(title__icontains=terms) |
        Q(education_group_year__acronym__icontains=terms)
    ).select_related('author__person',
                     'education_group_year',
                     'education_group_year__academic_year',
                     'proposition_dissertation__author__person').distinct()
    offer_prop_search = request.GET.get('offer_prop_search', '')
    academic_year_search = request.GET.get('academic_year', '')
    status_search = request.GET.get('status_search', '')

    if offer_prop_search != '':
        offer_prop_search = int(offer_prop_search)
        offer_prop = offer_proposition.find_by_id(offer_prop_search)
        disserts = disserts.filter(education_group_year__education_group=offer_prop.education_group)
    if academic_year_search != '':
        academic_year_search = int(academic_year_search)
        disserts = disserts.filter(
            education_group_year__academic_year=academic_year.find_academic_year_by_id(academic_year_search)
        )
    if status_search != '':
        disserts = disserts.filter(status=status_search)
    offer_props = OfferProposition.objects.filter(
        education_group__facultyadviser__adviser__person__user=request.user).distinct()
    show_validation_commission = offer_proposition.show_validation_commission(offer_props)
    show_evaluation_first_year = offer_proposition.show_evaluation_first_year(offer_props)
    year = timezone.now().year
    academic_year_10y = AcademicYear.objects.filter(year__gte=year - 10, year__lte=year + 1)

    if 'bt_xlsx' in request.GET:
        xls = generate_xls(disserts)
        filename = 'dissertations_{}.xlsx'.format(time.strftime("%Y-%m-%d_%H:%M"))
        response = HttpResponse(xls, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        response['Content-Disposition'] = "%s%s" % ("attachment; filename=", filename)
        return response

    else:
        return render(request, "manager_dissertations_list.html",
                      {'dissertations': disserts,
                       'show_validation_commission': show_validation_commission,
                       'show_evaluation_first_year': show_evaluation_first_year,
                       'academic_year_10y': academic_year_10y,
                       'offer_props': offer_props,
                       'offer_prop_search': offer_prop_search,
                       'academic_year_search': academic_year_search,
                       'status_search': status_search
                       })


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_delete(request, pk):
    dissert = dissertation.find_by_id(pk)
    dissert.deactivate()
    dissertation_update.add(request, dissert, dissert.status, justification=_("Delete dissertation"))
    return redirect('manager_dissertations_list')


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_role_delete(request, pk):
    dissert_role = dissertation_role.find_by_id(pk)
    if dissert_role is None:
        return redirect('manager_dissertations_list')
    dissert = dissert_role.dissertation
    person = mdl.person.find_by_user(request.user)
    adv = adviser.search_by_person(person)
    if adviser_can_manage(dissert, adv) and \
            _justification_dissert_role_delete_change(request, dissert, dissert_role, _("Manager deleted jury")):
        return redirect('manager_dissertations_detail', pk=dissert.pk)
    else:
        return redirect('manager_dissertations_list')


def _justification_dissert_role_delete_change(request, dissert, dissert_role, intitule):
    if dissert.status != 'DRAFT' and _role_can_be_deleted(dissert, dissert_role):
        justification = "%s %s" % (intitule, dissert_role)
        dissertation_update.add(request, dissert, dissert.status, justification=justification)
        dissert_role.delete()
        return True
    else:
        return False


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_role_delete_by_ajax(request, pk):
    dissert_role = get_object_or_404(DissertationRole.objects.select_related('dissertation'), pk=pk)
    dissert = dissert_role.dissertation
    adv = request.user.person.adviser
    if adviser_can_manage(dissert, adv) and \
            _justification_dissert_role_delete_change(request, dissert, dissert_role, _("Manager deleted jury")):
        return HttpResponse(status.HTTP_200_OK)
    else:
        return redirect('manager_dissertations_list')


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_to_dir_submit(request, pk):
    dissert = dissertation.find_by_id(pk)
    if dissert is None:
        return redirect('manager_dissertations_list')
    old_status = dissert.status
    new_status_display_str = new_status_display(dissert, "go_forward")
    if request.method == "POST":
        form = ManagerDissertationUpdateForm(request.POST)
        if form.is_valid():
            dissert.go_forward()
            data = form.cleaned_data
            justification = data['justification']
            dissertation_update.add(request, dissert, old_status, justification=justification)
            return redirect('manager_dissertations_detail', pk=pk)
    else:
        form = ManagerDissertationUpdateForm()
    return render(request, 'manager_dissertations_add_justification.html',
                  {'form': form, 'dissert': dissert, 'new_status_display': new_status_display_str})


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_to_dir_submit_list(request, pk):
    dissert = dissertation.find_by_id(pk)
    if dissert is None:
        return redirect('manager_dissertations_list')
    old_status = dissert.status
    dissert.go_forward()
    dissertation_update.add(request, dissert, old_status)
    return redirect('manager_dissertations_wait_recep_list')


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_to_dir_ok(request, pk):
    dissert = dissertation.find_by_id(pk)
    old_status = dissert.status
    new_status_display_result = new_status_display(dissert, "accept")
    if request.method == "POST":
        form = ManagerDissertationUpdateForm(request.POST)
        if form.is_valid():
            dissert.manager_accept()
            data = form.cleaned_data
            justification = data['justification']
            dissertation_update.add(request, dissert, old_status, justification=justification)
            return redirect('manager_dissertations_detail', pk=pk)
    else:
        form = ManagerDissertationUpdateForm()

    return render(request, 'manager_dissertations_add_justification.html',
                  {'form': form, 'dissert': dissert, 'new_status_display': new_status_display_result})


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_accept_comm_list(request, pk):
    dissert = dissertation.find_by_id(pk)
    if dissert is None:
        return redirect('manager_dissertations_list')
    old_status = dissert.status
    dissert.manager_accept()
    dissertation_update.add(request, dissert, old_status)
    return redirect('manager_dissertations_wait_comm_list')


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_accept_eval_list(request, pk):
    dissert = dissertation.find_by_id(pk)
    if dissert is None:
        return redirect('manager_dissertations_list')
    old_status = dissert.status
    dissert.manager_accept()
    dissertation_update.add(request, dissert, old_status)
    return redirect('manager_dissertations_wait_eval_list')


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_go_forward_from_list(request, pk, choice):
    dissert = dissertation.get_object_or_none(Dissertation, pk=pk)
    old_status = dissert.status
    if choice == 'ok':
        dissert.manager_accept()
    elif choice == 'ko':
        dissert.refuse()
    else:
        dissert.go_forward()
    dissertation_update.add(request, dissert, old_status)
    return redirect('manager_dissertations_search')


@login_required
@user_passes_test(adviser.is_manager)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def manager_dissertations_to_dir_ko(request, pk):
    dissert = get_object_or_404(Dissertation.objects.select_related('author__person'), pk=pk)
    old_status = dissert.status
    new_status_display_result = new_status_display(dissert, "refuse")
    if request.method == "POST":
        form = ManagerDissertationUpdateForm(request.POST)
        if form.is_valid():
            dissert.refuse()
            data = form.cleaned_data
            justification = data['justification']
            dissertation_update.add(request, dissert, old_status, justification=justification)
            return redirect('manager_dissertations_detail', pk=pk)
    else:
        form = ManagerDissertationUpdateForm()
    return render(request, 'manager_dissertations_add_justification.html',
                  {'form': form, 'dissert': dissert, 'new_status_display': new_status_display_result})


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_wait_list(request):
    offer_props = OfferProposition.objects.filter(
        education_group__facultyadviser__adviser__person__user=request.user).distinct()
    show_validation_commission = offer_proposition.show_validation_commission(offer_props)
    show_evaluation_first_year = offer_proposition.show_evaluation_first_year(offer_props)
    disserts = Dissertation.objects.filter(
        education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        active=True,
        status=dissertation_status.DIR_SUBMIT).select_related('author__person',
                                                              'education_group_year__academic_year',
                                                              'proposition_dissertation__author__person')
    return render(request, 'manager_dissertations_wait_list.html',
                  {'dissertations': disserts,
                   'show_validation_commission': show_validation_commission,
                   'show_evaluation_first_year': show_evaluation_first_year})


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_wait_comm_list(request):
    offer_props = OfferProposition.objects.filter(
        education_group__facultyadviser__adviser__person__user=request.user).distinct()
    all_advisers_array = str(adviser.convert_advisers_to_array(adviser.Adviser.objects.all().select_related('person')))
    show_validation_commission = offer_proposition.show_validation_commission(offer_props)
    show_evaluation_first_year = offer_proposition.show_evaluation_first_year(offer_props)
    return render(request, 'manager_dissertations_wait_commission_list.html',
                  {'show_validation_commission': show_validation_commission,
                   'STATUS_CHOICES': DissertationRoleStatus.choices(),
                   'show_evaluation_first_year': show_evaluation_first_year,
                   'all_advisers_array': all_advisers_array})


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_wait_comm_jsonlist(request):
    disserts = Dissertation.objects.filter(
        education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        status=dissertation_status.COM_SUBMIT,
        active=True).select_related('author__person',
                                    'education_group_year__academic_year',
                                    'education_group_year__education_group',
                                    'proposition_dissertation__author__person').distinct()
    dissert_waiting_list_json = [
        {
            'pk': dissert.pk,
            'title': dissert.title,
            'author': "{p.last_name} {p.first_name} ".format(p=dissert.author.person),
            'status': dissert.status,
            'education_group_year': str(dissert.education_group_year.academic_year),
            'education_groups': dissert.education_group_year.acronym,
            'proposition_dissertation': str(dissert.proposition_dissertation),
            'description': dissert.description
        } for dissert in disserts
    ]
    return JsonResponse(dissert_waiting_list_json, safe=False)


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertation_role_list_json(request, pk):
    dissert = get_object_or_404(Dissertation.objects.prefetch_related('dissertationrole_set__adviser__person'), pk=pk)
    dissert_roles = dissert.dissertationrole_set.all(). \
        order_by('status'). \
        select_related('dissertation', 'adviser__person')
    dissert_commission_sous_list = dissert_roles.values('pk', 'status',
                                                        first_name=F('adviser__person__first_name'),
                                                        middle_name=F('adviser__person__middle_name'),
                                                        last_name=F('adviser__person__last_name'),
                                                        dissert_pk=F('dissertation__pk')
                                                        )
    json_list = json.dumps(list(dissert_commission_sous_list))
    return HttpResponse(json_list, content_type='application/json')


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_wait_eval_list(request):
    education_groups = EducationGroup.objects.filter(facultyadviser__adviser__person__user=request.user)
    offer_props = OfferProposition.objects.filter(education_group__in=education_groups).distinct()
    show_validation_commission = offer_proposition.show_validation_commission(offer_props)
    show_evaluation_first_year = offer_proposition.show_evaluation_first_year(offer_props)
    disserts = Dissertation.objects.filter(
        education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        active=True,
        status=dissertation_status.EVA_SUBMIT).select_related('author__person',
                                                              'education_group_year__academic_year',
                                                              'proposition_dissertation__author__person')

    return render(request, 'manager_dissertations_wait_eval_list.html',
                  {'dissertations': disserts,
                   'show_validation_commission': show_validation_commission,
                   'show_evaluation_first_year': show_evaluation_first_year})


@login_required
@user_passes_test(adviser.is_manager)
def manager_dissertations_wait_recep_list(request):
    offer_props = OfferProposition.objects.filter(education_group__facultyadviser__adviser__person__user=request.user). \
        distinct()
    show_validation_commission = offer_proposition.show_validation_commission(offer_props)
    show_evaluation_first_year = offer_proposition.show_evaluation_first_year(offer_props)
    disserts = Dissertation.objects.filter(
        education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        active=True,
        status=dissertation_status.TO_RECEIVE).select_related('author__person',
                                                              'education_group_year__academic_year',
                                                              'proposition_dissertation__author__person')
    return render(request, 'manager_dissertations_wait_recep_list.html',
                  {'dissertations': disserts,
                   'show_validation_commission': show_validation_commission,
                   'show_evaluation_first_year': show_evaluation_first_year})


@login_required
@user_passes_test(adviser.is_manager)
def manager_students_list(request):
    student_with_enrollement_in_education_groups = Student.objects.filter(
        offerenrollment__education_group_year__education_group__facultyadviser__adviser__person__user=request.user,
        offerenrollment__education_group_year__academic_year=mdl.academic_year.current_academic_year()
    ).select_related('person'). \
        prefetch_related('dissertation_set__education_group_year__education_group',
                         'dissertation_set__education_group_year__academic_year',
                         'offerenrollment_set__education_group_year__academic_year').distinct()
    return render(request,
                  'manager_students_list.html',
                  {'students': student_with_enrollement_in_education_groups})


###########################
#      TEACHER VIEWS      #
###########################

@login_required
@user_passes_test(adviser.is_teacher)
def dissertations_list(request):
    adv = request.user.person.adviser
    dissert_role = DissertationRole.objects \
        .filter(adviser=adv, dissertation__active=True) \
        .exclude(dissertation__status__in=[dissertation_status.DRAFT,
                                           dissertation_status.ENDED,
                                           dissertation_status.ENDED_WIN,
                                           dissertation_status.ENDED_LOS]) \
        .select_related('dissertation__author__person',
                        'dissertation__education_group_year__academic_year',
                        'dissertation__proposition_dissertation__author__person'
                        ).order_by('dissertation__status',
                                   'dissertation__author__person__last_name',
                                   'dissertation__author__person__first_name'
                                   )
    adviser_list_dissertations = dissert_role.filter(status=DissertationRoleStatus.PROMOTEUR.name)
    adviser_list_dissertations_copro = dissert_role.filter(status=DissertationRoleStatus.CO_PROMOTEUR.name)
    adviser_list_dissertations_reader = dissert_role.filter(status=DissertationRoleStatus.READER.name)
    adviser_list_dissertations_accompanist = dissert_role.filter(status=DissertationRoleStatus.ACCOMPANIST.name)
    adviser_list_dissertations_internship = dissert_role.filter(status=DissertationRoleStatus.INTERNSHIP.name)
    adviser_list_dissertations_president = dissert_role.filter(status=DissertationRoleStatus.PRESIDENT.name)
    adviser_list_dissertations_history = \
        DissertationRole.objects.filter(adviser=adv,
                                        dissertation__active=True,
                                        dissertation__status__in=[dissertation_status.ENDED,
                                                                  dissertation_status.ENDED_WIN,
                                                                  dissertation_status.ENDED_LOS]).select_related(
            'dissertation__author__person',
            'dissertation__education_group_year__academic_year',
            'dissertation__proposition_dissertation__author__person'
        ).order_by('dissertation__creation_date')
    return render(request, "dissertations_list.html", locals())


def teacher_can_see_dissertation(adv, dissert):
    return dissertation_role.count_by_adviser_dissertation(adv, dissert) > 0


def teacher_is_promotor(adv, dissert):
    return dissertation_role.count_by_status_adviser_dissertation('PROMOTEUR', adv, dissert) > 0


@login_required
@check_for_dissert(adviser_is_in_jury)
def dissertations_detail(request, pk):
    dissert = get_object_or_404(Dissertation.objects.select_related(
        'author__person',
        'location',
        'education_group_year__academic_year',
        'proposition_dissertation__author__person',
        'education_group_year__education_group__offer_proposition'
    ).prefetch_related('advisers'), pk=pk)
    adv = request.user.person.adviser
    if teacher_can_see_dissertation(adv, dissert):
        count_dissertation_role = DissertationRole.objects.filter(dissertation=dissert).count()
        offer_prop = dissert.education_group_year.education_group.offer_proposition
        promotors_count = DissertationRole.objects.filter(dissertation=dissert). \
            filter(status=DissertationRoleStatus.PROMOTEUR.name).count()
        dissertation_roles = dissert.dissertationrole_set.all().order_by('status'). \
            select_related('adviser__person')
        dissertation_file_form = DissertationFileForm(
            instance=dissert
        )
        proposition_dissertation_file_form = PropositionDissertationFileForm(
            instance=dissert.proposition_dissertation
        )
        return render(
            request,
            'dissertations_detail.html',
            {
                'dissertation': dissert,
                'adviser': adv,
                'dissertation_roles': dissertation_roles,
                'count_dissertation_role': count_dissertation_role,
                'offer_prop': offer_prop,
                'promotors_count': promotors_count,
                'teacher_is_promotor': teacher_is_promotor(adv, dissert),
                'dissertation_file_form': dissertation_file_form,
                'dissertation_file': dissertation_file_form.initial['dissertation_file'],
                'proposition_dissertation_file_form': proposition_dissertation_file_form,
                'proposition_dissertation_file':
                    proposition_dissertation_file_form.initial['proposition_dissertation_file']
            }
        )
    else:
        return redirect('dissertations_list')


@login_required
@check_for_dissert(adviser_is_in_jury)
def dissertations_detail_updates(request, pk):
    dissert = get_object_or_404(Dissertation.objects.prefetch_related('dissertationupdate_set'), pk=pk)
    adv = request.user.person.adviser
    dissertation_updates = dissert.dissertationupdate_set.all().order_by('created').select_related('person')
    return render(request, 'dissertations_detail_updates.html',
                  {'dissertation': dissert,
                   'adviser': adv,
                   'dissertation_updates': dissertation_updates
                   }
                  )


@login_required
@user_passes_test(adviser.is_teacher)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def dissertations_to_dir_ok(request, pk):
    dissert = get_object_or_404(Dissertation.objects.select_related('author__person'), pk=pk)
    old_status = dissert.status
    new_status_display_result = new_status_display(dissert, "accept")
    if request.method == "POST":
        form = ManagerDissertationUpdateForm(request.POST)
        if form.is_valid():
            dissert.teacher_accept()
            data = form.cleaned_data
            justification = data['justification']
            dissertation_update.add(request, dissert, old_status, justification=justification)
            return redirect('dissertations_detail', pk=pk)
    else:
        form = ManagerDissertationUpdateForm()
    return render(
        request,
        'dissertations_add_justification.html',
        {
            'form': form,
            'dissert': dissert,
            'new_status_display': new_status_display_result
        }
    )


@login_required
@user_passes_test(adviser.is_teacher)
@check_for_dissert(autorized_dissert_promotor_or_manager)
def dissertations_to_dir_ko(request, pk):
    dissert = get_object_or_404(Dissertation.objects.select_related('author__person'), pk=pk)
    old_status = dissert.status
    new_status_display_result = new_status_display(dissert, "refuse")
    if request.method == "POST":
        form = ManagerDissertationUpdateForm(request.POST)
        if form.is_valid():
            dissert.refuse()
            data = form.cleaned_data
            justification = data['justification']
            dissertation_update.add(request, dissert, old_status, justification=justification)
            return redirect('dissertations_detail', pk=pk)
    else:
        form = ManagerDissertationUpdateForm()
    return render(
        request,
        'dissertations_add_justification.html',
        {
            'form': form,
            'dissert': dissert,
            'new_status_display': new_status_display_result
        }
    )


@login_required
@user_passes_test(adviser.is_teacher)
def dissertations_wait_list(request):
    adv = request.user.person.adviser
    disserts = Dissertation.objects.filter(
        active=True,
        status=dissertation_status.DIR_SUBMIT,
    ).filter(Q(
        dissertationrole__adviser=adv,
        dissertationrole__status=DissertationRoleStatus.PROMOTEUR.name
    )).order_by('author__person__last_name') \
        .select_related('author__person',
                        'education_group_year__academic_year',
                        'proposition_dissertation__author__person')
    return render(request, 'dissertations_wait_list.html', {'disserts': disserts})


@login_required
@user_passes_test(adviser.is_teacher)
def dissertations_role_delete(request, pk):
    dissert_role = dissertation_role.find_by_id(pk)
    if dissert_role is None:
        return redirect('dissertations_list')
    dissert = dissert_role.dissertation
    person = mdl.person.find_by_user(request.user)
    adv = adviser.search_by_person(person)
    offer_prop = offer_proposition.get_by_dissertation(dissert)
    if offer_prop is not None and teacher_is_promotor(adv, dissert):
        if offer_prop.adviser_can_suggest_reader and _role_can_be_deleted(dissert, dissert_role):
            justification = "%s %s" % (_("Teacher deleted jury"), str(dissert_role))
            dissertation_update.add(request, dissert, dissert.status, justification=justification)
            dissert_role.delete()

    return redirect('dissertations_detail', pk=dissert.pk)


class DissertationJuryNewView(AjaxTemplateMixin, UserPassesTestMixin, CreateView):
    model = DissertationRole
    template_name = 'dissertations_jury_edit_inner.html'
    form_class = ManagerDissertationRoleForm
    raise_exception = True

    def test_func(self):
        dissert = self.dissertation
        count_dissertation_role = dissertation_role.count_by_dissertation(dissert)
        offer_prop = offer_proposition.get_by_dissertation(dissert)
        if offer_prop is not None:
            if adviser.is_teacher(self.request.user):
                return offer_prop.adviser_can_suggest_reader \
                       and count_dissertation_role < MAX_DISSERTATION_ROLE_FOR_ONE_DISSERTATION
            if adviser.is_manager(self.request.user):
                return count_dissertation_role < MAX_DISSERTATION_ROLE_FOR_ONE_DISSERTATION \
                       and dissert.status != 'DRAFT'

    def dispatch(self, request, *args, **kwargs):
        if autorized_dissert_promotor_or_manager(request.user, self.dissertation.pk):
            return super().dispatch(request, *args, **kwargs)
        return redirect('dissertations')

    @cached_property
    def dissertation(self):
        return get_object_or_404(dissertation.Dissertation, pk=self.kwargs['pk'])

    def get_initial(self):
        return {'status': dissertation_role_status, 'dissertation': self.dissertation}

    def get_form_kwargs(self):
        form_kwargs = super().get_form_kwargs()
        form_kwargs['request'] = self.request
        return form_kwargs

    def get_context_data(self, **kwargs):
        context = super(DissertationJuryNewView, self).get_context_data(**kwargs)
        context['manager'] = adviser.is_manager(self.request.user)
        return context

    def get_success_url(self):
        return None


class AdviserAutocomplete(autocomplete.Select2QuerySetView):
    def get_result_label(self, item):
        return "{} {}, {}".format(item.person.last_name, item.person.first_name, item.person.email)

    def get_queryset(self):
        qs = Adviser.objects.all().select_related("person").order_by("person__last_name")
        if self.q:
            qs = qs.filter(Q(person__last_name__icontains=self.q) | Q(person__first_name__icontains=self.q))
        return qs