[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")
|
||||
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):
|
||||
help_info = f"""{self._menu_header()}
|
||||
|
||||
|
|
@ -291,6 +346,107 @@ class TODO:
|
|||
"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):
|
||||
# Une entrée {"section": "..."} affiche un titre de section SANS
|
||||
# consommer de numéro : la numérotation reste continue sur les vraies
|
||||
|
|
|
|||
|
|
@ -1649,6 +1649,42 @@ TRANSLATIONS = {
|
|||
"fr": "Rien à copier.",
|
||||
"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": {
|
||||
"fr": "moy",
|
||||
"en": "avg",
|
||||
|
|
@ -1657,6 +1693,63 @@ TRANSLATIONS = {
|
|||
"fr": "Dernière 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": {
|
||||
"fr": "📐 Redimensionner le disque d'une VM",
|
||||
"en": "📐 Resize a VM disk",
|
||||
|
|
@ -1987,6 +2080,18 @@ TRANSLATIONS = {
|
|||
"fr": "Attente de l'arrêt de la VM…",
|
||||
"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.": {
|
||||
"fr": "VM éteinte.",
|
||||
"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