#!/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')