#! /usr/bin/python3
# -*- coding: utf-8 -*-
###########################################################################
#
# Eole NG
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
# eole@ac-dijon.fr
#
###########################################################################

"""

Outil d'importation scribe
==========================

Script d'importation des données du ldap à partir de fichiers plats (xml ou csv)

choix de la source de données et imports
----------------------------------------

- SCONET / STS-Web

  - élèves et responsables
  - enseignants
  - personnels administratifs

- AAF

  - élèves et responsables
  - enseignants
  - personnels administratifs

- Be1d

  - pas d'options (que les élèves)

- CSV2

  - élèves
  - enseignants
  - personnels administratifs
  - comptes invités

"""
import sys
from os import environ, getcwd, chdir
from os.path import isfile, dirname
from pyeole.process import system_out, system_code
from scribe.storage import init_store
from scribe.eoleldap import Ldap
from scribe.ldapconf import SUPPORT_ETAB
from scribe.eoletools import nscd_start, nscd_stop
from scribe.parsing import sconet, aaf, be1d, scribecsv2
from scribe.importation import preferences, writer, config
from scribe.importation import log

#______________________________________________________________________________
# utilitaires de manipulation de la console

class OutOfRange(Exception):
    """ Exception OutOfRange """
    pass


def select_list(selection, default=''):
    """
    Utilitaire de construction d'une sélection en ligne de commande
    :param: selection liste
    :return:  l'identifiant sélectionné (entier)
    """
    def_sel = 0
    # affichage de la liste (ordonnée)
    for item in selection:
        if item == default:
            def_sel = selection.index(item)
        print(selection.index(item) , ':', item)
    # recuperation du numero
    print()
    try:
        choice = input("Choisissez un nombre dans la liste [%s] : " % def_sel)
        if choice == '':
            choice = def_sel
        number = int(choice)
        if number not in list(range(len(selection))):
            raise OutOfRange("Erreur: de choix")
    except:
        print("attention : nombre non pris en compte !")
        return select_list(selection)
    print()
    return selection[number]

def select_label_list(selection, default=''):
    """
    Utilitaire de construction d'une sélection en ligne de commande
    :param: selection (liste de tuples)
    :return:  l'identifiant sélectionné (entier)
    """
    def_sel = 0
    # affichage de la liste (ordonnée)
    for indice, couple in enumerate(selection):
        if couple[0] == default:
            def_sel = indice
        print(indice, ':', couple[1])
    # recuperation du numero
    print()
    try:
        choice = input("Choisissez un nombre dans la liste [%s] : " % def_sel)
        if choice == '':
            choice = def_sel
        number = int(choice)
        if number not in list(range(len(selection))):
            raise OutOfRange("Erreur: de choix")
    except:
        print("attention : nombre non pris en compte !")
        return select_label_list(selection)
    print()
    return selection[number][0]

def text_input(default):
    """
    entrée texte avec valeur par défaut
    """
    choice = input("[%s] : " % default)
    if choice == '':
        choice = default
    print()
    return choice

def integer_input(default):
    """
    entrée texte avec valeur par défaut et test entier
    """
    choice = input("[%s] : " % default)
    if choice == '':
        choice = default
    try:
        int(choice)
    except:
        print("attention : nombre entier attendu !")
        return integer_input(default)
    print()
    return choice

def ask_prefs(prefs):
    """
    Affichage des questions sur les préférences
    """
    for pref in prefs.preferences:
        print(pref.question, ':')
        if pref._type in ['liste', 'select']:
            rep = select_label_list(pref.options, pref.default)
        elif pref._type == 'entier':
            rep = integer_input(pref.default)
        else:
            rep = text_input(pref.default)
        prefs.set_default(pref.name, rep)
    prefs.save()

def ask_eleves_prefs():
    """
    préférences élèves
    """
    print("************************")
    print("** Préférences Elèves **")
    print("************************")
    print()
    ask_prefs(preferences.get_eleves_prefs())

def ask_responsables_prefs():
    """
    préférences responsables
    """
    print("******************************")
    print("** Préférences Responsables **")
    print("******************************")
    print()
    ask_prefs(preferences.get_responsables_prefs())

def ask_enseignants_prefs():
    """
    préférences enseignants
    """
    print("*****************************")
    print("** Préférences Enseignants **")
    print("*****************************")
    print()
    ask_prefs(preferences.get_enseignants_prefs())

