# -*- mode: python -*-
# -*- coding: utf-8 -*-

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2021 Emmanuel Viennet.  All rights reserved.
#
# 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 2 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   Emmanuel Viennet      emmanuel.viennet@viennet.net
#
##############################################################################

"""Fonctions sur les moduleimpl
"""
# codes anciens déplacés de ZEntreprise
import datetime

import sco_utils as scu
from notesdb import ScoDocCursor, EditableTable, DateISOtoDMY, DateDMYtoISO
from sco_permissions import ScoImplement
from sco_exceptions import ScoValueError, AccessDenied
from notes_log import log
import scolog
import sco_formsemestre

# --- Gestion des "Implémentations de Modules"
# Un "moduleimpl" correspond a la mise en oeuvre d'un module
# dans une formation spécifique, à une date spécifique.
_moduleimplEditor = EditableTable(
    "notes_moduleimpl",
    "moduleimpl_id",
    (
        "moduleimpl_id",
        "module_id",
        "formsemestre_id",
        "responsable_id",
        "computation_expr",
    ),
)

_modules_enseignantsEditor = EditableTable(
    "notes_modules_enseignants",
    "modules_enseignants_id",
    ("modules_enseignants_id", "moduleimpl_id", "ens_id"),
)


def do_moduleimpl_create(context, args):
    "create a moduleimpl"
    cnx = context.GetDBConnexion()
    r = _moduleimplEditor.create(cnx, args)
    context._inval_cache(
        formsemestre_id=args["formsemestre_id"]
    )  # > creation moduleimpl
    return r


def do_moduleimpl_delete(context, oid, formsemestre_id=None):
    "delete moduleimpl (desinscrit tous les etudiants)"
    cnx = context.GetDBConnexion()
    # --- desinscription des etudiants
    cursor = cnx.cursor(cursor_factory=ScoDocCursor)
    req = (
        "DELETE FROM notes_moduleimpl_inscription WHERE moduleimpl_id=%(moduleimpl_id)s"
    )
    cursor.execute(req, {"moduleimpl_id": oid})
    # --- suppression des enseignants
    cursor.execute(
        "DELETE FROM notes_modules_enseignants WHERE moduleimpl_id=%(moduleimpl_id)s",
        {"moduleimpl_id": oid},
    )
    # --- suppression des references dans les absences
    cursor.execute(
        "UPDATE absences SET moduleimpl_id=NULL WHERE moduleimpl_id=%(moduleimpl_id)s",
        {"moduleimpl_id": oid},
    )
    # --- destruction du moduleimpl
    _moduleimplEditor.delete(cnx, oid)
    context._inval_cache(formsemestre_id=formsemestre_id)  # > moduleimpl_delete


def do_moduleimpl_list(
    context, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None
):
    "list moduleimpls"
    args = locals()
    cnx = context.GetDBConnexion()
    modimpls = _moduleimplEditor.list(cnx, args)  # *args, **kw)
    # Ajoute la liste des enseignants
    for mo in modimpls:
        mo["ens"] = do_ens_list(context, args={"moduleimpl_id": mo["moduleimpl_id"]})
    return scu.return_text_if_published(modimpls, REQUEST)


def do_moduleimpl_edit(context, args, formsemestre_id=None, cnx=None):
    "edit a moduleimpl"
    if not cnx:
        cnx = context.GetDBConnexion()
    _moduleimplEditor.edit(cnx, args)

    context._inval_cache(formsemestre_id=formsemestre_id)  # > modif moduleimpl


