- Comandos: search, list, tree, copy, info, add, grupos, stats - Conexión SSH a HST (72.62.2.84) - Estructura autoreferenciada (sin cat/subcat) - Grupos válidos: hst, spe, vsn, vue, flg 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
155 lines
4.5 KiB
Python
155 lines
4.5 KiB
Python
"""
|
|
Modelos de datos para TZZR
|
|
|
|
Representa la tabla hst con su estructura:
|
|
- id: integer (PK)
|
|
- ref: referencia corta
|
|
- h_maestro: referencia única completa
|
|
- grupo: hst, spe, hsu, msu, cat, subcat
|
|
- nombre_es, nombre_en: nombres
|
|
- padre_h_maestro: referencia al padre
|
|
- rootref, descripcion, imagen_url, mrf
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Optional, List
|
|
|
|
|
|
@dataclass
|
|
class Hashtag:
|
|
"""Representa un hashtag en la tabla hst"""
|
|
id: int
|
|
ref: str
|
|
h_maestro: str
|
|
grupo: str
|
|
nombre_es: Optional[str] = None
|
|
nombre_en: Optional[str] = None
|
|
padre_h_maestro: Optional[str] = None
|
|
rootref: Optional[str] = None
|
|
descripcion: Optional[str] = None
|
|
imagen_url: Optional[str] = None
|
|
mrf: Optional[str] = None
|
|
created_at: Optional[datetime] = None
|
|
children: List["Hashtag"] = field(default_factory=list)
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> "Hashtag":
|
|
"""Crea un Hashtag desde un diccionario"""
|
|
return cls(
|
|
id=data.get("id"),
|
|
ref=data.get("ref", ""),
|
|
h_maestro=data.get("h_maestro", ""),
|
|
grupo=data.get("grupo", ""),
|
|
nombre_es=data.get("nombre_es"),
|
|
nombre_en=data.get("nombre_en"),
|
|
padre_h_maestro=data.get("padre_h_maestro"),
|
|
rootref=data.get("rootref"),
|
|
descripcion=data.get("descripcion"),
|
|
imagen_url=data.get("imagen_url"),
|
|
mrf=data.get("mrf"),
|
|
created_at=data.get("created_at"),
|
|
)
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convierte a diccionario"""
|
|
return {
|
|
"id": self.id,
|
|
"ref": self.ref,
|
|
"h_maestro": self.h_maestro,
|
|
"grupo": self.grupo,
|
|
"nombre_es": self.nombre_es,
|
|
"nombre_en": self.nombre_en,
|
|
"padre_h_maestro": self.padre_h_maestro,
|
|
"rootref": self.rootref,
|
|
"descripcion": self.descripcion,
|
|
"imagen_url": self.imagen_url,
|
|
"mrf": self.mrf,
|
|
"created_at": self.created_at,
|
|
}
|
|
|
|
@property
|
|
def display_name(self) -> str:
|
|
"""Nombre para mostrar"""
|
|
name = self.nombre_es or self.ref
|
|
return f"{self.ref}: {name}"
|
|
|
|
def __str__(self) -> str:
|
|
return self.display_name
|
|
|
|
|
|
@dataclass
|
|
class TreeNode:
|
|
"""Nodo de árbol para visualización jerárquica"""
|
|
hashtag: Hashtag
|
|
depth: int = 0
|
|
is_last: bool = False
|
|
parent_is_last: List[bool] = field(default_factory=list)
|
|
|
|
@property
|
|
def prefix(self) -> str:
|
|
"""Genera el prefijo visual para el árbol"""
|
|
if self.depth == 0:
|
|
return ""
|
|
|
|
parts = []
|
|
for is_last in self.parent_is_last[:-1]:
|
|
parts.append(" " if is_last else "| ")
|
|
|
|
if self.parent_is_last:
|
|
parts.append("+-- " if self.is_last else "|-- ")
|
|
|
|
return "".join(parts)
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.prefix}{self.hashtag.display_name}"
|
|
|
|
|
|
def build_tree(hashtags: List[dict], root_h_maestro: str) -> List[TreeNode]:
|
|
"""
|
|
Construye una lista de TreeNodes para visualización
|
|
|
|
Args:
|
|
hashtags: Lista de diccionarios con datos de hashtags
|
|
root_h_maestro: h_maestro del nodo raíz
|
|
"""
|
|
# Convertir a Hashtags
|
|
items = [Hashtag.from_dict(h) for h in hashtags]
|
|
|
|
# Crear índice por h_maestro
|
|
by_maestro = {h.h_maestro: h for h in items}
|
|
|
|
# Encontrar hijos de cada nodo
|
|
children_map = {}
|
|
for h in items:
|
|
if h.padre_h_maestro:
|
|
if h.padre_h_maestro not in children_map:
|
|
children_map[h.padre_h_maestro] = []
|
|
children_map[h.padre_h_maestro].append(h)
|
|
|
|
# Construir lista de nodos
|
|
nodes = []
|
|
|
|
def add_node(hashtag: Hashtag, depth: int, parent_is_last: List[bool]):
|
|
children = children_map.get(hashtag.h_maestro, [])
|
|
children.sort(key=lambda x: x.nombre_es or x.ref)
|
|
|
|
for i, child in enumerate(children):
|
|
is_last = i == len(children) - 1
|
|
node = TreeNode(
|
|
hashtag=child,
|
|
depth=depth,
|
|
is_last=is_last,
|
|
parent_is_last=parent_is_last + [is_last]
|
|
)
|
|
nodes.append(node)
|
|
add_node(child, depth + 1, parent_is_last + [is_last])
|
|
|
|
# Agregar raíz
|
|
if root_h_maestro in by_maestro:
|
|
root = by_maestro[root_h_maestro]
|
|
nodes.append(TreeNode(hashtag=root, depth=0, is_last=True, parent_is_last=[]))
|
|
add_node(root, 1, [True])
|
|
|
|
return nodes
|