def ask_administratifs_prefs():
    """
    préférences administratifs
    """
    print("********************************")
    print("** Préférences Administratifs **")
    print("********************************")
    print()
    ask_prefs(preferences.get_administratifs_prefs())

def ask_invites_prefs():
    """
    préférences comptes invités
    """
    print("*************************")
    print("** Préférences Invités **")
    print("*************************")
    print()
    ask_prefs(preferences.get_invites_prefs())


#______________________________________________________________________________

class Console:
    """
        Questions à utiliser en mode console
    """
    def __init__(self):

        self.store = init_store(config.DB_DIR)
        self.import_type = None
        self.data_type = None
        self.data_category = None
        self.do_reader = None
        self.do_writer = None
        self.last_path = ''

#______________________________________________________________________________

        self.reader = {

        ('sconet', 'eleve'): self.do_sconet_eleve_resp,
        ('sconet', 'eleve_ss_resp'): self.do_sconet_eleve_resp,
        ('sconet', 'enseignant'): self.do_sconet_enseignant,

        ('aaf', 'eleve'): self.do_aaf_eleve_resp,
        ('aaf', 'eleve_ss_resp'): self.do_aaf_eleve_resp,
        ('aaf', 'enseignant'): self.do_aaf_enseignant,

        ('be1d', 'eleve'): self.do_be1d_eleve,
        ('be1d', 'eleve_ss_resp'): self.do_be1d_eleve,

        ('csv2', 'eleve_ss_resp'): self.csv2_eleve,
        ('csv2', 'enseignant'): self.csv2_enseignant,
        ('csv2', 'administratif'): self.csv2_administratif,
        ('csv2', 'autre'): self.csv2_autre,

        }

        self.writer = {

        'eleve': self.do_write_eleve,
        'eleve_ss_resp': self.do_write_eleve,
        'enseignant': self.do_write_enseignant,
        'administratif': self.do_write_administratif,
        'autre': self.do_write_invite,
        }

#______________________________________________________________________________


    def run(self):
        """
            point d'entrée de la console
        """
        # type d'importation
        self.do_import_type()
        # type de données (aaf, sconet..)
        self.do_data_type()
        # categories (enseignants, élèves..)
        self.do_data_category()
        # lecture des données
        self.do_reader()
        # écriture des données
        self.do_writer()
        print()

    def do_import_type(self):
        """
            demande le type d'Import
        """
        print("""Type d'importation à réaliser""")
        types = [('maj', "Mise à jour de comptes"),
                 ('annu', "Importation annuelle")]
        self.import_type = select_label_list(types)
        log.log.info("type d'import : %s" % self.import_type)

    def do_data_type(self):
        """
            demande le type de données (Sconet, aaf, ...)
        """
        print("""Quelle source de données voulez-vous utiliser ?""")
        self.data_type = select_label_list(config.DATA_TYPE)
        log.log.info("source de données : %s" % self.data_type)

    def do_data_category(self):
        """
            demande le type de traitement de données (élèves, responsables...)
        """
        print("""Quelles données voulez-vous importer ?""")
        self.data_category = select_label_list(config.CATEGORIES[self.data_type])
        log.log.info("catégorie d'utilisateurs : %s" % self.data_category)
        self.do_reader = self.reader[(self.data_type, self.data_category)]
        self.do_writer = self.writer[self.data_category]

#______________________________________________________________________________
# sconet

    def do_sconet_eleve_resp(self):
        """
            traitement sconet élèves et responsables
        """
        ask_eleves_prefs()
        if self.data_category != 'eleve_ss_resp':
            ask_responsables_prefs()
        eleve_file = self._filechooser("Fichier Sconet Eleves (ex : ElevesSansAdresses.xml)")
        nomenclature_file = self._filechooser("Fichier Sconet Nomenclature (ex : Nomenclature.xml)")
        if self.data_category != 'eleve_ss_resp':
            responsable_file = self._filechooser("Fichier Sconet Responsables (ex : ResponsablesAvecAdresses.xml)")
        structure_file = self._filechooser("Fichier Sconet Structures (ex : Structures.xml)")

        sconet.parse_sco_divisions(self.store, structure_file, nomenclature_file)
        sconet.parse_sco_groupes(self.store, structure_file)
        sconet.parse_sco_eleves(self.store, eleve_file)
        if self.data_category != 'eleve_ss_resp':
            sconet.parse_sco_responsables(self.store, responsable_file)

    def do_sconet_enseignant(self):
        """
            traitement sconet enseignants
        """
        ask_enseignants_prefs()
        ask_administratifs_prefs()
        ens_file = self._filechooser("Fichier STS Personnels (exemple : sts_emp_xxx_xxx.xml)")
        sconet.parse_sts_profs(self.store, ens_file)
