#!/bin/bash /usr/scripts/python.sh # -*- coding: utf-8 -*- """ Interface utilisateur du système de gestion des machines et adhérents du crans Copyright (C) Valentin Samir Licence : GPLv3 """ ### sur le dialog (1.1) de zamok, il manque les fonctionnalité suivante présente dans la 1.2 ### --default-button pour choisir le bouton sélectionner par defaut ### --not-tags pour masquer les tags quand il ne servent à rien pour l'utilisateur (mais sont utilisé comme index par le programme) import argparse import os import sys import subprocess if '/usr/scripts' not in sys.path: sys.path.append('/usr/scripts') import lc_ldap.attributs as attributs import lc_ldap.objets as objets from dialog import adherent, club, machine from dialog.CPS import TailCall, tailcaller from dialog.lc import main def handle_dialog_exit_code(dialog, code): """Gère les codes de retour du menu principal. Paramètres: - ``dialog``: (pythondialog.Dialog) instance courante de l'objet Dialog - ``code`` : (int) code de retour de la fenêtre Dialog Retourne: - ``True`` quand dialog a quitté correctement (l'utilisateur a bien saisi une valeur) - ``False`` quand l'utilisateur annule la sortie du programme (aucune valeur n'a été saisie) Lève les exceptions: - :py:exc:`SystemExit` quand la sortie du programme est confirmée """ def exit_from_program(): """ Quitte le programme après avoir vidé la console Lève l'exception :py:exc:`SystemExit` """ subprocess.call('clear') sys.exit(0) if code == dialog.DIALOG_CANCEL: exit_from_program() elif code == dialog.DIALOG_ESC: msg = ( "Vous avez appuyé sur ESC ou CTRL+C dans la dernière fenêtre" " de dialogue.\n\nVoulez vous quitter le programme ?" ) if dialog.yesno(msg, width=60) == dialog.DIALOG_OK: exit_from_program() return False else: return True class GestCrans(adherent.Dialog, club.Dialog, machine.Dialog): @tailcaller def menu_principal(self, tag=None, machine=None, proprio=None): """Menu principal de l'application. Paramètres: - ``tag``: (str) clé du menu sélectionnée par défaut - ``machine``: (lc_ldap.Machine) machine sélectionnée - ``proprio``: (lc_ldap.Adherent ou lc_ldap.Club) proprio sélectionné Le menu principal est affiché lorsqu'on démarre l'application, et lorsqu'on revient au menu principal suite à une action sur un objet (arguments machine ou proprio). Le menu est créé en deux étapes : - Actions spécifiques à l'objet (`machine`, `proprio`) sélectionné - Actions génériques """ # Droits nécessaires pour effectuer les différentes actions. # Par défaut, on vérifie les droits ''. # La clé correspond à la clé dans le dictionnaire menu. menu_droits = { '': [attributs.cableur, attributs.nounou], 'aKM': [attributs.nounou], } # On initialise le menu avec des actions "génériques" menu = { 'aA': { 'text': "Inscrire un nouvel adhérent", 'callback': self.create_adherent, }, 'mA': { 'text': "Modifier l'inscription d'un adhérent", 'callback': self.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': self.create_machine_adherent, }, # 'dA': { # 'text': "Détruire un adhérent", # 'callback': self.delete_adherent, # 'help': "Suppression de l'adhérent ainsi que de ses machines", # }, 'mM': { 'text': "Modifier une machine existante", 'callback': self.modif_machine, 'help': "Changer le nom ou la MAC d'une machine.", }, # 'dM': { # 'text': "Détruire une machine", # 'callback': self.delete_machine, # }, 'aC': { 'text': "Inscrire un nouveau club", 'callback': self.create_club, }, 'mC': { 'text': "Modifier un club", 'callback': self.modif_club, }, 'aMC': { 'text': "Ajouter une machine à un club", 'callback': self.create_machine_club, }, # 'dC': { # 'text': "Détruire un club", # 'callback': self.delete_club, # }, 'aKM': { 'text': "Ajouter une machine à l'association", 'callback': self.create_machine_crans, }, '': { 'text': "---------------------------------------", 'callback': None, }, } # On liste les actions pour définir leur ordre d'affichage menu_order = [ # Actions adhérent "aA", "mA", "aMA", "", # Actions machine "mM", "", # Actions club "aC", "mC", "aMC", "", # Actions Cr@ns "aKM", ] # Ajout des actions spécifiques en tête du menu # On récupère le propriétaire de la machine pour ajouter les # actions associées if machine and not proprio: proprio = machine.proprio() # Sauf si le propriétaire est le Cr@ns if isinstance(proprio, objets.AssociationCrans): proprio = None # On a sélectionné un objet. On ajoute les actions spécifiques à # l'objet avant les actions génériques. if machine or proprio: menu_order = [''] + menu_order # Actions spécifiques à une machine if machine: menu_machine = { 'mMc': { 'text': "Modifier la machine %s" % machine['host'][0], 'callback': TailCall(self.modif_machine, machine=machine), 'help': "Changer le nom ou la MAC d'une machine." }, } menu_machine_order = ['mMc'] menu.update(menu_machine) menu_order = menu_machine_order + menu_order # Actions spécifiques à un proprio (adhérent ou club) if proprio: if 'adherent' in proprio['objectClass']: menu_proprio = { 'mAc': { 'text': ("Modifier l'inscription de %s" % proprio.get("cn", proprio["nom"])[0]), 'callback': TailCall(self.modif_adherent, adherent=proprio) }, 'aMc': { 'text': ("Ajouter une machine à %s" % proprio.get("cn", proprio["nom"])[0]), 'callback': TailCall(self.create_machine_adherent, adherent=proprio) }, } menu_proprio_order = ['mAc', 'aMc'] elif 'club' in proprio['objectClass']: menu_proprio = { 'mCc': { 'text': ("Modifier l'inscription de %s" % proprio.get("cn", proprio["nom"])[0]), 'callback': TailCall(self.modif_club, club=proprio) }, 'aMc': { 'text': ("Ajouter une machine à %s" % proprio.get("cn", proprio["nom"])[0]), 'callback': TailCall(self.create_machine_club, club=proprio) }, } menu_proprio_order = ['mCc', 'aMc'] else: raise EnvironmentError( "Le proprio sélectionné doit être un adhérent ou un club." ) menu.update(menu_proprio) menu_order = menu_proprio_order + menu_order def dialog_menu_principal(default_item=None): """Renvoie la boîte de dialogue telle que définie avec le menu précédent.""" # On construit une liste de triplets (clé, titre, texte # d'aide), passée à dialog, à partir de la liste des clés du # menu et des fonctions associées choices = [] # On reprend les clés dans l'ordre défini par la liste menu_order for key in menu_order: # Vérification des droits if self.has_right(menu_droits.get(key, menu_droits[''])): choices.append(( key, menu[key]['text'], menu[key].get('help', ""), )) # On enlève les lignes de séparation qui seraient au début # ou à la fin de la boîte de dialogue while choices[0][0] == '': choices = choices[1:] while choices[-1][0] == '': choices = choices[:-1] return self.dialog.menu( "Que souhaitez vous faire ?", width=0, height=0, menu_height=0, item_help=1, default_item=str(default_item), title="Menu principal", scrollbar=True, timeout=self.timeout, cancel_label="Quitter", backtitle=self._connected_as(), choices=choices, ) # Appel de dialog pour afficher le menu construit précédemment. # Oui, il faut passer la fonction qui gère une annulation d'abord... (code, tag) = self.handle_dialog( TailCall( handle_dialog_exit_code, self.dialog, self.dialog.DIALOG_ESC, ), dialog_menu_principal, tag, ) # Appel qui permet de revenir au menu principal rappel_menu_principal = TailCall( self.menu_principal, tag=tag, proprio=proprio, machine=machine, ) # On récupère le callback de la ligne sélectionnée par # l'utilisateur fonction_selectionnee = menu.get(tag, menu[''])['callback'] # On vérifie si l'utilisateur a appelé une ligne non-séparateur if ( handle_dialog_exit_code(self.dialog, code) and fonction_selectionnee ): # On appelle la fonction sélectionnée puis on revient au # menu principal return TailCall(fonction_selectionnee, cont=rappel_menu_principal) else: # La ligne est un séparateur ou l'utilisateur a annulé, on # revient au menu principal immédiatement return rappel_menu_principal if __name__ == '__main__': parser = argparse.ArgumentParser( description=( 'Interface utilisateur du système de gestion ' 'des machines et adhérents du Cr@ns' ) ) parser.add_argument( '--test', help='Utiliser la base de test', dest='ldap_test', default=False, action='store_true', ) parser.add_argument( '--debug', help='Afficher des info de débug comme les tracebacks', dest='debug_enable', default=False, action='store_true', ) parser.add_argument( 'login', help="Se connecter en tant qu'un autre utilisateur", type=str, default=None, nargs='?', ) args = parser.parse_args() main(GestCrans( ldap_test=args.ldap_test, debug_enable=args.debug_enable, custom_user=args.login, )) os.system('clear')