""" Vehicle drill-down navigation screen for the NEXUS AUTOPARTS console application. Guides the user through a four-level hierarchy: Brand -> Model -> Year -> Engine Each level presents a filterable list. After engine selection the screen navigates to the catalogue (``catalogo``) with the resolved ``model_year_engine`` id. """ from console.core.screens import Screen from console.core.keybindings import Key # Ordered sequence of drill-down levels. _LEVELS = ("brand", "model", "year", "engine") # Human-readable titles for each level (Spanish). _LEVEL_TITLES = { "brand": "Seleccione Marca", "model": "Seleccione Modelo", "year": "Seleccione Ano", "engine": "Seleccione Motor", } class VehiculoNavScreen(Screen): """Four-level vehicle drill-down: Brand -> Model -> Year -> Engine.""" def __init__(self): super().__init__("vehiculo_nav", "Consulta por Vehiculo") # ------------------------------------------------------------------ # Internal helpers # ------------------------------------------------------------------ def _get_title_for_level(self, context): """Return the title string for the current drill-down level.""" level = context.get("level", "brand") return _LEVEL_TITLES.get(level, "Seleccione") def _get_subtitle(self, context): """Build a breadcrumb subtitle from selections made so far. Example: ``"TOYOTA > CAMRY > 2023 > Seleccione motor"`` """ parts = [] if context.get("brand"): parts.append(context["brand"]) if context.get("model"): parts.append(context["model"]) if context.get("year") is not None: parts.append(str(context["year"])) level = context.get("level", "brand") parts.append(_LEVEL_TITLES.get(level, "")) return " > ".join(parts) def _load_items(self, context, db): """Fetch the item list from the database for the current level.""" level = context.get("level", "brand") if level == "brand": context["all_items"] = db.get_brands() elif level == "model": context["all_items"] = db.get_models(brand=context.get("brand")) elif level == "year": context["all_items"] = db.get_years( brand=context.get("brand"), model=context.get("model"), ) elif level == "engine": context["all_items"] = db.get_engines( brand=context.get("brand"), model=context.get("model"), year=context.get("year"), ) else: context["all_items"] = [] self._apply_filter(context) def _apply_filter(self, context): """Reduce ``all_items`` to those matching ``filter_text``. Matching is a case-insensitive substring test on the display name. """ level = context.get("level", "brand") ft = context.get("filter_text", "").lower() all_items = context.get("all_items", []) if ft: context["filtered_items"] = [ item for item in all_items if ft in self._get_display_name(item, level).lower() ] else: context["filtered_items"] = list(all_items) @staticmethod def _get_display_name(item, level): """Extract the human-readable display string from an item dict.""" if level == "year": return str(item.get("year", "")) return item.get("name", "") # ------------------------------------------------------------------ # Screen interface # ------------------------------------------------------------------ def render(self, context, db, renderer): # First-render initialisation if "level" not in context: context["level"] = "brand" context["filter_text"] = "" context["selected_index"] = 0 self._load_items(context, db) level = context["level"] title = self._get_title_for_level(context) subtitle = self._get_subtitle(context) renderer.draw_header(title, subtitle) # Build the (number, label) tuples expected by draw_filter_list. filtered = context.get("filtered_items", []) display_items = [ (str(idx + 1), self._get_display_name(item, level)) for idx, item in enumerate(filtered) ] renderer.draw_filter_list( display_items, context.get("filter_text", ""), context.get("selected_index", 0), title=f"SELECCIONAR {level.upper()}", ) renderer.draw_footer([ ("Escriba", "Filtrar"), ("ENTER", "Seleccionar"), ("\u2191\u2193", "Mover"), ("ESC", "Atras"), ]) def on_key(self, key, context, db, renderer, nav): filtered = context.get("filtered_items", []) level = context.get("level", "brand") # -- ESC: go back one level, or return to menu ---------------- if key == Key.ESCAPE: if level == "brand": return "back" prev = _LEVELS[_LEVELS.index(level) - 1] context["level"] = prev context["filter_text"] = "" context["selected_index"] = 0 self._load_items(context, db) return None # -- ENTER: select item and advance --------------------------- if key == Key.ENTER and filtered: idx = context.get("selected_index", 0) if idx >= len(filtered): return None selected = filtered[idx] if level == "brand": context["brand"] = selected["name"] context["level"] = "model" elif level == "model": context["model"] = selected["name"] context["level"] = "year" elif level == "year": context["year"] = selected["year"] context["level"] = "engine" elif level == "engine": context["engine_id"] = selected["id"] context["engine_name"] = selected["name"] # Resolve the model_year_engine row mye_list = db.get_model_year_engine( context["brand"], context["model"], context["year"], context["engine_id"], ) if mye_list: mye_id = mye_list[0]["id"] return ( "catalogo", { "mye_id": mye_id, "brand": context["brand"], "model": context["model"], "year": context["year"], "engine": context["engine_name"], }, f"{context['brand']} {context['model']} {context['year']}", ) else: renderer.show_message( "No se encontro configuracion para este vehiculo", "error", ) return None # Reset filter for the new level context["filter_text"] = "" context["selected_index"] = 0 self._load_items(context, db) return None # -- Arrow keys: move selection cursor ------------------------ if key == Key.UP: if context.get("selected_index", 0) > 0: context["selected_index"] -= 1 return None if key == Key.DOWN: if context.get("selected_index", 0) < len(filtered) - 1: context["selected_index"] += 1 return None # -- Backspace: trim filter text ------------------------------ if key in (Key.BACKSPACE, 8, 263): if context.get("filter_text"): context["filter_text"] = context["filter_text"][:-1] self._apply_filter(context) context["selected_index"] = 0 return None # -- Printable characters: append to filter ------------------- if isinstance(key, int) and 32 <= key <= 126: context["filter_text"] = context.get("filter_text", "") + chr(key) self._apply_filter(context) context["selected_index"] = 0 return None return None