#______________________________________________________________________________
# aaf

    def do_aaf_eleve_resp(self):
        """
            traitement aaf élèves et responsables
        """
        ask_eleves_prefs()
        if self.data_category != 'eleve_ss_resp':
            ask_responsables_prefs()
        eleve_file = self._filechooser("Fichier AAF Eleves (exemple : ENT_xxx_Eleve_0000.xml)")
        if self.data_category != 'eleve_ss_resp':
            responsable_file = self._filechooser("Fichier AAF Responsables (ENT_xxx_PersRelEleve_0000.xml)")
        if self.data_category != 'eleve_ss_resp':
            aaf.parse_aaf_responsables(self.store, responsable_file)
        aaf.parse_aaf_eleves(self.store, eleve_file)


    def do_aaf_enseignant(self):
        """
            traitement aaf enseignants
        """
        ask_enseignants_prefs()
        ask_administratifs_prefs()
        ens_file = self._filechooser("Fichier AAF Personnels (exemple : ENT_xxx_PersEducNat_0000.xml)")
        aaf.parse_aaf_profs(self.store, ens_file)

#______________________________________________________________________________
# be1d

    def do_be1d_eleve(self):
        """
            traitement be1d élèves
            (pour l'instant le seul choix be1d)
        """
        ask_eleves_prefs()
        if self.data_category != 'eleve_ss_resp':
            ask_responsables_prefs()
        ele_file = self._filechooser("Fichier CSV ONDE/BE1D élèves (exemple : CSVExtraction.csv)")
        if self.data_category != 'eleve_ss_resp':
            responsable_file = self._filechooser("Fichier CSV ONDE/BE1D responsables (exemple : parents.csv)")
        be1d.parse_be1d_eleves(self.store, ele_file)
        if self.data_category != 'eleve_ss_resp':
            be1d.parse_be1d_responsables(self.store, responsable_file)

#______________________________________________________________________________
# csv2

    def csv2_eleve(self):
        """
            traitement csv élèves
        """
        ask_eleves_prefs()
        ele_file = self._filechooser("Fichier CSV élèves (exemple : eleves.csv)")
        scribecsv2.parse_csv_eleves(self.store, ele_file)

    def csv2_enseignant(self):
        """
            traitement csv enseignants
        """
        ask_enseignants_prefs()
        ens_file = self._filechooser("Fichier CSV enseignants (exemple : professeurs.csv)")
        scribecsv2.parse_csv_profs(self.store, ens_file)

    def csv2_administratif(self):
        """
            traitement csv administratifs
        """
        ask_administratifs_prefs()
        adm_file = self._filechooser("Fichier CSV des personnels administratifs (exemple : personnels.csv)")
        scribecsv2.parse_csv_administratifs(self.store, adm_file)

    def csv2_autre(self):
        """
        """
        ask_invites_prefs()
        inv_file = self._filechooser("Fichier CSV invités (exemple : comptes.csv)")
        scribecsv2.parse_csv_invites(self.store, inv_file)


#______________________________________________________________________________
# writer eleve

    def do_write_eleve(self):
        """
            écriture des élèves
        """
        log.add_lock()
        log.debuglog("Arrêt de LSC...", title=True)
        nscd_stop()
        connexion = Ldap()
        connexion.connect()
        if SUPPORT_ETAB:
            prefs = preferences.get_eleves_prefs()
            etab = prefs.get_default('etab')
            etab_prefix = prefs.get_default('etab_prefix')
        else:
            etab = None
            etab_prefix = ''
        if self.import_type != 'maj':
            writer.purge_eleves_options(connexion=connexion, etab=etab)
        writer.write_niveau(store=self.store, connexion=connexion, etab=etab,
                etab_prefix=etab_prefix)
        writer.write_classe(store=self.store, connexion=connexion,
                etab_prefix=etab_prefix)
        if config.WRITE_OPTS == "oui":
            writer.write_option(store=self.store, connexion=connexion,
                    etab=etab, etab_prefix=etab_prefix)
        writer.write_eleve(store=self.store, connexion=connexion, etab=etab)
        if self.data_category != 'eleve_ss_resp':
            writer.write_responsable(store=self.store, connexion=connexion)
        writer.write_samba(connexion)
        connexion.close()
        log.debuglog("Démarrage de LSC...", title=True)
        nscd_start()
        log.del_lock()