def do_moduleimpl_withmodule_list(
    context, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None
):
    """Liste les moduleimpls et ajoute dans chacun le module correspondant
    Tri la liste par semestre/UE/numero_matiere/numero_module.

    Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée.
    """
    args = locals()
    del args["context"]
    del args["REQUEST"]
    modimpls = do_moduleimpl_list(context, **args)
    for mo in modimpls:
        mo["module"] = context.do_module_list(args={"module_id": mo["module_id"]})[0]
        mo["ue"] = context.do_ue_list(args={"ue_id": mo["module"]["ue_id"]})[0]
        mo["matiere"] = context.do_matiere_list(
            args={"matiere_id": mo["module"]["matiere_id"]}
        )[0]

    # tri par semestre/UE/numero_matiere/numero_module
    extr = lambda x: (
        x["ue"]["numero"],
        x["ue"]["ue_id"],
        x["matiere"]["numero"],
        x["matiere"]["matiere_id"],
        x["module"]["numero"],
        x["module"]["code"],
    )
    modimpls.sort(lambda x, y: cmp(extr(x), extr(y)))

    return scu.return_text_if_published(modimpls, REQUEST)


def do_moduleimpl_inscription_list(
    context, moduleimpl_id=None, etudid=None, REQUEST=None
):
    "list moduleimpl_inscriptions"
    args = locals()
    cnx = context.GetDBConnexion()
    return scu.return_text_if_published(
        _moduleimpl_inscriptionEditor.list(cnx, args), REQUEST
    )


def do_moduleimpl_listeetuds(context, moduleimpl_id):
    "retourne liste des etudids inscrits a ce module"
    req = "select distinct Im.etudid from notes_moduleimpl_inscription Im, notes_formsemestre_inscription Isem, notes_moduleimpl M where Isem.etudid=Im.etudid and Im.moduleimpl_id=M.moduleimpl_id and M.moduleimpl_id = %(moduleimpl_id)s"
    cnx = context.GetDBConnexion()
    cursor = cnx.cursor(cursor_factory=ScoDocCursor)
    cursor.execute(req, {"moduleimpl_id": moduleimpl_id})
    res = cursor.fetchall()
    return [x[0] for x in res]


def do_moduleimpl_inscrit_tout_semestre(context, moduleimpl_id, formsemestre_id):
    "inscrit tous les etudiants inscrit au semestre a ce module"
    # UNUSED
    cnx = context.GetDBConnexion()
    cursor = cnx.cursor(cursor_factory=ScoDocCursor)
    req = """INSERT INTO notes_moduleimpl_inscription
                            (moduleimpl_id, etudid)
                SELECT %(moduleimpl_id)s, I.etudid
                FROM  notes_formsemestre_inscription I
                WHERE I.formsemestre_id=%(formsemestre_id)s"""
    args = {"moduleimpl_id": moduleimpl_id, "formsemestre_id": formsemestre_id}
    cursor.execute(req, args)


# --- Inscriptions aux modules
_moduleimpl_inscriptionEditor = EditableTable(
    "notes_moduleimpl_inscription",
    "moduleimpl_inscription_id",
    ("moduleimpl_inscription_id", "etudid", "moduleimpl_id"),
)


def do_moduleimpl_inscription_create(context, args, REQUEST=None, formsemestre_id=None):
    "create a moduleimpl_inscription"
    cnx = context.GetDBConnexion()
    log("do_moduleimpl_inscription_create: " + str(args))
    r = _moduleimpl_inscriptionEditor.create(cnx, args)
    context._inval_cache(formsemestre_id=formsemestre_id)  # > moduleimpl_inscription
    if REQUEST:
        scolog.logdb(
            REQUEST,
            cnx,
            method="moduleimpl_inscription",
            etudid=args["etudid"],
            msg="inscription module %s" % args["moduleimpl_id"],
            commit=False,
        )
    return r


def do_moduleimpl_inscription_delete(context, oid, formsemestre_id=None):
    "delete moduleimpl_inscription"
    cnx = context.GetDBConnexion()
    _moduleimpl_inscriptionEditor.delete(cnx, oid)
    context._inval_cache(formsemestre_id=formsemestre_id)  # > moduleimpl_inscription


