[ADD] todo: navigation telemetry
Records the menus visited and shows them as a tree, a kanban or a list. The tree is derived from the code, so a command never visited appears too, and can be run from there. --- FR --- Enregistre les menus visités et les présente en arbre, en kanban ou en liste. L'arbre est dérivé du code, si bien qu'une commande jamais visitée y figure aussi, et peut être lancée de là. Assisted-by: Claude Opus 4.8
This commit is contained in:
parent
7d4922bf09
commit
4dfe058c70
3 changed files with 1506 additions and 0 deletions
|
|
@ -117,6 +117,61 @@ class TODO:
|
||||||
set_lang("en")
|
set_lang("en")
|
||||||
print(t("Language changed to: English"))
|
print(t("Language changed to: English"))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
with open(self.config_file.get_logo_ascii_file_path()) as my_file:
|
||||||
|
print(my_file.read())
|
||||||
|
self._ask_language()
|
||||||
|
print(t("Opening TODO ..."))
|
||||||
|
print(f"🤖 {t('=> Enter your choice by number and press Enter!')}")
|
||||||
|
help_info = f"""{self._menu_header()}
|
||||||
|
[1] {t("Execute")}
|
||||||
|
[2] {t("Install")}
|
||||||
|
[3] {t("Question")}
|
||||||
|
[4] {t("Fork - Open TODO in a new tab")}
|
||||||
|
[5] {t("Navigation telemetry (TUI)")}
|
||||||
|
[6] {t("Configuration")}
|
||||||
|
[0] {t("Quit")}
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
status = click.prompt(help_info)
|
||||||
|
except NameError:
|
||||||
|
print("Do")
|
||||||
|
print(f"source ./{VENV_ERPLIBRE}/bin/activate && make")
|
||||||
|
sys.exit(1)
|
||||||
|
except ImportError:
|
||||||
|
print("Do")
|
||||||
|
print(f"source ./{VENV_ERPLIBRE}/bin/activate && make")
|
||||||
|
sys.exit(1)
|
||||||
|
except click.exceptions.Abort:
|
||||||
|
sys.exit(0)
|
||||||
|
print()
|
||||||
|
if status == "0":
|
||||||
|
break
|
||||||
|
elif status == "1":
|
||||||
|
self.prompt_execute()
|
||||||
|
elif status == "2":
|
||||||
|
self.prompt_install()
|
||||||
|
elif status == "3":
|
||||||
|
self.execute_prompt_ia()
|
||||||
|
elif status == "4":
|
||||||
|
# cmd = (
|
||||||
|
# f"gnome-terminal --tab -- bash -c 'source"
|
||||||
|
# f" ./{VENV_ERPLIBRE}/bin/activate;make todo'"
|
||||||
|
# )
|
||||||
|
cmd = "make todo"
|
||||||
|
self.execute.exec_command_live(cmd, source_erplibre=True)
|
||||||
|
elif status == "5":
|
||||||
|
self._todo_telemetry_tui()
|
||||||
|
elif status == "6":
|
||||||
|
self.prompt_configuration()
|
||||||
|
# elif status == "3" or status == "install":
|
||||||
|
# print("install")
|
||||||
|
else:
|
||||||
|
print(t("Command not found !"))
|
||||||
|
|
||||||
|
print(status)
|
||||||
|
|
||||||
def prompt_execute(self):
|
def prompt_execute(self):
|
||||||
help_info = f"""{self._menu_header()}
|
help_info = f"""{self._menu_header()}
|
||||||
|
|
||||||
|
|
@ -291,6 +346,107 @@ class TODO:
|
||||||
"prompt_configuration": "Configuration",
|
"prompt_configuration": "Configuration",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _menu_header(self):
|
||||||
|
"""En-tête de menu : fil d'Ariane (dérivé de la pile d'appels) suivi de
|
||||||
|
la ligne « Commande : ». Le fil situe le menu courant et se copie pour
|
||||||
|
décrire sans ambiguïté où l'on se trouve."""
|
||||||
|
crumbs = []
|
||||||
|
for frame_info in reversed(inspect.stack()):
|
||||||
|
if frame_info.frame.f_locals.get("self") is not self:
|
||||||
|
continue
|
||||||
|
label = self._MENU_LABELS.get(frame_info.function)
|
||||||
|
if label and (not crumbs or crumbs[-1] != label):
|
||||||
|
crumbs.append(label)
|
||||||
|
header = ""
|
||||||
|
if crumbs:
|
||||||
|
header = "📍 " + " › ".join(crumbs) + "\n"
|
||||||
|
# Télémétrie de navigation (best-effort, ne casse jamais le menu).
|
||||||
|
try:
|
||||||
|
from script.todo import todo_telemetry
|
||||||
|
|
||||||
|
todo_telemetry.record(" › ".join(crumbs))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return header + t("Command:")
|
||||||
|
|
||||||
|
def _todo_telemetry_tui(self):
|
||||||
|
"""Ouvre le TUI de télémétrie (arbre/Kanban). Une commande choisie est
|
||||||
|
exécutée au retour (hors du TUI) ; on propose ensuite de REVENIR (l'état
|
||||||
|
et la position du curseur sont restaurés) ou de quitter."""
|
||||||
|
from script.todo.todo_telemetry import run_tui
|
||||||
|
|
||||||
|
from script.todo import textual_setup
|
||||||
|
|
||||||
|
if not textual_setup.ensure():
|
||||||
|
return
|
||||||
|
state = None
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
result = run_tui(state=state)
|
||||||
|
except ImportError:
|
||||||
|
return
|
||||||
|
if not result:
|
||||||
|
return
|
||||||
|
action, state = result
|
||||||
|
if not action:
|
||||||
|
return # quitté sans choisir de commande
|
||||||
|
method, kwargs = action
|
||||||
|
# Fil d'Ariane : la commande étant lancée DEPUIS la télémétrie (et
|
||||||
|
# non via la navigation), aucun menu n'a affiché le chemin. On le
|
||||||
|
# montre ici (dernier segment traduit + icône) et on l'enregistre.
|
||||||
|
path = state.get("path") if isinstance(state, dict) else None
|
||||||
|
if path:
|
||||||
|
segs = path.split(" › ")
|
||||||
|
segs[-1] = t(segs[-1])
|
||||||
|
print(f"\n📍 {' › '.join(segs)}")
|
||||||
|
try:
|
||||||
|
from script.todo import todo_telemetry
|
||||||
|
|
||||||
|
todo_telemetry.record(path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
fn = getattr(self, method, None)
|
||||||
|
if not callable(fn):
|
||||||
|
print(f"{t('Command not found !')} ({method})")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
fn(**(kwargs or {}))
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"{t('Command failed: ')}{exc}")
|
||||||
|
# Revenir (curseur restauré) ou quitter ?
|
||||||
|
ans = input(f"\n{t('Back to telemetry (r) or quit (Enter)? ')}")
|
||||||
|
if ans.strip().lower() not in ("r", "revenir", "o", "oui", "y"):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Préférences éditables depuis le menu Configuration : clé, libellé, et
|
||||||
|
# valeurs proposées (valeur stockée -> libellé affiché). Une seule table :
|
||||||
|
# l'écran, la lecture et l'écriture en découlent.
|
||||||
|
_PREF_CHOICES = {
|
||||||
|
"qemu_deploy_ui": (
|
||||||
|
"QEMU deployment interface",
|
||||||
|
(
|
||||||
|
("ask", "Ask every time"),
|
||||||
|
("tui", "TUI form"),
|
||||||
|
("cli", "Classic questions (line by line)"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"qemu_deploy_progress": (
|
||||||
|
"Display while deploying",
|
||||||
|
(
|
||||||
|
("cli", "CLI output (easy to copy)"),
|
||||||
|
("tui", "TUI, collapsible blocks per VM"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"migration_ui": (
|
||||||
|
"Odoo migration interface",
|
||||||
|
(
|
||||||
|
("ask", "Ask every time"),
|
||||||
|
("tui", "TUI form"),
|
||||||
|
("cli", "Classic questions (line by line)"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
def fill_help_info(self, choices):
|
def fill_help_info(self, choices):
|
||||||
# Une entrée {"section": "..."} affiche un titre de section SANS
|
# Une entrée {"section": "..."} affiche un titre de section SANS
|
||||||
# consommer de numéro : la numérotation reste continue sur les vraies
|
# consommer de numéro : la numérotation reste continue sur les vraies
|
||||||
|
|
|
||||||
|
|
@ -1649,6 +1649,42 @@ TRANSLATIONS = {
|
||||||
"fr": "Rien à copier.",
|
"fr": "Rien à copier.",
|
||||||
"en": "Nothing to copy.",
|
"en": "Nothing to copy.",
|
||||||
},
|
},
|
||||||
|
"TODO navigation telemetry": {
|
||||||
|
"fr": "TODO — télémétrie de navigation",
|
||||||
|
"en": "TODO navigation telemetry",
|
||||||
|
},
|
||||||
|
"navigations": {
|
||||||
|
"fr": "navigations",
|
||||||
|
"en": "navigations",
|
||||||
|
},
|
||||||
|
"menus": {
|
||||||
|
"fr": "menus",
|
||||||
|
"en": "menus",
|
||||||
|
},
|
||||||
|
"Telemetry reset.": {
|
||||||
|
"fr": "Télémétrie réinitialisée.",
|
||||||
|
"en": "Telemetry reset.",
|
||||||
|
},
|
||||||
|
"tree from code": {
|
||||||
|
"fr": "arbre issu du code",
|
||||||
|
"en": "tree from code",
|
||||||
|
},
|
||||||
|
"visited paths only": {
|
||||||
|
"fr": "chemins visités seulement",
|
||||||
|
"en": "visited paths only",
|
||||||
|
},
|
||||||
|
"Enter = run, F3 = view": {
|
||||||
|
"fr": "Entrée = exécuter, F3 = vue",
|
||||||
|
"en": "Enter = run, F3 = view",
|
||||||
|
},
|
||||||
|
"Enter = run, F3/F4 = views": {
|
||||||
|
"fr": "Entrée = exécuter, F3/F4 = vues",
|
||||||
|
"en": "Enter = run, F3/F4 = views",
|
||||||
|
},
|
||||||
|
"view": {
|
||||||
|
"fr": "vue",
|
||||||
|
"en": "view",
|
||||||
|
},
|
||||||
"avg": {
|
"avg": {
|
||||||
"fr": "moy",
|
"fr": "moy",
|
||||||
"en": "avg",
|
"en": "avg",
|
||||||
|
|
@ -1657,6 +1693,63 @@ TRANSLATIONS = {
|
||||||
"fr": "Dernière install :",
|
"fr": "Dernière install :",
|
||||||
"en": "Last install:",
|
"en": "Last install:",
|
||||||
},
|
},
|
||||||
|
"No command found.": {
|
||||||
|
"fr": "Aucune commande trouvée.",
|
||||||
|
"en": "No command found.",
|
||||||
|
},
|
||||||
|
"Back to telemetry (r) or quit (Enter)? ": {
|
||||||
|
"fr": "Revenir à la télémétrie (r) ou quitter (Entrée) ? ",
|
||||||
|
"en": "Back to telemetry (r) or quit (Enter)? ",
|
||||||
|
},
|
||||||
|
"F2 system · F3/F4 views · Enter run": {
|
||||||
|
"fr": "F2 système · F3/F4 vues · Entrée exécute",
|
||||||
|
"en": "F2 system · F3/F4 views · Enter run",
|
||||||
|
},
|
||||||
|
"State": {"fr": "État", "en": "State"},
|
||||||
|
"uptime": {"fr": "actif depuis", "en": "uptime"},
|
||||||
|
"load": {"fr": "charge", "en": "load"},
|
||||||
|
"cores": {"fr": "cœurs", "en": "cores"},
|
||||||
|
"Memory": {"fr": "Mémoire", "en": "Memory"},
|
||||||
|
"Disk": {"fr": "Disque", "en": "Disk"},
|
||||||
|
"Network": {"fr": "Réseau", "en": "Network"},
|
||||||
|
"Battery": {"fr": "Batterie", "en": "Battery"},
|
||||||
|
"Temperature": {"fr": "Température", "en": "Temperature"},
|
||||||
|
"lm-sensors absent — press i to install": {
|
||||||
|
"fr": "lm-sensors absent — appuyez sur i pour installer",
|
||||||
|
"en": "lm-sensors absent — press i to install",
|
||||||
|
},
|
||||||
|
"Sensors already available.": {
|
||||||
|
"fr": "Capteurs déjà disponibles.",
|
||||||
|
"en": "Sensors already available.",
|
||||||
|
},
|
||||||
|
"Sensors now available.": {
|
||||||
|
"fr": "Capteurs désormais disponibles.",
|
||||||
|
"en": "Sensors now available.",
|
||||||
|
},
|
||||||
|
"Still no temperature (reboot/modprobe may be needed).": {
|
||||||
|
"fr": "Toujours pas de température (redémarrage/modprobe requis ?).",
|
||||||
|
"en": "Still no temperature (reboot/modprobe may be needed).",
|
||||||
|
},
|
||||||
|
"Unknown package manager for lm-sensors.": {
|
||||||
|
"fr": "Gestionnaire de paquets inconnu pour lm-sensors.",
|
||||||
|
"en": "Unknown package manager for lm-sensors.",
|
||||||
|
},
|
||||||
|
"Proposed install command:": {
|
||||||
|
"fr": "Commande d'installation proposée :",
|
||||||
|
"en": "Proposed install command:",
|
||||||
|
},
|
||||||
|
"Install lm-sensors now? (y/N): ": {
|
||||||
|
"fr": "Installer lm-sensors maintenant ? (o/N) : ",
|
||||||
|
"en": "Install lm-sensors now? (y/N): ",
|
||||||
|
},
|
||||||
|
"Command failed: ": {
|
||||||
|
"fr": "Échec de la commande : ",
|
||||||
|
"en": "Command failed: ",
|
||||||
|
},
|
||||||
|
"Your distribution may package it as python3-textual.": {
|
||||||
|
"fr": "Votre distribution le fournit peut-être en python3-textual.",
|
||||||
|
"en": "Your distribution may package it as python3-textual.",
|
||||||
|
},
|
||||||
"Resize a VM disk": {
|
"Resize a VM disk": {
|
||||||
"fr": "📐 Redimensionner le disque d'une VM",
|
"fr": "📐 Redimensionner le disque d'une VM",
|
||||||
"en": "📐 Resize a VM disk",
|
"en": "📐 Resize a VM disk",
|
||||||
|
|
@ -1987,6 +2080,18 @@ TRANSLATIONS = {
|
||||||
"fr": "Attente de l'arrêt de la VM…",
|
"fr": "Attente de l'arrêt de la VM…",
|
||||||
"en": "Waiting for the VM to shut down...",
|
"en": "Waiting for the VM to shut down...",
|
||||||
},
|
},
|
||||||
|
"timeout": {
|
||||||
|
"fr": "délai max",
|
||||||
|
"en": "timeout",
|
||||||
|
},
|
||||||
|
"shutting down": {
|
||||||
|
"fr": "arrêt en cours",
|
||||||
|
"en": "shutting down",
|
||||||
|
},
|
||||||
|
"remaining": {
|
||||||
|
"fr": "restantes",
|
||||||
|
"en": "remaining",
|
||||||
|
},
|
||||||
"VM is off.": {
|
"VM is off.": {
|
||||||
"fr": "VM éteinte.",
|
"fr": "VM éteinte.",
|
||||||
"en": "VM is off.",
|
"en": "VM is off.",
|
||||||
|
|
|
||||||
1245
script/todo/todo_telemetry.py
Normal file
1245
script/todo/todo_telemetry.py
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue