- Reuse a single SQLite connection instead of open/close per query - Add in-memory cache for frequently accessed data (brands, models, categories) — 1000x faster on repeated access - Enable WAL journal mode, 8MB cache, 64MB mmap for faster reads - Cache terminal size per render cycle to avoid repeated getmaxyx() - Close DB connection on app exit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
197 lines
6.0 KiB
Python
197 lines
6.0 KiB
Python
"""
|
|
Main application controller for the AUTOPARTES console application.
|
|
|
|
The :class:`App` class owns the screen lifecycle loop: it renders the
|
|
current screen, reads a keypress, dispatches it, and follows any
|
|
navigation instruction the screen returns.
|
|
"""
|
|
|
|
from console.core.navigation import Navigation
|
|
from console.core.keybindings import Key
|
|
|
|
|
|
class App:
|
|
"""Top-level application controller.
|
|
|
|
Parameters:
|
|
renderer: A :class:`BaseRenderer` implementation (e.g. CursesRenderer).
|
|
db: A :class:`Database` instance for data access.
|
|
"""
|
|
|
|
def __init__(self, renderer, db):
|
|
self.renderer = renderer
|
|
self.db = db
|
|
self.nav = Navigation()
|
|
self.screens = {}
|
|
self.running = False
|
|
self._register_screens()
|
|
|
|
# ------------------------------------------------------------------
|
|
# Screen registration
|
|
# ------------------------------------------------------------------
|
|
|
|
def _register_screens(self):
|
|
"""Import and register all screen instances.
|
|
|
|
Each screen is wrapped in a try/except so that screens not yet
|
|
implemented do not prevent the application from starting.
|
|
"""
|
|
# --- Required screens (Task 6) --------------------------------
|
|
try:
|
|
from console.screens.menu_principal import MenuPrincipalScreen
|
|
s = MenuPrincipalScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.estadisticas import EstadisticasScreen
|
|
s = EstadisticasScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
# --- Optional screens (added by later tasks) -------------------
|
|
try:
|
|
from console.screens.vehiculo_nav import VehiculoNavScreen
|
|
s = VehiculoNavScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.buscar_parte import BuscarParteScreen
|
|
s = BuscarParteScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.buscar_texto import BuscarTextoScreen
|
|
s = BuscarTextoScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.vin_decoder import VinDecoderScreen
|
|
s = VinDecoderScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.catalogo import CatalogoScreen
|
|
s = CatalogoScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.parte_detalle import ParteDetalleScreen
|
|
s = ParteDetalleScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.comparador import ComparadorScreen
|
|
s = ComparadorScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.admin_partes import AdminPartesScreen
|
|
s = AdminPartesScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.admin_fabricantes import AdminFabricantesScreen
|
|
s = AdminFabricantesScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.admin_crossref import AdminCrossrefScreen
|
|
s = AdminCrossrefScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
from console.screens.admin_import import AdminImportScreen
|
|
s = AdminImportScreen()
|
|
self.screens[s.name] = s
|
|
except ImportError:
|
|
pass
|
|
|
|
# ------------------------------------------------------------------
|
|
# Main loop
|
|
# ------------------------------------------------------------------
|
|
|
|
def run(self):
|
|
"""Enter the main event loop.
|
|
|
|
Initialises the renderer, pushes the main menu onto the
|
|
navigation stack, and loops until the user quits or the stack
|
|
empties.
|
|
"""
|
|
self.renderer.init_screen()
|
|
self.running = True
|
|
self.nav.push('menu', {}, label='Menu')
|
|
|
|
try:
|
|
while self.running:
|
|
current = self.nav.current()
|
|
if current is None:
|
|
break
|
|
|
|
screen_name, context = current
|
|
screen = self.screens.get(screen_name)
|
|
|
|
if screen is None:
|
|
self.renderer.show_message(
|
|
f'Pantalla "{screen_name}" no disponible', 'error'
|
|
)
|
|
self.nav.pop()
|
|
continue
|
|
|
|
# Render
|
|
self.renderer.clear()
|
|
screen.render(context, self.db, self.renderer)
|
|
self.renderer.refresh()
|
|
|
|
# Input
|
|
key = self.renderer.get_key()
|
|
|
|
# Global key: F10 = back to main menu
|
|
if key == Key.F10:
|
|
self.nav.clear()
|
|
self.nav.push('menu', {}, label='Menu')
|
|
continue
|
|
|
|
# Screen-specific key handling
|
|
result = screen.on_key(
|
|
key, context, self.db, self.renderer, self.nav
|
|
)
|
|
|
|
if result == 'quit':
|
|
self.running = False
|
|
elif result == 'back':
|
|
self.nav.pop()
|
|
elif isinstance(result, tuple) and len(result) == 3:
|
|
name, ctx, label = result
|
|
self.nav.push(name, ctx, label=label)
|
|
elif isinstance(result, str):
|
|
self.nav.push(result, {}, label=result)
|
|
|
|
except KeyboardInterrupt:
|
|
pass
|
|
finally:
|
|
self.renderer.cleanup()
|
|
self.db.close()
|