Files
Autoparts-DB/console/screens/estadisticas.py

168 lines
5.3 KiB
Python

"""
Statistics dashboard screen for the AUTOPARTES console application.
Displays database table counts and coverage metrics retrieved via
:meth:`Database.get_stats`.
"""
from console.core.screens import Screen
from console.core.keybindings import Key
from console.config import APP_NAME, VERSION
from console.utils.formatting import format_number
# Human-readable labels for each database table counter.
_TABLE_LABELS = [
("brands", "Marcas"),
("models", "Modelos"),
("engines", "Motores"),
("years", "Anos"),
("part_categories", "Categorias"),
("part_groups", "Grupos de Partes"),
("parts", "Partes OEM"),
("aftermarket_parts", "Partes Aftermarket"),
("manufacturers", "Fabricantes"),
("part_cross_references","Cross-References"),
]
# Footer key labels
_FOOTER = [
("F5", "Refrescar"),
("F10", "Menu"),
("ESC", "Atras"),
]
class EstadisticasScreen(Screen):
"""Read-only statistics dashboard showing database counters."""
def __init__(self):
super().__init__(name="estadisticas", title="Estadisticas del Sistema")
self._stats = None
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _load_stats(self, db):
"""Fetch fresh statistics from the database."""
try:
self._stats = db.get_stats()
except Exception:
self._stats = None
def _build_fields(self):
"""Build the detail fields list from the cached stats dict."""
if self._stats is None:
return [("Error", "No se pudieron cargar las estadisticas")]
fields = []
# -- Section: BASE DE DATOS --
for key, label in _TABLE_LABELS:
value = self._stats.get(key, 0)
fields.append((label, format_number(value)))
return fields
def _build_coverage_fields(self):
"""Build coverage / summary fields."""
if self._stats is None:
return []
fields = []
# Vehicle-part fitments
fitments = self._stats.get("vehicle_parts", 0)
fields.append(("Fitments", format_number(fitments)))
# Top brands by fitment count
top_brands = self._stats.get("top_brands", [])
if top_brands:
parts = []
for b in top_brands[:5]:
parts.append(f"{b['name']}({format_number(b['count'])})")
fields.append(("Top marcas", " ".join(parts)))
return fields
# ------------------------------------------------------------------
# Screen interface
# ------------------------------------------------------------------
def render(self, context, db, renderer):
# Load stats on first render (or after refresh)
if self._stats is None:
self._load_stats(db)
# Header
renderer.draw_header(
f" {APP_NAME} v{VERSION}",
" Estadisticas ",
)
h, w = renderer.get_size()
# -- Section title: BASE DE DATOS --
section_title = " BASE DE DATOS "
border_char = "\u2500" # ─
pad_len = max(w - 4 - len(section_title), 0)
section_line = border_char * 2 + section_title + border_char * pad_len
renderer.draw_text(3, 2, section_line[:w - 4], "title")
# Database counters
db_fields = self._build_fields()
max_label = max((len(lbl) for lbl, _ in db_fields), default=10)
dot_total = max_label + 4
row = 5
for label, value in db_fields:
if row >= h - 6:
break
dots = "." * (dot_total - len(label))
label_part = f" {label}{dots}: "
renderer.draw_text(row, 0, label_part, "field_label")
renderer.draw_text(row, len(label_part), str(value), "field_value")
row += 1
# -- Section title: COBERTURA --
row += 1
if row < h - 5:
section_title2 = " COBERTURA "
pad_len2 = max(w - 4 - len(section_title2), 0)
section_line2 = border_char * 2 + section_title2 + border_char * pad_len2
renderer.draw_text(row, 2, section_line2[:w - 4], "title")
row += 2
coverage_fields = self._build_coverage_fields()
cov_max_label = max(
(len(lbl) for lbl, _ in coverage_fields), default=10
)
cov_dot_total = cov_max_label + 4
for label, value in coverage_fields:
if row >= h - 3:
break
dots = "." * (cov_dot_total - len(label))
label_part = f" {label}{dots}: "
renderer.draw_text(row, 0, label_part, "field_label")
renderer.draw_text(
row, len(label_part), str(value), "field_value"
)
row += 1
# Footer
renderer.draw_footer(_FOOTER)
def on_key(self, key, context, db, renderer, nav):
# F5: refresh stats
if key == Key.F5:
self._stats = None # will reload on next render
return None
# ESC or Backspace: go back
if key in (Key.ESCAPE, Key.BACKSPACE):
return "back"
return None