Ubicación
racks/services/dashboard_sections.py
Descripción
Lógica compartida de ordenamiento de las secciones del Dashboard (Rooms, Maps/Rows, No Map). Proporciona la lista tipada de secciones ordenadas, consumida tanto por el render completo (racks/views.index) como por el partial HTMX (core/htmx_views.racks_list).
El servicio abstrae los dos mecanismos de persistencia:
- Blueprints:
Blueprint.sort_order(columna de la BD, por blueprint) - Secciones fijas (“Rooms”, “No Map”):
OrgUIPreference(por organización, con claves canónicasdashboard_rooms_sort/dashboard_nomap_sort)
Introducido en: v1.51.0 (2026-07-13), como refactor de la lógica de reorden manual ({% regroup %} → lista tipada compartida).
Funciones públicas
build_sections(racks_by_group, group_rollups, org_id, rooms=None) → list[dict]
Construye la lista ordenada de secciones para los templates del Dashboard.
Parámetros:
racks_by_group: dict[str, list[Rack]]— Diccionarioblueprint_name → [racks]. La clave""alberga los racks sin blueprint (No Map).group_rollups: dict[str, object]— Métricas agregadas por grupo (potencia, utilización). Típicamente decalculate_group_rollups().org_id: int | None— ID de la organización (para leer ui_prefs;Nonesi no autenticado).rooms: list[Room] | None— Lista de salas (DCIM Fase 3). Si se proporciona, emite la sección “Rooms” al inicio.
Retorna: Lista de dicts tipados {"kind": str, "key": str, "sort": tuple, ...} ordenada por s["sort"].
Tipos de sección:
kind: "rooms"— Caja de Salas (DCIM Fase 3). Fields:rooms: list[Room],sort.kind: "bp"— Blueprint (Map o Row). Fields:blueprint_id: int,name: str,mode: str(“map” o “row”),racks: list[Rack],rollup: object | None,sort.kind: "nomap"— Racks sin blueprint. Fields:racks: list[Rack],rollup: object | None,sort.
Orden de secciones: Determinado por el campo sort de cada sección, tupla (prioridad, índice_manual, aux, nombre):
- Con posición manual fijada (drag&drop previo) → esa posición exacta.
- Sin fijar: Rooms delante, blueprints en medio (alfabético si no tienen
sort_order), No Map al final.
int_pref(org_id: int, key: str) → int | None
Lee una preferencia de organización como entero, saltando el L1 (Valkey) si falla o corrupción.
Fuente de datos: OrgUIPreference.objects.filter(organization_id=org_id, user__isnull=True, key=key). Solo prefs de organización (no de usuario).
Retorna: Valor entero o None si no existe, no es parseable, u org_id es None.
section_sort_key(kind: str, sort_value: int | None, name: str = "") → tuple
Tupla de orden uniforme para los tres tipos de sección.
Parámetros:
kind— “rooms” | “bp” | “nomap”.sort_value— Índice manual (deBlueprint.sort_ordero de ui_pref) oNonesi no fijado.name— Nombre de la sección (usado como fallback alfabético).
Retorna: (prioridad, índice, secundaria, nombre_lower) para sort estable.
Constantes
ROOMS_SORT_KEY = "dashboard_rooms_sort"— Clave de ui_pref para la posición de Rooms.NOMAP_SORT_KEY = "dashboard_nomap_sort"— Clave de ui_pref para la posición de No Map.
Fuentes de datos
| Dato | Origen | Modelo |
|---|---|---|
| Posición manual de blueprints | Blueprint.sort_order | blueprints.models.Blueprint |
| Posición manual de Rooms | OrgUIPreference.value con key=dashboard_rooms_sort | core.models_prefs.OrgUIPreference |
| Posición manual de No Map | OrgUIPreference.value con key=dashboard_nomap_sort | core.models_prefs.OrgUIPreference |
| Racks agrupados | Parámetro racks_by_group (pre-filtrados y agrupados en la vista) | racks.models.Rack |
| Salas (Rooms) | Parámetro rooms (pre-filtradas por org) | blueprints.models.Room (DCIM) |
| Métricas agregadas | Parámetro group_rollups | Cálculo en racks/views.py → calculate_group_rollups() |
Consumidores
racks/views.py—index(request)— render del Dashboard completo con todas las secciones ordenadas.core/htmx_views.py—racks_list(request)— partial HTMX (búsqueda/filtrado en vivo). Nota: Este partial omite deliberadamente Rooms y no incluye roll-ups (comportamiento histórico).
Comportamiento por defecto (sin reorden previo)
Si una organización nunca ha arrastrado las secciones (no existen ui_prefs), el orden es:
- Rooms (si hay salas)
- Blueprints en orden manual (
sort_order), y los sin ordenar alfabéticamente - No Map (racks sin blueprint)
El servicio no crean ui_prefs automáticamente; el endpoint POST /api/blueprints/reorder es el que las escribe al arrastrar.
Detalles de implementación
build_sections() workflow
- Emite la sección Rooms (si
roomsproporcionado). - Itera
racks_by_group, saltando la clave""(No Map). - Para cada grupo (blueprint):
- Ordena internamente los racks por nombre.
- Obtiene el primer rack para extraer
blueprint_id,blueprint_sort,blueprint_mode. - Emite un dict de sección “bp”.
- Emite la sección No Map (con los racks de clave
""). - Ordena el listado por campo
sort(tupla de orden). - Retorna el resultado.
Caché en tests
Detalle cazado en tests: el L1 (Valkey) de ui_prefs sobrevive entre tests, y los ids de Organization se reutilizan al resetear la BD. Los asserts sobre prefs deben leer PostgreSQL directo (_pref_db helper) en lugar de get_ui_pref.
Véase también
- [[entity—blueprints—model—blueprint]]
- [[entity—core—service—ui-pref]]
- [[entity—blueprints—endpoint—reorder-secciones]]
- [[feature—dashboard—reorder-todas-secciones]]
- [[entity—racks—model—rack]]