Files
Autoparts-DB/console/screens/menu_principal.py
consultoria-as 7b2a904498 feat: migrate to PostgreSQL + SQLAlchemy ORM, rebrand to Nexus Autoparts
- Migrate from SQLite to PostgreSQL with normalized schema
- Add 11 lookup tables (fuel_type, body_type, drivetrain, transmission,
  materials, position_part, manufacture_type, quality_tier, countries,
  reference_type, shapes)
- Rewrite dashboard/server.py (76 routes) using SQLAlchemy text() queries
- Rewrite console/db.py (27 methods) using SQLAlchemy ORM
- Add models.py with 27 SQLAlchemy model definitions
- Add config.py for centralized DB_URL configuration
- Add migrate_to_postgres.py migration script
- Add docs/METABASE_GUIDE.md with complete data entry guide
- Rebrand from "AUTOPARTS DB" to "NEXUS AUTOPARTS"
- Fill vehicle data gaps via NHTSA API + heuristics:
  engines (cylinders, power, torque), brands (country, founded_year),
  models (body_type, production years), MYE (drivetrain, transmission, trim)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 05:24:47 +00:00

138 lines
4.5 KiB
Python

"""
Main menu screen for the NEXUS AUTOPARTS console application.
Displays a numbered Pick-style menu with navigation options for all
application sections. Number keys jump directly; arrow keys move the
selection; ENTER activates.
"""
from console.core.screens import Screen
from console.core.keybindings import Key
from console.config import APP_NAME, APP_SUBTITLE, VERSION
# Menu items: list of (display_number, label, screen_name).
# Separators use display_number None and screen_name None.
_MENU_ITEMS = [
("1", "Consulta por Vehiculo", "vehiculo_nav"),
("2", "Busqueda por Numero de Parte", "buscar_parte"),
("3", "Busqueda por Descripcion", "buscar_texto"),
("4", "Decodificador VIN", "vin_decoder"),
("5", "Catalogo de Categorias", "catalogo"),
(None, None, None), # separator
("6", "Administracion de Partes", "admin_partes"),
("7", "Administracion de Fabricantes", "admin_fabricantes"),
("8", "Cross-References", "admin_crossref"),
("9", "Importar / Exportar Datos", "admin_import"),
(None, None, None), # separator
("0", "Estadisticas del Sistema", "estadisticas"),
]
# Quick lookup: digit character -> screen name
_KEY_MAP = {item[0]: item[2] for item in _MENU_ITEMS if item[0] is not None}
# Footer key labels
_FOOTER = [
("F1", "Ayuda"),
("F3", "Buscar"),
("F10", "Menu"),
("ESC", "Salir"),
]
class MenuPrincipalScreen(Screen):
"""Main menu screen with numbered items and arrow-key navigation."""
def __init__(self):
super().__init__(name="menu", title="Menu Principal")
self._selected = 0 # index into _MENU_ITEMS (skipping separators)
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _selectable_indices(self):
"""Return list of indices in _MENU_ITEMS that are not separators."""
return [i for i, item in enumerate(_MENU_ITEMS) if item[0] is not None]
def _move_selection(self, direction):
"""Move selection up (-1) or down (+1), skipping separators."""
indices = self._selectable_indices()
if not indices:
return
try:
pos = indices.index(self._selected)
except ValueError:
pos = 0
pos = max(0, min(len(indices) - 1, pos + direction))
self._selected = indices[pos]
# ------------------------------------------------------------------
# Screen interface
# ------------------------------------------------------------------
def render(self, context, db, renderer):
# Header
renderer.draw_header(
f" {APP_NAME} v{VERSION}",
f"{APP_SUBTITLE} ",
)
# Build items list for draw_menu.
# Separators use the special "---" marker understood by the renderer.
menu_items = []
for num, label, _screen in _MENU_ITEMS:
if num is None:
menu_items.append(("---", ""))
else:
menu_items.append((num, label))
renderer.draw_menu(
menu_items,
selected_index=self._selected,
title="MENU PRINCIPAL",
)
# Footer
renderer.draw_footer(_FOOTER)
def on_key(self, key, context, db, renderer, nav):
# --- Number keys: direct navigation ---
if 48 <= key <= 57: # ord('0') .. ord('9')
digit = chr(key)
screen_name = _KEY_MAP.get(digit)
if screen_name:
label = next(
(lbl for num, lbl, _ in _MENU_ITEMS if num == digit),
screen_name,
)
return (screen_name, {}, label)
# --- Arrow keys ---
if key == Key.UP:
self._move_selection(-1)
return None
if key == Key.DOWN:
self._move_selection(1)
return None
# --- ENTER: activate selected ---
if key == Key.ENTER:
item = _MENU_ITEMS[self._selected]
num, label, screen_name = item
if screen_name is not None:
return (screen_name, {}, label)
return None
# --- ESC: quit confirmation ---
if key == Key.ESCAPE:
confirmed = renderer.show_message(
"Desea salir de la aplicacion?", "confirm"
)
if confirmed:
return "quit"
return None
return None