#______________________________________________________________________________
# writer enseignant

    def do_write_enseignant(self):
        """
            écriture des enseignants
        """
        log.add_lock()
        log.debuglog("Arrêt de LSC...", title=True)
        nscd_stop()
        connexion = Ldap()
        connexion.connect()
        if SUPPORT_ETAB:
            prefs = preferences.get_enseignants_prefs()
            etab = prefs.get_default('etab')
            etab_prefix = prefs.get_default('etab_prefix')
        else:
            etab = None
            etab_prefix = ''
        if self.import_type != 'maj':
            writer.purge_equipes(connexion=connexion, etab=etab)
        writer.verify_classe(store=self.store, connexion=connexion,
		                     etab_prefix=etab_prefix)
        writer.write_matiere(store=self.store, connexion=connexion,
                             etab=etab, etab_prefix=etab_prefix)
        writer.verify_option(store=self.store, connexion=connexion,
                                 etab_prefix=etab_prefix)
        writer.write_enseignant(store=self.store, connexion=connexion,
                                etab=etab)
        if self.data_type in ['sconet', 'aaf']:
            writer.write_service(store=self.store, connexion=connexion,
                                 etab=etab, etab_prefix=etab_prefix)
            writer.write_administratif(store=self.store, connexion=connexion,
                                       etab=etab)
        writer.write_samba(connexion)
        connexion.close()
        log.debuglog("Démarrage de LSC...", title=True)
        nscd_start()
        log.del_lock()

#______________________________________________________________________________
# writer administratif

    def do_write_administratif(self):
        """
            écriture des administratifs
        """
        log.add_lock()
        log.debuglog("Arrêt de LSC...", title=True)
        nscd_stop()
        connexion = Ldap()
        connexion.connect()
        # FIXME
        #if self.import_type != 'maj':
        #    writer.purge_equipes(connexion=connexion)
        #writer.write_service(store=self.store, connexion=connexion)
        writer.write_administratif(store=self.store, connexion=connexion)
        writer.write_samba(connexion)
        connexion.close()
        log.debuglog("Démarrage de LSC...", title=True)
        nscd_start()
        log.del_lock()

#______________________________________________________________________________
# writer invité

    def do_write_invite(self):
        """
            écriture des autres
        """
        # arrêt de LSC inutile pour les comptes invités
        log.add_lock()
        connexion = Ldap()
        connexion.connect()
        writer.write_invite(store=self.store, connexion=connexion)
        connexion.close()
        log.del_lock()

#______________________________________________________________________________
# divers

    def _filechooser(self, question):
        """
            filechooser zenity
        """
        print(question)
        old_dir = getcwd()
        if self.last_path != '' and old_dir != self.last_path:
            chdir(self.last_path)
        if 'SSH_CLIENT' in environ:
            if 'DISPLAY' in environ:
                # from "ssh -X" :)
                cmd = ['zenity', '--file-selection',
                       '--title=\"%s\"' % question]
                fichier = system_out(cmd)[1].rstrip()
            else:
                # from ssh without -X :(
                fichier = input(question)
        else:
            # mode console !
            input("appuyez sur une touche pour continuer...")
            cmd = '/usr/share/eole/backend/zenity_console.py'
            fichier = system_out(cmd)[1].rstrip()
        if getcwd() != old_dir:
            chdir(old_dir)
        if fichier == "":
            log.infolog("Importation annulée", title=True)
            sys.exit(1)
        elif isfile(fichier):
            self.last_path = dirname(fichier)
            return fichier
        else:
            print("Fichier \"%s\" introuvable !" % fichier)
            return self._filechooser(question)


#______________________________________________________________________________
# main entry point

def run():
    """
        Traite les options passees en ligne de commande, et lance le
        script avec les bons parametres
    """
    system_code('clear')
    try:
        log.test_lock()
    except Exception as message:
        print("\n%s\n" % message)
        sys.exit(1)
    log.start_importation('console')
    try:
        console = Console()
        console.run()
    except Exception as exc:
        import traceback
        traceback.print_exc()
        log.log.exception("Erreur générale dans l'importation en mode console")
        sys.exit(1)
    log.end_importation('console')

if __name__ == '__main__':
    run()