def do_moduleimpl_inscrit_etuds(
    context, moduleimpl_id, formsemestre_id, etudids, reset=False, REQUEST=None
):
    """Inscrit les etudiants (liste d'etudids) a ce module.
    Si reset, desinscrit tous les autres.
    """
    # Verifie qu'ils sont tous bien inscrits au semestre
    for etudid in etudids:
        insem = context.do_formsemestre_inscription_list(
            args={"formsemestre_id": formsemestre_id, "etudid": etudid}
        )
        if not insem:
            raise ScoValueError("%s n'est pas inscrit au semestre !" % etudid)

    # Desinscriptions
    if reset:
        cnx = context.GetDBConnexion()
        cursor = cnx.cursor(cursor_factory=ScoDocCursor)
        cursor.execute(
            "delete from notes_moduleimpl_inscription where moduleimpl_id = %(moduleimpl_id)s",
            {"moduleimpl_id": moduleimpl_id},
        )
    # Inscriptions au module:
    inmod_set = set(
        [
            # hum ?
            x["etudid"]
            for x in do_moduleimpl_inscription_list(
                context, moduleimpl_id=moduleimpl_id
            )
        ]
    )
    for etudid in etudids:
        # deja inscrit ?
        if not etudid in inmod_set:
            do_moduleimpl_inscription_create(
                context,
                {"moduleimpl_id": moduleimpl_id, "etudid": etudid},
                REQUEST=REQUEST,
                formsemestre_id=formsemestre_id,
            )

    context._inval_cache(formsemestre_id=formsemestre_id)  # > moduleimpl_inscrit_etuds


def do_ens_list(context, *args, **kw):
    "liste les enseignants d'un moduleimpl (pas le responsable)"
    cnx = context.GetDBConnexion()
    ens = _modules_enseignantsEditor.list(cnx, *args, **kw)
    return ens


def do_ens_edit(context, *args, **kw):
    "edit ens"
    cnx = context.GetDBConnexion()
    _modules_enseignantsEditor.edit(cnx, *args, **kw)


def do_ens_create(context, args):
    "create ens"
    cnx = context.GetDBConnexion()
    r = _modules_enseignantsEditor.create(cnx, args)
    return r


def do_ens_delete(context, oid):
    "delete ens"
    cnx = context.GetDBConnexion()
    r = _modules_enseignantsEditor.delete(cnx, oid)
    return r


def can_change_module_resp(context, REQUEST, moduleimpl_id):
    """Check if current user can modify module resp. (raise exception if not).
    = Admin, et dir des etud. (si option l'y autorise)
    """
    M = do_moduleimpl_withmodule_list(context, moduleimpl_id=moduleimpl_id)[0]
    # -- check lock
    sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
    if sem["etat"] != "1":
        raise ScoValueError("Modification impossible: semestre verrouille")
    # -- check access
    authuser = REQUEST.AUTHENTICATED_USER
    uid = str(authuser)
    # admin ou resp. semestre avec flag resp_can_change_resp
    if not authuser.has_permission(ScoImplement, context) and (
        (uid not in sem["responsables"]) or (not sem["resp_can_change_ens"])
    ):
        raise AccessDenied("Modification impossible pour %s" % uid)
    return M, sem


def can_change_ens(context, REQUEST, moduleimpl_id, raise_exc=True):
    "check if current user can modify ens list (raise exception if not)"
    M = do_moduleimpl_withmodule_list(context, moduleimpl_id=moduleimpl_id)[0]
    # -- check lock
    sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
    if sem["etat"] != "1":
        if raise_exc:
            raise ScoValueError("Modification impossible: semestre verrouille")
        else:
            return False
    # -- check access
    authuser = REQUEST.AUTHENTICATED_USER
    uid = str(authuser)
    # admin, resp. module ou resp. semestre
    if (
        uid != M["responsable_id"]
        and not authuser.has_permission(ScoImplement, context)
        and (uid not in sem["responsables"])
    ):
        if raise_exc:
            raise AccessDenied("Modification impossible pour %s" % uid)
        else:
            return False
    return M, sem