diff --git a/gestion/gest_crans_lc.py b/gestion/gest_crans_lc.py new file mode 100755 index 00000000..0bf6bb84 --- /dev/null +++ b/gestion/gest_crans_lc.py @@ -0,0 +1,305 @@ +#!/bin/bash /usr/scripts/python.sh +# -*- coding: utf-8 -*- + +u""" +Interface utilisateur du système de gestion des machines +et adhérents du crans + +Copyright (C) Valentin Samir +Licence : GPLv3 + +""" +import os +import sys +import signal +from pythondialog import Dialog + +from gestion.affich_tools import get_screen_size, coul + +import lc_ldap.shortcuts +import lc_ldap.printing as printing + +def handle_exit_code(d, code): + if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + if code == d.DIALOG_CANCEL: + msg = "Vous avez choisi Annuler dans la dernière fenêtre de dialogue.\n\n" \ + "Voulez vous quitter le programme ?" + os.system('clear') + sys.exit(0) + else: + msg = "Vous avez appuyer sur ESC dans la dernière fenêtre de dialogue.\n\n" \ + "Voulez vous quitter le programme ?" + if d.yesno(msg, width=60) == d.DIALOG_OK: + os.system('clear') + sys.exit(0) + return False + else: + return True # code est d.DIALOG_OK + + + +def search(dialog, objectClassS, title, values={}): + """ + Rechercher des adhérents ou des machines dans la base ldap + retourne le tuple (code de retour dialog, valeurs entrée par l'utilisateur, liste d'objets trouvés) + La fonction est découpé en trois partie : + * affichage dialog et récupération du résultat + * construction de filtres de recherche ldap et recherches ldap + * filtre sur les résultats des recherches ldap + """ + select_dict = { + #label attribut ldap search for substring param dialog: line col valeur input-line icol len max-chars + 'Nom' : {'ldap':'nom', 'sub':True, 'params' : [ 1, 1, values.get('nom', ""), 1, 13, 20, 20]}, + 'Prenom' : {'ldap':'prenom', 'sub':True, 'params' : [ 2, 1, values.get('prenom', ""), 2, 13, 20, 20]}, + 'Téléphone' : {'ldap':'tel', 'sub':True, 'params' : [ 3, 1, values.get('tel', ""), 3, 13, 10, 00]}, + 'Chambre' : {'ldap':'chbre','sub':True, 'params' : [ 4, 1, values.get('chbre',""), 4, 13, 05, 00]}, + 'aid' : {'ldap' : 'aid', 'sub':False, 'params' : [ 5, 1, values.get('aid',""), 5, 13, 05, 05]}, + 'mail' : {'ldap' : 'mail', 'sub':True, 'params' : [ 6, 1, values.get('mail',""), 6, 13, 20, 00]}, + # seconde colone + 'Machine' : {'ldap' : '*', 'sub':True, 'params' : [1, 35, "", 1, 43, 0, 0]}, + 'Host' : {'ldap' : 'host', 'sub':True, 'params' : [2, 37, values.get('host',""), 2, 43, 17, 17]}, + 'Mac' : {'ldap' : 'macAddress', 'sub':False, 'params' : [3, 37, values.get('macAddress',""), 3, 43, 17, 17]}, + 'IP' : {'ldap' : 'ipHostNumber', 'sub':False,'params' : [4, 37, values.get('ipHostNumber',""), 4, 43, 15, 15]}, + 'mid' : {'ldap' : 'mid', 'sub':False, 'params' : [5, 37, values.get('mid',""), 5, 43, 5, 5]}, + } + # On a besoin de l'ordre pour récupérer les valeurs ensuite + select_adherent = ['Nom', 'Prenom', 'Téléphone', 'Chambre', 'aid', 'mail'] + select_machine = ['Host', 'Mac', 'IP', 'mid'] + def box(): + # On met les argument à dialog à la main ici, sinon, c'est difficile de choisir comment mettre une seconde colone + cmd = ["--form", "Entrez vos paramètres de recherche", '0', '0', '0'] + for key in select_adherent: + cmd.extend(['%s :' % key] + [str(e) for e in select_dict[key]['params']]) + cmd.extend(['Machine :'] + [str(e) for e in select_dict['Machine']['params']]) + for key in select_machine: + cmd.extend(['%s :' % key] + [str(e) for e in select_dict[key]['params']]) + cmd.extend(["Les champs vides sont ignorés.", '7', '1', "", '0', '0', '0', '0']) + # On utilise quand même la fonction de la bibliothèques pour passer les arguments + (code, output) = dialog._perform(*(cmd,), title=title, backtitle="Entrez vos paramètres de recherche") + if output: + return (code, output.split('\n')[:-1]) + else: # empty selection + return (code, []) + + (code, dialog_values) = box() + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code, values, [] + else: + # Transformation de la liste des valeures entrée en dictionnnaire + dialog_values = dict(zip(select_adherent + select_machine, dialog_values)) + ldap_values = dict([(select_dict[key]['ldap'], value) for key, value in dialog_values.items()]) + + # Construction des filtres ldap pour les adhérents et les machines + filter_adherent = [] + filter_machine = [] + for (key, value) in dialog_values.items(): + if value: + if key in select_adherent: + filter_adherent.append((u"(%s=*%s*)" if select_dict[key]['sub'] else u"(%s=%s)") % (select_dict[key]['ldap'], unicode(value, 'utf-8'))) + elif key in select_machine: + filter_machine.append((u"(%s=*%s*)" if select_dict[key]['sub'] else u"(%s=%s)") % (select_dict[key]['ldap'], unicode(value, 'utf-8'))) + if filter_adherent: + filter_adherent=u"(&%s)" % "".join(filter_adherent) + if filter_machine: + filter_machine=u"(&%s)" % "".join(filter_machine) + + # Récupération des adhérents et des machines + adherents=conn.search(filter_adherent) if filter_adherent else [] + machines=conn.search(filter_machine) if filter_machine else [] + + # Filtrage des machines en fonction des adhérents + if filter_adherent: + if filter_machine: + # Si on filtre sur des adhérent et des machines, on calcule l'intersection + adherents_dn = set([a.dn for a in adherents]) + machines_f = [m for m in machines if m.parent_dn in adherents_dn] + else: + # Sinon on filtre seulement sur les adhérents, récupère les machines des adhérents trouvés + machines_f = [m for a in adherents for m in a.machines()] + else: + # Sinon si on filtre seulement sur des machines + machines_f = machines + + # Filtrage des adhérents en fonction des machines + if filter_machine: + if filter_adherent: + # Si on filtre sur des adhérents et des machines, on calcule l'intersection + machines_dn = set([m.parent_dn for m in machines]) + adherents_f = [a for a in adherents if a.dn in machines_dn] + else: + # Sinon on récupères les proprios des machines trouvées + adherents_f = [m.proprio() for m in machines] + else: + # Sinon si on filtre seulement sur des adhérents + adherents_f = adherents + + # On filtre sur les objectClassS + return code, ldap_values, [ o for objectClass in objectClassS for o in machines_f+adherents_f if objectClass in o['objectClass'] ] + +def select_one(dialog, items, default_item=None): + """Fait selectionner un item parmis une liste d'items à l'utisateur""" + ### TODO afficher correctement les objets dans items + choices=[] + count = 0 + default_tag = items.index(default_item) if default_item in items else default_item + for i in items: + choices.append((str(count), str(i), 1 if default_tag == count else 0)) + count+=1 + tag='' + while not tag: + (line, col) = get_screen_size() + (code, tag) = dialog.radiolist( + "Choisir", + choices=choices, + default_item=str(default_tag), + width=col-4, + height=line-3, + list_height=line-3, + scrollbar=True) + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code, None + if not tag: + dialog.msgbox("Merci de choisir l'un des item de la liste ou d'annuler", title="Sélection", width=0, height=0) + return code, items[int(tag)] + +def select_confirm(dialog, item, title): + """Affiche un item et demande si c'est bien celui là que l'on veux (supprimer, éditer, créer,...)""" + return dialog.yesno(printing.sprint(item), no_collapse=True, colors=True, title=title, width=0, height=0, backtitle="Appuyez sur MAJ pour selectionner du texte") == dialog.DIALOG_OK + +def select(dialog, objectClassS, title, values={}): + """Permet de choisir un objet adhérent ou machine dans la base ldap""" + while True: + try: + # On fait effectuer une recherche à l'utilisateur + code, values, items = search(dialog, objectClassS, title, values) + # Si il a appuyé sur annuler ou sur escape, on annule + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code, None + # S'il n'y a pas de résultas, on recommence + elif not items: + dialog.msgbox("Aucun Résultat", title="Recherche", width=0, height=0) + # S'il y a plusieurs résultats + elif len(items)>1: + item = None + while True: + # On en fait choisir un + code, item = select_one(dialog, items, item) + # Si il à appuyer sur annuler ou sur escape, on recommence la recherche + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + break + # Sinon, on fait confirmer son choix à l'utilisateur + elif select_confirm(dialog, item, title): + return code, item + # S'il y a exactement 1 résultat à la recherche, on fait confirmer son choix à l'utilisateur + elif len(items) == 1 and select_confirm(dialog, items[0], title): + return code, items[0] + except Exception as e: + dialog.msgbox("%r" % e, title="Erreur rencontrée", width=0, height=0) + +def modif_adherent(dialog): + code, adherent = select(dialog, ["adherent"], "Recherche d'un adhérent") + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code + dialog.msgbox("todo", width=0, height=0) + +def create_adherent(dialog): + dialog.msgbox("todo", width=0, height=0) + +def delete_adherent(dialog): + code, adherent = select(dialog, ["adherent"], "Recherche d'un adhérent") + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code + dialog.msgbox("todo", width=0, height=0) + +def modif_machine(dialog): + code, machine = select(dialog, ["machineFixe", "machineWifi", "machineCrans", "borneWifi"], "Recherche d'une machine") + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code + dialog.msgbox("todo", width=0, height=0) + +def create_machine_adherent(dialog): + dialog.msgbox("todo", width=0, height=0) + +def delete_machine(dialog): + code, machine = select(dialog, ["machineFixe", "machineWifi", "machineCrans", "borneWifi"], "Recherche d'une machine") + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return code + dialog.msgbox("todo", width=0, height=0) + + +def create_club(dialog): + dialog.msgbox("todo", width=0, height=0) + + +def delete_club(dialog): + dialog.msgbox("todo", width=0, height=0) + +def modif_club(dialog): + dialog.msgbox("todo", width=0, height=0) + +def create_machine_club(dialog): + dialog.msgbox("todo", width=0, height=0) + +def create_machine_crans(dialog): + dialog.msgbox("todo", width=0, height=0) + +def create_borne(dialog): + dialog.msgbox("todo", width=0, height=0) + +def menu_principal(dialog): + menu = { + 'aA' : {'text':"Inscrire un nouvel adhérent", 'callback': create_adherent}, + 'mA' : {'text':"Modifier l'inscription d'un adhérent", 'callback': modif_adherent, 'help':"Changer la chambre, la remarque, la section, la carte d'étudiant ou précâbler."}, + 'aMA': {'text':"Ajouter une machine à un adhérent", 'callback': create_machine_adherent}, + 'dA' : {'text':"Détruire un adhérent", 'callback': delete_adherent, 'help':"Suppression de l'adhérent ainsi que de ses machines"}, + 'mM' : {'text':"Modifier une machine existante", 'callback': modif_machine, 'help':"Changer le nom ou la MAC d'une machine."}, + 'dM' : {'text':"Détruire une machine", 'callback': delete_machine}, + 'aC' : {'text':"Inscrire un nouveau club", 'callback': create_club}, + 'mC' : {'text':"Modifier un club", 'callback': modif_club}, + 'aMC': {'text':"Ajouter une machine à un club", 'callback': create_machine_club}, + 'dC' : {'text':"Détruire un club", 'callback': delete_club}, + 'aKM': {'text':"Ajouter une machine à l'association", 'callback': create_machine_crans}, + 'aKB': {'text':"Ajouter une borne wifi", 'callback': create_borne}, + '' : {'text':"---------------------------------------",'callback': lambda x: x}, + } + ### Les clef qui n'existe pas sont toute renvoyé sur la clef '' + medu_order = ["aA", "mA", "aMA", "dA", "", "mM", "dM", " ", "aC", "mC", "aMC", "dC", " ", "aKM", "aKB"] + def box(default_item=None): + return dialog.menu( + "Que souhaitez vous faire ?", + width=55, + height=0, + menu_height=15, + item_help=1, + default_item=str(default_item), + title="Menu principal", + scrollbar=True, + cancel_label="Quitter", + backtitle=u"Vous êtes connecté en tant que %s" % conn.current_login, + choices=[(key, menu.get(key, menu[''])['text'], menu.get(key, menu['']).get('help', "")) for key in medu_order]) + + tag=None + while True: + (code, tag) = box(tag) + if handle_exit_code(dialog, code): + menu.get(tag, menu[''])['callback'](dialog) + + +if __name__ == '__main__': + signal.signal(signal.SIGINT, signal.SIG_IGN) + # On initialise le moteur de rendu en spécifiant qu'on va faire du dialog + printing.template(dialog=True) + + # On ouvre une connexion lc_ldap + conn = lc_ldap.shortcuts.lc_ldap_admin() + # On vérifie que l'utilisateur système existe dans ldap (pour la gestion des droits) + luser=conn.search(u"uid=%s" % conn.current_login) + if not luser: + sys.stderr.write("L'utilisateur %s n'existe pas dans la base de donnée" % conn.current_login) + sys.exit(1) + conn.droits = [str(d) for d in luser[0]['droits']] + conn.dn = luser[0].dn + + dialog = Dialog() + menu_principal(dialog) + os.system('clear')