[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
"""Écran de reprise de migration Odoo, en TUI.
|
|
|
|
|
|
|
|
|
|
Pendant de `qemu_deploy_form` pour l'outil de migration. Deux interfaces
|
|
|
|
|
posent la MÊME première question — « où en est-on, et par où reprend-on ? » —
|
|
|
|
|
et renvoient les MÊMES chaînes de réponse (« c », « n », « r », « q »,
|
|
|
|
|
« 0 »..« 4 », « 4.<version> ») que `TodoUpgrade.apply_resume_answer` traduit
|
|
|
|
|
en progression. La décision est donc écrite une seule fois.
|
|
|
|
|
|
|
|
|
|
- run_resume_tui(ctx, run_app=True) : renvoie la réponse, ou None pour
|
|
|
|
|
retomber sur les invites en ligne.
|
|
|
|
|
|
|
|
|
|
`ctx` vient de `TodoUpgrade.resume_context()` : pure donnée, aucun accès à la
|
|
|
|
|
base ni au disque depuis l'affichage.
|
|
|
|
|
"""
|
[REF] format : passer l'outillage et les tests sous ruff
Le formateur de ce dépôt est ruff depuis qu'il remplace black, qui ne connaît
aucune cible au-delà de py313 ; ce passage applique sa norme à l'arbre entier,
d'un coup, pour qu'aucun commit de fond n'ait à porter du style. L'écart tient
presque entièrement aux chaînes coupées à la main que ruff recolle quand elles
tiennent sur une ligne, et aux « with » multiples qu'il regroupe : aucune
valeur ne change, et les clés de traduction non plus.
Vérifié : la suite unitaire reste verte après le passage, et le contrôle de
syntaxe ne signale rien.
--- EN ---
This repository's formatter is ruff since it replaced black, which knows no
target beyond py313; this pass applies its standard to the whole tree at once,
so that no substantive commit has to carry style. The difference is almost
entirely the hand-split strings ruff joins back when they fit on one line, and
the multiple "with" it merges: no value changes, nor do the translation keys.
Checked: the unit suite stays green after the pass, and the syntax check
reports nothing.
Assisted-by: Claude Opus 5
2026-09-24 13:30:31 -04:00
|
|
|
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from script.todo.todo_i18n import t
|
|
|
|
|
except Exception: # pragma: no cover - repli si i18n indisponible
|
|
|
|
|
|
|
|
|
|
def t(key: str) -> str:
|
|
|
|
|
return key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def version_line(versions):
|
|
|
|
|
"""« 13✓ 14✓ 15 16 17 18 » — l'avancement des montées de version."""
|
|
|
|
|
return " ".join(
|
|
|
|
|
f"{v['version']}{'✓' if v['done'] else ''}" for v in versions
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def next_version(versions):
|
|
|
|
|
"""Première version pas encore migrée : celle que « continuer » reprend."""
|
|
|
|
|
for item in versions:
|
|
|
|
|
if not item["done"]:
|
|
|
|
|
return item["version"]
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_resume_tui(ctx, run_app: bool = True):
|
|
|
|
|
"""Écran de reprise. Renvoie la réponse choisie, ou None si annulé.
|
|
|
|
|
`run_app=False` renvoie l'instance sans la lancer (tests headless)."""
|
|
|
|
|
from textual.app import App, ComposeResult
|
|
|
|
|
from textual.containers import Horizontal, Vertical
|
|
|
|
|
from textual.widgets import (
|
|
|
|
|
Button,
|
|
|
|
|
DataTable,
|
|
|
|
|
Footer,
|
|
|
|
|
Header,
|
|
|
|
|
OptionList,
|
|
|
|
|
Static,
|
|
|
|
|
)
|
|
|
|
|
from textual.widgets.option_list import Option
|
|
|
|
|
|
|
|
|
|
result = {"answer": None}
|
|
|
|
|
|
|
|
|
|
class Resume(App):
|
|
|
|
|
CSS = """
|
|
|
|
|
#head { height: auto; padding: 0 1; color: $text-muted; }
|
|
|
|
|
#steps { height: auto; max-height: 12; border: solid $accent; }
|
|
|
|
|
#bumps { height: auto; max-height: 10; border: solid $panel; }
|
|
|
|
|
.grouptitle { color: $accent; text-style: bold; padding: 1 1 0 1; }
|
|
|
|
|
#actions { height: auto; padding: 1 1 0 1; }
|
|
|
|
|
#hint { height: auto; color: $text-muted; padding: 0 1; }
|
|
|
|
|
"""
|
|
|
|
|
BINDINGS = [
|
|
|
|
|
("c", "cont", t("Continue where it stopped")),
|
[ADD] migration: offer « go back to a step » on the resume screen
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
2026-08-12 01:58:38 -04:00
|
|
|
("b", "back_step", t("Go back to a step")),
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
("n", "new", t("New migration, erase everything")),
|
|
|
|
|
("r", "keep_zip", t("Keep the zip only")),
|
|
|
|
|
("q", "quit_nothing", t("Quit without doing anything")),
|
|
|
|
|
("escape", "quit_nothing", t("Quit without doing anything")),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def compose(self) -> ComposeResult:
|
|
|
|
|
yield Header()
|
|
|
|
|
yield Static(
|
|
|
|
|
f" {t('File'):<9}: {ctx['file']}\n"
|
|
|
|
|
f" {t('Database'):<9}: {ctx['database']}"
|
|
|
|
|
f" · {t('Target')} : {ctx['target']}\n"
|
|
|
|
|
f" {t('Started'):<9}: {ctx['started']}",
|
|
|
|
|
id="head",
|
|
|
|
|
)
|
|
|
|
|
yield Static(f"{t('Steps')}", classes="grouptitle")
|
|
|
|
|
yield DataTable(id="steps")
|
|
|
|
|
if ctx["versions"]:
|
|
|
|
|
yield Static(
|
|
|
|
|
f"{t('Version bumps')} ({version_line(ctx['versions'])})",
|
|
|
|
|
classes="grouptitle",
|
|
|
|
|
)
|
|
|
|
|
yield OptionList(id="bumps")
|
|
|
|
|
with Vertical():
|
|
|
|
|
with Horizontal(id="actions"):
|
|
|
|
|
yield Button(
|
|
|
|
|
t("Continue where it stopped"),
|
|
|
|
|
variant="primary",
|
|
|
|
|
id="a_cont",
|
|
|
|
|
)
|
|
|
|
|
yield Button(t("New migration"), id="a_new")
|
[ADD] migration: offer « go back to a step » on the resume screen
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
2026-08-12 01:58:38 -04:00
|
|
|
yield Button(t("Go back to a step"), id="a_back")
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
yield Button(t("Keep the zip only"), id="a_keep")
|
|
|
|
|
yield Button(t("Quit"), id="a_quit")
|
|
|
|
|
yield Static(
|
[ADD] migration: offer « go back to a step » on the resume screen
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
2026-08-12 01:58:38 -04:00
|
|
|
f" {t('Enter on a step or a version = replay from there')}"
|
|
|
|
|
f" · b = {t('Go back to a step')}",
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
id="hint",
|
|
|
|
|
)
|
|
|
|
|
yield Footer()
|
|
|
|
|
|
|
|
|
|
def on_mount(self) -> None:
|
|
|
|
|
self.title = t("Migration in progress")
|
|
|
|
|
table = self.query_one("#steps", DataTable)
|
|
|
|
|
table.cursor_type = "row"
|
|
|
|
|
table.add_columns("", "", t("Step"), t("Detail"))
|
|
|
|
|
for item in ctx["steps"]:
|
|
|
|
|
table.add_row(
|
|
|
|
|
f"[{item['step']}]",
|
|
|
|
|
item["icon"],
|
|
|
|
|
item["label"],
|
|
|
|
|
item["detail"],
|
|
|
|
|
)
|
|
|
|
|
# Curseur sur la première étape inachevée : c'est là que ça a
|
|
|
|
|
# calé, donc là qu'on veut probablement rejouer.
|
|
|
|
|
for index, item in enumerate(ctx["steps"]):
|
|
|
|
|
if item["icon"] != "✅":
|
|
|
|
|
table.move_cursor(row=index)
|
|
|
|
|
break
|
|
|
|
|
if ctx["versions"]:
|
|
|
|
|
bumps = self.query_one("#bumps", OptionList)
|
|
|
|
|
for item in ctx["versions"]:
|
|
|
|
|
mark = "✓" if item["done"] else " "
|
|
|
|
|
bumps.add_option(
|
|
|
|
|
Option(
|
|
|
|
|
f" {mark} Odoo {item['version']}.0 — "
|
|
|
|
|
f"{t('rebuilds the intermediate database')}",
|
|
|
|
|
id=str(item["version"]),
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
upcoming = next_version(ctx["versions"])
|
|
|
|
|
if upcoming is not None:
|
|
|
|
|
bumps.highlighted = [
|
|
|
|
|
v["version"] for v in ctx["versions"]
|
|
|
|
|
].index(upcoming)
|
|
|
|
|
|
|
|
|
|
# -- choix ------------------------------------------------------ #
|
|
|
|
|
def _answer(self, value):
|
|
|
|
|
result["answer"] = value
|
|
|
|
|
self.exit()
|
|
|
|
|
|
|
|
|
|
def on_data_table_row_selected(self, event) -> None:
|
|
|
|
|
index = event.cursor_row
|
|
|
|
|
if 0 <= index < len(ctx["steps"]):
|
|
|
|
|
self._answer(str(ctx["steps"][index]["step"]))
|
|
|
|
|
|
|
|
|
|
def on_option_list_option_selected(self, event) -> None:
|
|
|
|
|
self._answer(f"4.{event.option.id}")
|
|
|
|
|
|
|
|
|
|
def on_button_pressed(self, event) -> None:
|
[ADD] migration: offer « go back to a step » on the resume screen
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
2026-08-12 01:58:38 -04:00
|
|
|
if event.button.id == "a_back":
|
|
|
|
|
self.action_back_step()
|
|
|
|
|
return
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
mapping = {
|
|
|
|
|
"a_cont": "c",
|
|
|
|
|
"a_new": "n",
|
|
|
|
|
"a_keep": "r",
|
|
|
|
|
"a_quit": "q",
|
|
|
|
|
}
|
|
|
|
|
value = mapping.get(event.button.id)
|
|
|
|
|
if value:
|
|
|
|
|
self._answer(value)
|
|
|
|
|
|
[ADD] migration: offer « go back to a step » on the resume screen
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
2026-08-12 01:58:38 -04:00
|
|
|
def action_back_step(self) -> None:
|
|
|
|
|
"""Amener au tableau des étapes, où le choix se fait déjà.
|
|
|
|
|
|
|
|
|
|
Le mécanisme existait — Entrée sur une ligne — mais vivait dans une
|
|
|
|
|
ligne d'astuce sous quatre boutons nommés. Une capacité qu'il faut
|
|
|
|
|
deviner n'est pas offerte ; celle-ci porte donc une touche, un
|
|
|
|
|
bouton et un message, comme les autres.
|
|
|
|
|
"""
|
|
|
|
|
table = self.query_one("#steps", DataTable)
|
|
|
|
|
table.focus()
|
|
|
|
|
self.notify(t("Choose a step, Enter replays from there."))
|
|
|
|
|
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
def action_cont(self) -> None:
|
|
|
|
|
self._answer("c")
|
|
|
|
|
|
|
|
|
|
def action_new(self) -> None:
|
|
|
|
|
self._answer("n")
|
|
|
|
|
|
|
|
|
|
def action_keep_zip(self) -> None:
|
|
|
|
|
self._answer("r")
|
|
|
|
|
|
|
|
|
|
def action_quit_nothing(self) -> None:
|
|
|
|
|
self._answer("q")
|
|
|
|
|
|
|
|
|
|
app = Resume()
|
|
|
|
|
app._result = result # lecture par les tests headless
|
|
|
|
|
if not run_app:
|
|
|
|
|
return app
|
|
|
|
|
app.run()
|
|
|
|
|
return result["answer"]
|