Compare commits
4 commits
master
...
cleanup-ge
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ef07bc5a28 | ||
![]() |
890a1067fc | ||
![]() |
e274be0864 | ||
![]() |
6ff3e180a2 |
3 changed files with 401 additions and 142 deletions
|
@ -1,10 +1,23 @@
|
||||||
#!/bin/bash /usr/scripts/python.sh
|
#!/bin/bash /usr/scripts/python.sh
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) Valentin Samir
|
||||||
|
# Licence : GPLv3
|
||||||
u"""
|
u"""
|
||||||
Copyright (C) Valentin Samir
|
Module implémentant la "Continuation Passing Style".
|
||||||
Licence : GPLv3
|
|
||||||
|
|
||||||
|
Le concept est d'exécuter des fonctions qui sont des TailCallers,
|
||||||
|
qui elles-mêmes seront amenées à appeler d'autres fonctions,
|
||||||
|
qui sont des TailCalls, ou qui lèvent des exceptions de type
|
||||||
|
Continue(TailCall).
|
||||||
|
|
||||||
|
Ces TailCalls pourraient être amenés à exécuter d'autres fonctions,
|
||||||
|
dans ce cas, pour faire en sorte que leur empreinte mémoire soit
|
||||||
|
faible, auquel cas, soit ces fonctions sont elles-mêmes des
|
||||||
|
TailCalls, soit ce sont des TailCallers.
|
||||||
|
|
||||||
|
L'intérêt de ce concept est de favoriser une stack à empreinte
|
||||||
|
basse.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -16,27 +29,33 @@ import traceback
|
||||||
|
|
||||||
if '/usr/scripts' not in sys.path:
|
if '/usr/scripts' not in sys.path:
|
||||||
sys.path.append('/usr/scripts')
|
sys.path.append('/usr/scripts')
|
||||||
|
|
||||||
from pythondialog import Dialog as PythonDialog
|
from pythondialog import Dialog as PythonDialog
|
||||||
from pythondialog import DialogTerminatedBySignal, PythonDialogErrorBeforeExecInChildProcess
|
from pythondialog import DialogTerminatedBySignal, PythonDialogErrorBeforeExecInChildProcess
|
||||||
from pythondialog import error as DialogError
|
from pythondialog import error as DialogError
|
||||||
from gestion.affich_tools import get_screen_size, coul
|
from gestion import affichage
|
||||||
|
|
||||||
debug_enable = False
|
from cranslib import clogger
|
||||||
debugf = None
|
|
||||||
|
# Modifier level à debug pour débugguer.
|
||||||
|
LOGGER = clogger.CLogger('gest_crans_lc', service="CPS", level='info')
|
||||||
|
ENCODING = affichage.guess_preferred_encoding()
|
||||||
|
|
||||||
def mydebug(txt):
|
def mydebug(txt):
|
||||||
"""Petit fonction pour écrire des messages de débug dans /tmp/gest_crans_lc.log"""
|
"""Petite fonction pour logguer du debug avec CLogger.
|
||||||
global debugf, debug_enable
|
LOGGER.debug n'écrit que si level est à debug"""
|
||||||
if debug_enable:
|
if isinstance(txt, list):
|
||||||
if debugf is None:
|
txt = "\n".join(txt)
|
||||||
debugf = open('/tmp/gest_crans_lc.log', 'w')
|
if isinstance(txt, unicode):
|
||||||
if isinstance(txt, list):
|
txt = txt.encode(ENCODING)
|
||||||
for v in txt:
|
LOGGER.debug(txt)
|
||||||
mydebug(' ' + str(v))
|
|
||||||
else:
|
class Continue(Exception):
|
||||||
debugf.write(str(txt)+'\n')
|
"""Exception pour envoyer des TailCall en les raisant
|
||||||
debugf.flush()
|
l'argument tailCall est soit une fonction décorée, soit
|
||||||
os.fsync(debugf)
|
la fonction retournante elle-même."""
|
||||||
|
def __init__(self, tailCall):
|
||||||
|
self.tailCall = tailCall
|
||||||
|
|
||||||
# Implémentation "à la main" de la tail récursion en python
|
# Implémentation "à la main" de la tail récursion en python
|
||||||
# voir http://kylem.net/programming/tailcall.html
|
# voir http://kylem.net/programming/tailcall.html
|
||||||
|
@ -45,63 +64,91 @@ def mydebug(txt):
|
||||||
# code en CPS (Continuation Passing Style)
|
# code en CPS (Continuation Passing Style)
|
||||||
class TailCaller(object) :
|
class TailCaller(object) :
|
||||||
"""
|
"""
|
||||||
Classe permetant, en décorant des fonctions avec, d'avoir de la tail récursion
|
Un TailCaller est le doux nom d'un objet (une fonction décorée) qui crée et
|
||||||
faite "à la main" en python (voir http://kylem.net/programming/tailcall.html)
|
joue avec des TailCall. Les TailCalls sont des jouets qui servent à réduire
|
||||||
|
l'empreinte dans la stack d'appels récursifs.
|
||||||
|
|
||||||
Parameters
|
Lorsqu'on __call__ un TailCaller, celui-ci exécute la fonction qu'il décore.
|
||||||
----------
|
Si le retour de celle-ci est un TailCall, on exécute le call de celui-ci,
|
||||||
f : function
|
et ainsi de suite, jusqu'à avoir un retour qui ne soit pas un TailCall.
|
||||||
Fonction décoré
|
|
||||||
|
Un call ou la fonction du TailCaller peuvent retourner des erreurs de type
|
||||||
|
continue, qui généralement contiennent un TailCall, mais peuvent aussi
|
||||||
|
contenir une sortie.
|
||||||
"""
|
"""
|
||||||
other_callers = {}
|
other_callers = {}
|
||||||
def __init__(self, f) :
|
|
||||||
|
def __init__(self, f):
|
||||||
|
"""On prend f et son nom et on les stocke dans le TailCaller, wouhou,
|
||||||
|
c'est trop fou."""
|
||||||
self.f = f
|
self.f = f
|
||||||
self.func_name = f.func_name
|
self.func_name = f.func_name
|
||||||
TailCaller.other_callers[id(self)]=f.func_name
|
TailCaller.other_callers[id(self)]=f.func_name
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
"""Supprime le TailCaller de la liste"""
|
||||||
del(TailCaller.other_callers[id(self)])
|
del(TailCaller.other_callers[id(self)])
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs) :
|
def __call__(self, *args, **kwargs):
|
||||||
|
"""Quand on appelle le TailCaller pour de vrai,
|
||||||
|
on vérifie la position actuelle du bordel dans la stack,
|
||||||
|
et on exécute la fonction référencée, et les TailCalls qu'on
|
||||||
|
peut croiser sur la route, pour peu que leur position en
|
||||||
|
stack soit postérieure à celle du TailCaller actuel."""
|
||||||
mydebug("***%s calling" % self.func_name)
|
mydebug("***%s calling" % self.func_name)
|
||||||
stacklvl = len(inspect.stack())
|
stacklvl = len(inspect.stack())
|
||||||
|
|
||||||
|
# On applique f une première fois aux arguments
|
||||||
|
# qui vont bien.
|
||||||
|
# Cela peut retourner un TailCall, un résultat, ou
|
||||||
|
# lever une erreur de type Continue. Souvent les fonctions
|
||||||
|
# raisent des Continue contenant des TailCall, dans le but
|
||||||
|
# de ne pas augmenter le contenu de la stack.
|
||||||
try:
|
try:
|
||||||
ret = self.f(*args, **kwargs)
|
ret = self.f(*args, **kwargs)
|
||||||
except Continue as c:
|
except Continue as c:
|
||||||
ret = c.tailCall
|
# Le TailCall est là-dedans.
|
||||||
|
ret = c.tailCall
|
||||||
|
|
||||||
|
# Si f a terminé, ret n'est pas un TailCall mais sa valeur
|
||||||
|
# de retour. Sinon, on entre dans la boucle.
|
||||||
while isinstance(ret, TailCall):
|
while isinstance(ret, TailCall):
|
||||||
# Si la continuation a été créer par un TailCaller précédent, on propage
|
# Ici, on regarde l'endroit dans la stack de l'objet courant
|
||||||
# Cela permet d'assurer qu'un TailCaller ne s'occupe que des TailCall
|
# vis-à-vis de la position dans la stack de ret. (plus le stack
|
||||||
# Crée après lui.
|
# lvl est élevé, plus on a été appelé récemment)
|
||||||
if stacklvl>=ret.stacklvl:
|
# On constate donc ici qu'on ne s'intéresse qu'aux TailCall qui
|
||||||
|
# ont été créés après l'appel du TailCaller actuel.
|
||||||
|
if stacklvl >= ret.stacklvl:
|
||||||
mydebug("***%s terminate" % self.func_name)
|
mydebug("***%s terminate" % self.func_name)
|
||||||
|
# Ici, on peut donc raise un Continue, qui fait sortir
|
||||||
|
# de l'exécution du TailCaller courant.
|
||||||
raise Continue(ret)
|
raise Continue(ret)
|
||||||
mydebug("***%s doing %s" % (self.func_name, ret))
|
mydebug("***%s doing %s" % (self.func_name, ret))
|
||||||
|
|
||||||
|
# L'appel à un TailCall se fait via la méthode handle.
|
||||||
|
# Si ça retourne une continuation, on récupère le tailCall
|
||||||
|
# dedans dans ret, et on boucle. Sinon, soit le TailCall
|
||||||
|
# retourne un TailCall, soit il retourne un résultat.
|
||||||
try:
|
try:
|
||||||
ret = ret.handle()
|
ret = ret.handle()
|
||||||
except Continue as c:
|
except Continue as c:
|
||||||
ret = c.tailCall
|
ret = c.tailCall
|
||||||
|
|
||||||
mydebug("***%s returning %s" % (self.func_name, ret))
|
mydebug("***%s returning %s" % (self.func_name, ret))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def tailcaller(f):
|
def tailcaller(f):
|
||||||
"""
|
"""Décorateur permettant aux fonctions d'une classe d'être
|
||||||
Décorateur retardant la décoration par TailCaller d'une fonction
|
décorées comme TailCaller à l'instanciation de ladite classe.
|
||||||
À utiliser sur les fonctions faisant de la tail récursion
|
|
||||||
et devant retourner une valeur. On l'utilise de toute
|
Cela permet que seules les instances aient des méthodes qui
|
||||||
façon sur la fonction d'entrée dans l'application
|
soient des TailCallers. La conversion se faisant au premier
|
||||||
Une fonction de devrait pas instancier de TailCall sans
|
appel de la fonction dans l'instance.
|
||||||
être décoré par ce décorteur de façon générale
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f.tailCaller = True
|
f.tailCaller = True
|
||||||
return f
|
return f
|
||||||
|
|
||||||
class Continue(Exception):
|
|
||||||
"""Exception pour envoyer des TailCall en les raisant"""
|
|
||||||
def __init__(self, tailCall):
|
|
||||||
self.tailCall = tailCall
|
|
||||||
|
|
||||||
class TailCall(object) :
|
class TailCall(object) :
|
||||||
"""
|
"""
|
||||||
Appel tail récursif à une fonction et ses argument
|
Appel tail récursif à une fonction et ses argument
|
||||||
|
@ -109,12 +156,17 @@ class TailCall(object) :
|
||||||
l'appel à une fonction (avec ses listes d'arguements)
|
l'appel à une fonction (avec ses listes d'arguements)
|
||||||
à un moment futur. La fonction sera appelée dans les
|
à un moment futur. La fonction sera appelée dans les
|
||||||
cas suivant :
|
cas suivant :
|
||||||
* Le TailCall est retourné par une fonction qui est un TailCaller
|
* Le TailCall est retourné dans l'appel de la fonction d'un
|
||||||
|
TailCaller.
|
||||||
* L'exception Continue(TailCall(...)) est levée et traverse
|
* L'exception Continue(TailCall(...)) est levée et traverse
|
||||||
une fonction qui est un TailCaller
|
une fonction qui est un TailCaller
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, call, *args, **kwargs) :
|
def __init__(self, call, *args, **kwargs) :
|
||||||
|
"""Création du TailCall, on note la position dans la pile
|
||||||
|
Si la fonction passée à TailCall est un TailCall, on override
|
||||||
|
le TailCall qu'on est en train d'instancier.
|
||||||
|
"""
|
||||||
self.stacklvl = len(inspect.stack())
|
self.stacklvl = len(inspect.stack())
|
||||||
if isinstance(call, TailCall):
|
if isinstance(call, TailCall):
|
||||||
call.kwargs.update(**kwargs)
|
call.kwargs.update(**kwargs)
|
||||||
|
@ -129,6 +181,8 @@ class TailCall(object) :
|
||||||
self.check(self.args, self.kwargs)
|
self.check(self.args, self.kwargs)
|
||||||
|
|
||||||
def check(self, args, kwargs):
|
def check(self, args, kwargs):
|
||||||
|
"""On vérifie si le TailCall a le bon nombre d'arguments,
|
||||||
|
et si les keywords arguments sont bons."""
|
||||||
call = self.call
|
call = self.call
|
||||||
if isinstance(call, TailCaller):
|
if isinstance(call, TailCaller):
|
||||||
call = call.f
|
call = call.f
|
||||||
|
@ -136,6 +190,7 @@ class TailCall(object) :
|
||||||
if targs.varargs is not None:
|
if targs.varargs is not None:
|
||||||
if len(args) + len(kwargs) > len(targs.args):
|
if len(args) + len(kwargs) > len(targs.args):
|
||||||
raise TypeError("%s() takes at most %s arguments (%s given)" % (call.func_name, len(targs.args), len(args) + len(kwargs)))
|
raise TypeError("%s() takes at most %s arguments (%s given)" % (call.func_name, len(targs.args), len(args) + len(kwargs)))
|
||||||
|
|
||||||
for key in kwargs:
|
for key in kwargs:
|
||||||
if key not in targs.args:
|
if key not in targs.args:
|
||||||
raise TypeError("%s() got an unexpected keyword argument '%s'" % (call.func_name, key))
|
raise TypeError("%s() got an unexpected keyword argument '%s'" % (call.func_name, key))
|
||||||
|
@ -160,7 +215,11 @@ class TailCall(object) :
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
tmpkwargs={}
|
"""Met à jour le TailCall en ajoutant args et kwargs
|
||||||
|
à la liste des arguments à passer au call lors de son appel
|
||||||
|
via handle.
|
||||||
|
"""
|
||||||
|
tmpkwargs = {}
|
||||||
tmpkwargs.update(self.kwargs)
|
tmpkwargs.update(self.kwargs)
|
||||||
tmpkwargs.update(kwargs)
|
tmpkwargs.update(kwargs)
|
||||||
self.check(self.args + args, tmpkwargs)
|
self.check(self.args + args, tmpkwargs)
|
||||||
|
@ -171,14 +230,12 @@ class TailCall(object) :
|
||||||
def handle(self) :
|
def handle(self) :
|
||||||
"""
|
"""
|
||||||
Exécute la fonction call sur sa liste d'argument.
|
Exécute la fonction call sur sa liste d'argument.
|
||||||
on déréférence les TailCaller le plus possible pour réduire
|
|
||||||
la taille de la stack
|
Si la fonction à exécuter est un TailCaller, on déréférence.
|
||||||
"""
|
"""
|
||||||
caller = None
|
|
||||||
call = self.call
|
call = self.call
|
||||||
while isinstance(call, TailCaller) :
|
while isinstance(call, TailCaller):
|
||||||
caller = call
|
call = call.f
|
||||||
call = self.call.f
|
|
||||||
return call(*self.args, **self.kwargs)
|
return call(*self.args, **self.kwargs)
|
||||||
|
|
||||||
def unicode_of_Error(x):
|
def unicode_of_Error(x):
|
||||||
|
@ -227,7 +284,7 @@ class Dialog(object):
|
||||||
"""
|
"""
|
||||||
Nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan
|
Nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan
|
||||||
"""
|
"""
|
||||||
(lines, cols) = get_screen_size()
|
(lines, cols) = affichage.getTerminalSize()
|
||||||
print "\033[48;5;17m"
|
print "\033[48;5;17m"
|
||||||
print " "*(lines * cols)
|
print " "*(lines * cols)
|
||||||
cols = int(min(cols/2, 65))
|
cols = int(min(cols/2, 65))
|
||||||
|
|
|
@ -723,19 +723,19 @@ class Dialog(proprio.Dialog):
|
||||||
def set_chambre(adherent, chbre):
|
def set_chambre(adherent, chbre):
|
||||||
try:
|
try:
|
||||||
adherent['postalAddress'] = []
|
adherent['postalAddress'] = []
|
||||||
adherent['chbre'] = unicode(output, 'utf-8')
|
adherent['chbre'] = unicode(chbre, 'utf-8')
|
||||||
except UniquenessError:
|
except UniquenessError:
|
||||||
if expulse_squatteur(adherent, chbre):
|
if expulse_squatteur(adherent, chbre):
|
||||||
# La chambre est maintenant normalement libre
|
# La chambre est maintenant normalement libre
|
||||||
adherent['chbre'] = unicode(output, 'utf-8')
|
adherent['chbre'] = unicode(chbre, 'utf-8')
|
||||||
else:
|
else:
|
||||||
raise Continue(self_cont)
|
raise Continue(self_cont)
|
||||||
return adherent
|
return adherent
|
||||||
|
|
||||||
def todo(chbre, adherent, self_cont, success_cont):
|
def todo(chbre, adherent, self_cont, success_cont):
|
||||||
if not output:
|
if not chbre:
|
||||||
raise Continue(self_cont)
|
raise Continue(self_cont)
|
||||||
if output == "????":
|
if chbre == "????":
|
||||||
raise ValueError("Chambre ???? invalide")
|
raise ValueError("Chambre ???? invalide")
|
||||||
if create:
|
if create:
|
||||||
return set_chambre(adherent, chbre)
|
return set_chambre(adherent, chbre)
|
||||||
|
@ -745,7 +745,7 @@ class Dialog(proprio.Dialog):
|
||||||
adherent.validate_changes()
|
adherent.validate_changes()
|
||||||
adherent.history_gen()
|
adherent.history_gen()
|
||||||
adherent.save()
|
adherent.save()
|
||||||
self.display_item(item=adherent, title="Adhérent déménagé dans la chambre %s" % output)
|
self.display_item(item=adherent, title="Adhérent déménagé dans la chambre %s" % chbre)
|
||||||
raise Continue(success_cont(adherent=adherent))
|
raise Continue(success_cont(adherent=adherent))
|
||||||
|
|
||||||
(code, output) = self.handle_dialog(cont, box)
|
(code, output) = self.handle_dialog(cont, box)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash /usr/scripts/python.sh
|
#!/bin/bash /usr/scripts/python.sh
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
u"""
|
"""
|
||||||
Interface utilisateur du système de gestion des machines
|
Interface utilisateur du système de gestion des machines
|
||||||
et adhérents du crans
|
et adhérents du crans
|
||||||
|
|
||||||
|
@ -13,122 +13,268 @@ Licence : GPLv3
|
||||||
### --default-button pour choisir le bouton sélectionner par defaut
|
### --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)
|
### --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 os
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import subprocess
|
||||||
|
|
||||||
if '/usr/scripts' not in sys.path:
|
if '/usr/scripts' not in sys.path:
|
||||||
sys.path.append('/usr/scripts')
|
sys.path.append('/usr/scripts')
|
||||||
|
|
||||||
import lc_ldap.objets as objets
|
|
||||||
import lc_ldap.attributs as attributs
|
import lc_ldap.attributs as attributs
|
||||||
|
import lc_ldap.objets as objets
|
||||||
|
|
||||||
from dialog import adherent, club, machine
|
from dialog import adherent, club, machine
|
||||||
from dialog.CPS import TailCall, tailcaller
|
from dialog.CPS import TailCall, tailcaller
|
||||||
from dialog.lc import main
|
from dialog.lc import main
|
||||||
|
|
||||||
def handle_exit_code(d, code):
|
|
||||||
"""Gère les codes de retour dialog du menu principal"""
|
def handle_dialog_exit_code(dialog, code):
|
||||||
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
|
"""Gère les codes de retour du menu principal.
|
||||||
if code == d.DIALOG_CANCEL:
|
|
||||||
#msg = "Vous avez choisi Annuler dans la dernière fenêtre de dialogue.\n\n" \
|
Paramètres:
|
||||||
# "Voulez vous quitter le programme ?"
|
- ``dialog``: (pythondialog.Dialog) instance courante de l'objet Dialog
|
||||||
os.system('clear')
|
- ``code`` : (int) code de retour de la fenêtre Dialog
|
||||||
sys.exit(0)
|
|
||||||
else:
|
Retourne:
|
||||||
msg = "Vous avez appuyé sur ESC ou CTRL+C dans la dernière fenêtre de dialogue.\n\n" \
|
- ``True`` quand dialog a quitté correctement (l'utilisateur a bien
|
||||||
"Voulez vous quitter le programme ?"
|
saisi une valeur)
|
||||||
if d.yesno(msg, width=60) == d.DIALOG_OK:
|
- ``False`` quand l'utilisateur annule la sortie du programme
|
||||||
os.system('clear')
|
(aucune valeur n'a été saisie)
|
||||||
sys.exit(0)
|
|
||||||
|
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
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class GestCrans(adherent.Dialog, club.Dialog, machine.Dialog):
|
class GestCrans(adherent.Dialog, club.Dialog, machine.Dialog):
|
||||||
@tailcaller
|
@tailcaller
|
||||||
def menu_principal(self, tag=None, machine=None, proprio=None):
|
def menu_principal(self, tag=None, machine=None, proprio=None):
|
||||||
"""Menu principal de l'application affiché au lancement"""
|
"""Menu principal de l'application.
|
||||||
a = attributs
|
|
||||||
|
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 = {
|
menu_droits = {
|
||||||
'default' : [a.cableur, a.nounou],
|
'': [attributs.cableur, attributs.nounou],
|
||||||
'aKM' : [a.nounou],
|
'aKM': [attributs.nounou],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# On initialise le menu avec des actions "génériques"
|
||||||
menu = {
|
menu = {
|
||||||
'aA' : {'text':"Inscrire un nouvel adhérent", 'callback': self.create_adherent,},
|
'aA': {
|
||||||
'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."},
|
'text': "Inscrire un nouvel adhérent",
|
||||||
'aMA': {'text':"Ajouter une machine à un adhérent", 'callback': self.create_machine_adherent},
|
'callback': self.create_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},
|
'mA': {
|
||||||
'aC' : {'text':"Inscrire un nouveau club", 'callback': self.create_club},
|
'text': "Modifier l'inscription d'un adhérent",
|
||||||
'mC' : {'text':"Modifier un club", 'callback': self.modif_club},
|
'callback': self.modif_adherent,
|
||||||
'aMC': {'text':"Ajouter une machine à un club", 'callback': self.create_machine_club},
|
'help': (
|
||||||
#'dC' : {'text':"Détruire un club", 'callback': self.delete_club},
|
"Changer la chambre, la remarque, la section, "
|
||||||
'aKM': {'text':"Ajouter une machine à l'association", 'callback': self.create_machine_crans},
|
"la carte d'étudiant ou précâbler."
|
||||||
'' : {'text':"---------------------------------------",'callback': None},
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
'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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
### Les clef qui n'existe pas sont toute renvoyé sur la clef ''
|
|
||||||
#menu_order = ["aA", "mA", "aMA", "dA", "", "mM", "dM", " ", "aC", "mC", "aMC", "dC", " ", "aKM"]
|
# On liste les actions pour définir leur ordre d'affichage
|
||||||
#menu_order = ["aA", "mA", "aMA", "", "mM", " ", "aC", "mC", "aMC", " ", "aKM"]
|
menu_order = [
|
||||||
menu_order = ["aA", "mA", "aMA", "", "mM", "", "aC", "mC", "aMC", "", "aKM"]
|
# 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:
|
if machine and not proprio:
|
||||||
proprio = machine.proprio()
|
proprio = machine.proprio()
|
||||||
|
|
||||||
|
# Sauf si le propriétaire est le Cr@ns
|
||||||
if isinstance(proprio, objets.AssociationCrans):
|
if isinstance(proprio, objets.AssociationCrans):
|
||||||
proprio = None
|
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:
|
if machine or proprio:
|
||||||
menu_order = [''] + menu_order
|
menu_order = [''] + menu_order
|
||||||
|
|
||||||
|
# Actions spécifiques à une machine
|
||||||
if machine:
|
if machine:
|
||||||
menu_machine = {
|
menu_machine = {
|
||||||
'mMc' : {
|
'mMc': {
|
||||||
'text':"Modifier la machine %s" % machine['host'][0],
|
'text': "Modifier la machine %s" % machine['host'][0],
|
||||||
'callback': TailCall(self.modif_machine, machine=machine),
|
'callback': TailCall(self.modif_machine, machine=machine),
|
||||||
'help':"Changer le nom ou la MAC d'une machine."
|
'help': "Changer le nom ou la MAC d'une machine."
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
menu_machine_order = ['mMc']
|
menu_machine_order = ['mMc']
|
||||||
menu.update(menu_machine)
|
menu.update(menu_machine)
|
||||||
menu_order = menu_machine_order + menu_order
|
menu_order = menu_machine_order + menu_order
|
||||||
|
|
||||||
|
# Actions spécifiques à un proprio (adhérent ou club)
|
||||||
if proprio:
|
if proprio:
|
||||||
menu_adherent = {
|
|
||||||
'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_club = {
|
|
||||||
'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)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if 'adherent' in proprio['objectClass']:
|
if 'adherent' in proprio['objectClass']:
|
||||||
menu_proprio = menu_adherent
|
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']
|
menu_proprio_order = ['mAc', 'aMc']
|
||||||
elif 'club' in proprio['objectClass']:
|
elif 'club' in proprio['objectClass']:
|
||||||
menu_proprio = menu_club
|
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']
|
menu_proprio_order = ['mCc', 'aMc']
|
||||||
else:
|
else:
|
||||||
raise EnvironmentError("Je ne connais que des adherents et des club comme proprio")
|
raise EnvironmentError(
|
||||||
|
"Le proprio sélectionné doit être un adhérent ou un club."
|
||||||
|
)
|
||||||
menu.update(menu_proprio)
|
menu.update(menu_proprio)
|
||||||
menu_order = menu_proprio_order + menu_order
|
menu_order = menu_proprio_order + menu_order
|
||||||
|
|
||||||
def box(default_item=None):
|
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 = []
|
choices = []
|
||||||
|
|
||||||
|
# On reprend les clés dans l'ordre défini par la liste menu_order
|
||||||
for key in menu_order:
|
for key in menu_order:
|
||||||
if self.has_right(menu_droits.get(key, menu_droits['default'])):
|
# Vérification des droits
|
||||||
choices.append((key, menu[key]['text'], menu[key].get('help', "")))
|
if self.has_right(menu_droits.get(key, menu_droits[''])):
|
||||||
while choices[-1][0] == '':
|
choices.append((
|
||||||
choices=choices[:-1]
|
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] == '':
|
while choices[0][0] == '':
|
||||||
choices=choices[1:]
|
choices = choices[1:]
|
||||||
|
while choices[-1][0] == '':
|
||||||
|
choices = choices[:-1]
|
||||||
|
|
||||||
return self.dialog.menu(
|
return self.dialog.menu(
|
||||||
"Que souhaitez vous faire ?",
|
"Que souhaitez vous faire ?",
|
||||||
width=0,
|
width=0,
|
||||||
|
@ -141,23 +287,79 @@ class GestCrans(adherent.Dialog, club.Dialog, machine.Dialog):
|
||||||
timeout=self.timeout,
|
timeout=self.timeout,
|
||||||
cancel_label="Quitter",
|
cancel_label="Quitter",
|
||||||
backtitle=self._connected_as(),
|
backtitle=self._connected_as(),
|
||||||
choices=choices)
|
choices=choices,
|
||||||
|
)
|
||||||
|
|
||||||
(code, tag) = self.handle_dialog(TailCall(handle_exit_code, self.dialog, self.dialog.DIALOG_ESC), box, tag)
|
# Appel de dialog pour afficher le menu construit précédemment.
|
||||||
self_cont = TailCall(self.menu_principal, tag=tag, proprio=proprio, machine=machine)
|
# Oui, il faut passer la fonction qui gère une annulation d'abord...
|
||||||
callback = menu.get(tag, menu[''])['callback']
|
(code, tag) = self.handle_dialog(
|
||||||
if handle_exit_code(self.dialog, code) and callback:
|
TailCall(
|
||||||
return TailCall(callback, cont=TailCall(self.menu_principal, tag=tag, machine=machine, proprio=proprio))
|
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:
|
else:
|
||||||
return self_cont
|
# 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__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='Interface utilisateur du système de gestion des machines et adhérents du crans')
|
parser = argparse.ArgumentParser(
|
||||||
parser.add_argument('--test', help='Utiliser la base de test', dest='ldap_test', default=False, action='store_true')
|
description=(
|
||||||
parser.add_argument('--debug', help='Afficher des info de débug comme les tracebacks', dest='debug_enable', default=False, action='store_true')
|
'Interface utilisateur du système de gestion '
|
||||||
parser.add_argument('login', help="Se connecter en tant qu'un autre utilisateur", type=str, default=None, nargs='?')
|
'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()
|
args = parser.parse_args()
|
||||||
main(GestCrans(ldap_test=args.ldap_test, debug_enable=args.debug_enable, custom_user=args.login))
|
main(GestCrans(
|
||||||
|
ldap_test=args.ldap_test,
|
||||||
|
debug_enable=args.debug_enable,
|
||||||
|
custom_user=args.login,
|
||||||
|
))
|
||||||
os.system('clear')
|
os.system('clear')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue