fix secrets no found
Browse files- SETUP_PERSISTENCE.md +72 -0
- app/main.py +37 -6
- migrate_data.py +105 -0
- setup_datasets.py +93 -0
SETUP_PERSISTENCE.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🔧 Configuración de Persistencia de Datos
|
| 2 |
+
|
| 3 |
+
Este directorio contiene scripts para configurar la persistencia de datos usando Hugging Face Datasets.
|
| 4 |
+
|
| 5 |
+
## 📋 Problema
|
| 6 |
+
|
| 7 |
+
Cuando haces un **factory rebuild** en Hugging Face Spaces, todos los datos se pierden porque el contenedor se recrea desde cero.
|
| 8 |
+
|
| 9 |
+
## ✅ Solución
|
| 10 |
+
|
| 11 |
+
Usar Hugging Face Datasets para almacenar los datos de forma permanente.
|
| 12 |
+
|
| 13 |
+
## 🚀 Pasos Rápidos
|
| 14 |
+
|
| 15 |
+
### 1️⃣ Crear los Datasets (Una sola vez)
|
| 16 |
+
|
| 17 |
+
Ejecuta desde tu máquina local:
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
python3 setup_datasets.py
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
Esto creará 4 datasets privados en tu cuenta de Hugging Face:
|
| 24 |
+
- `Edoruin/makerpage-users`
|
| 25 |
+
- `Edoruin/makerpage-classrooms`
|
| 26 |
+
- `Edoruin/makerpage-loans`
|
| 27 |
+
- `Edoruin/makerpage-faces`
|
| 28 |
+
|
| 29 |
+
### 2️⃣ Configurar Secretos en HF Spaces
|
| 30 |
+
|
| 31 |
+
Ve a tu Space → **Settings** → **Repository secrets** y agrega:
|
| 32 |
+
|
| 33 |
+
```
|
| 34 |
+
HF_TOKEN=hf_tu_token_aqui
|
| 35 |
+
HF_DATASET_USERS=Edoruin/makerpage-users
|
| 36 |
+
HF_DATASET_CLASSROOMS=Edoruin/makerpage-classrooms
|
| 37 |
+
HF_DATASET_LOANS=Edoruin/makerpage-loans
|
| 38 |
+
HF_DATASET_FACES=Edoruin/makerpage-faces
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
### 3️⃣ Migrar Datos Existentes (Opcional)
|
| 42 |
+
|
| 43 |
+
Si ya tienes datos que quieres preservar, ejecuta desde el Space:
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
python migrate_data.py
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### 4️⃣ Reiniciar
|
| 50 |
+
|
| 51 |
+
Haz **restart** (no factory rebuild) del Space.
|
| 52 |
+
|
| 53 |
+
## ✨ Verificación
|
| 54 |
+
|
| 55 |
+
Revisa los logs del Space. Deberías ver:
|
| 56 |
+
|
| 57 |
+
```
|
| 58 |
+
[UserManager] HF Datasets: ENABLED
|
| 59 |
+
[ClassroomManager] HF Datasets: ENABLED
|
| 60 |
+
[LoanManager] HF Datasets: ENABLED
|
| 61 |
+
[FaceManager] HF Datasets: ENABLED
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
## 🎉 ¡Listo!
|
| 65 |
+
|
| 66 |
+
Ahora puedes hacer factory rebuild sin perder datos. Todo se guarda automáticamente en Hugging Face Datasets.
|
| 67 |
+
|
| 68 |
+
## 📝 Notas
|
| 69 |
+
|
| 70 |
+
- Los datasets son **privados** por defecto
|
| 71 |
+
- Los datos se guardan en local (backup) y en HF (persistente)
|
| 72 |
+
- Necesitas un token de Hugging Face con permisos de escritura
|
app/main.py
CHANGED
|
@@ -269,13 +269,30 @@ class ClassroomManager(HFDatasetManager):
|
|
| 269 |
self.classrooms = self._load()
|
| 270 |
|
| 271 |
def _load(self):
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
def save(self):
|
| 276 |
-
|
| 277 |
-
|
| 278 |
self._save_to_local(self.classrooms)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
def get_courses(self):
|
| 281 |
return self.classrooms
|
|
@@ -426,16 +443,30 @@ class FaceManager(HFDatasetManager):
|
|
| 426 |
self.faces = self._load()
|
| 427 |
|
| 428 |
def _load(self):
|
|
|
|
| 429 |
if self.use_hf:
|
| 430 |
data = self._load_from_hf()
|
| 431 |
if data is not None:
|
|
|
|
| 432 |
return data
|
| 433 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 434 |
|
| 435 |
def save(self):
|
|
|
|
|
|
|
| 436 |
self._save_to_local(self.faces)
|
|
|
|
|
|
|
| 437 |
if self.use_hf:
|
| 438 |
-
self._save_to_hf(self.faces)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
|
| 440 |
def add_face(self, label, descriptor):
|
| 441 |
# El descriptor es una lista de 128 floats (face-api.js)
|
|
|
|
| 269 |
self.classrooms = self._load()
|
| 270 |
|
| 271 |
def _load(self):
|
| 272 |
+
"""Carga los cursos desde HF Dataset o archivo JSON local."""
|
| 273 |
+
if self.use_hf:
|
| 274 |
+
data = self._load_from_hf()
|
| 275 |
+
if data is not None:
|
| 276 |
+
print(f"[ClassroomManager] Loaded {len(data)} courses from HF Dataset")
|
| 277 |
+
return data
|
| 278 |
+
print("[ClassroomManager] Failed to load from HF, trying local...")
|
| 279 |
+
|
| 280 |
+
data = self._load_from_local()
|
| 281 |
+
print(f"[ClassroomManager] Loaded {len(data)} courses from local JSON")
|
| 282 |
+
return data
|
| 283 |
|
| 284 |
def save(self):
|
| 285 |
+
"""Guarda la lista de cursos en HF Dataset y/o archivo JSON."""
|
| 286 |
+
# Siempre guardar localmente como backup
|
| 287 |
self._save_to_local(self.classrooms)
|
| 288 |
+
|
| 289 |
+
# Intentar guardar en HF si está habilitado
|
| 290 |
+
if self.use_hf:
|
| 291 |
+
success = self._save_to_hf(self.classrooms)
|
| 292 |
+
if success:
|
| 293 |
+
print(f"[ClassroomManager] Saved {len(self.classrooms)} courses to HF Dataset")
|
| 294 |
+
else:
|
| 295 |
+
print("[ClassroomManager] Failed to save to HF, data saved locally only")
|
| 296 |
|
| 297 |
def get_courses(self):
|
| 298 |
return self.classrooms
|
|
|
|
| 443 |
self.faces = self._load()
|
| 444 |
|
| 445 |
def _load(self):
|
| 446 |
+
"""Carga los rostros desde HF Dataset o archivo JSON local."""
|
| 447 |
if self.use_hf:
|
| 448 |
data = self._load_from_hf()
|
| 449 |
if data is not None:
|
| 450 |
+
print(f"[FaceManager] Loaded {len(data)} faces from HF Dataset")
|
| 451 |
return data
|
| 452 |
+
print("[FaceManager] Failed to load from HF, trying local...")
|
| 453 |
+
|
| 454 |
+
data = self._load_from_local()
|
| 455 |
+
print(f"[FaceManager] Loaded {len(data)} faces from local JSON")
|
| 456 |
+
return data
|
| 457 |
|
| 458 |
def save(self):
|
| 459 |
+
"""Guarda la lista de rostros en HF Dataset y/o archivo JSON."""
|
| 460 |
+
# Siempre guardar localmente como backup
|
| 461 |
self._save_to_local(self.faces)
|
| 462 |
+
|
| 463 |
+
# Intentar guardar en HF si está habilitado
|
| 464 |
if self.use_hf:
|
| 465 |
+
success = self._save_to_hf(self.faces)
|
| 466 |
+
if success:
|
| 467 |
+
print(f"[FaceManager] Saved {len(self.faces)} faces to HF Dataset")
|
| 468 |
+
else:
|
| 469 |
+
print("[FaceManager] Failed to save to HF, data saved locally only")
|
| 470 |
|
| 471 |
def add_face(self, label, descriptor):
|
| 472 |
# El descriptor es una lista de 128 floats (face-api.js)
|
migrate_data.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Script para migrar datos locales existentes a Hugging Face Datasets.
|
| 4 |
+
Ejecutar DESPUÉS de crear los datasets vacíos con setup_datasets.py
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import json
|
| 9 |
+
from datasets import Dataset
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
+
|
| 12 |
+
load_dotenv()
|
| 13 |
+
|
| 14 |
+
# Configuración
|
| 15 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 16 |
+
USERNAME = "Edoruin"
|
| 17 |
+
|
| 18 |
+
# Mapeo de archivos locales a datasets
|
| 19 |
+
MIGRATIONS = [
|
| 20 |
+
{
|
| 21 |
+
"local_file": "/app/data/users.json",
|
| 22 |
+
"dataset_name": f"{USERNAME}/makerpage-users",
|
| 23 |
+
"data_key": "users",
|
| 24 |
+
"description": "Usuarios"
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"local_file": "/app/data/classrooms.json",
|
| 28 |
+
"dataset_name": f"{USERNAME}/makerpage-classrooms",
|
| 29 |
+
"data_key": "classrooms",
|
| 30 |
+
"description": "Aulas/Cursos"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"local_file": "/app/data/prestamos.json",
|
| 34 |
+
"dataset_name": f"{USERNAME}/makerpage-loans",
|
| 35 |
+
"data_key": "loans",
|
| 36 |
+
"description": "Préstamos"
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"local_file": "/app/data/faces.json",
|
| 40 |
+
"dataset_name": f"{USERNAME}/makerpage-faces",
|
| 41 |
+
"data_key": "faces",
|
| 42 |
+
"description": "Rostros"
|
| 43 |
+
}
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
def migrate_file(local_file, dataset_name, data_key, description):
|
| 47 |
+
"""Migra un archivo JSON local a un dataset de HF."""
|
| 48 |
+
|
| 49 |
+
# Verificar si el archivo existe
|
| 50 |
+
if not os.path.exists(local_file):
|
| 51 |
+
print(f"⚠️ {description}: Archivo no encontrado ({local_file}), usando datos vacíos")
|
| 52 |
+
data = []
|
| 53 |
+
else:
|
| 54 |
+
# Leer datos locales
|
| 55 |
+
try:
|
| 56 |
+
with open(local_file, 'r') as f:
|
| 57 |
+
data = json.load(f)
|
| 58 |
+
print(f"📂 {description}: {len(data)} registros encontrados")
|
| 59 |
+
except Exception as e:
|
| 60 |
+
print(f"❌ {description}: Error leyendo archivo - {e}")
|
| 61 |
+
return False
|
| 62 |
+
|
| 63 |
+
# Subir a Hugging Face
|
| 64 |
+
try:
|
| 65 |
+
dataset_dict = {data_key: [data]}
|
| 66 |
+
dataset = Dataset.from_dict(dataset_dict)
|
| 67 |
+
dataset.push_to_hub(
|
| 68 |
+
dataset_name,
|
| 69 |
+
token=HF_TOKEN,
|
| 70 |
+
private=True
|
| 71 |
+
)
|
| 72 |
+
print(f"✅ {description}: Migrado exitosamente a {dataset_name}\n")
|
| 73 |
+
return True
|
| 74 |
+
|
| 75 |
+
except Exception as e:
|
| 76 |
+
print(f"❌ {description}: Error subiendo a HF - {e}\n")
|
| 77 |
+
return False
|
| 78 |
+
|
| 79 |
+
def main():
|
| 80 |
+
if not HF_TOKEN:
|
| 81 |
+
print("❌ ERROR: No se encontró HF_TOKEN")
|
| 82 |
+
return
|
| 83 |
+
|
| 84 |
+
print("🔄 Iniciando migración de datos locales a Hugging Face Datasets\n")
|
| 85 |
+
print("=" * 60)
|
| 86 |
+
|
| 87 |
+
success_count = 0
|
| 88 |
+
for migration in MIGRATIONS:
|
| 89 |
+
if migrate_file(**migration):
|
| 90 |
+
success_count += 1
|
| 91 |
+
|
| 92 |
+
print("=" * 60)
|
| 93 |
+
print(f"\n✨ Migración completada: {success_count}/{len(MIGRATIONS)} datasets actualizados")
|
| 94 |
+
|
| 95 |
+
if success_count == len(MIGRATIONS):
|
| 96 |
+
print("\n🎉 ¡Todos los datos han sido migrados exitosamente!")
|
| 97 |
+
print("\n📋 Próximos pasos:")
|
| 98 |
+
print("1. Reinicia tu Hugging Face Space")
|
| 99 |
+
print("2. Verifica que los datos aparezcan correctamente")
|
| 100 |
+
print("3. Ahora puedes hacer factory rebuild sin perder datos")
|
| 101 |
+
else:
|
| 102 |
+
print("\n⚠️ Algunos datasets no se pudieron migrar. Revisa los errores arriba.")
|
| 103 |
+
|
| 104 |
+
if __name__ == "__main__":
|
| 105 |
+
main()
|
setup_datasets.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Script para inicializar los datasets de Hugging Face con datos vacíos.
|
| 4 |
+
Ejecutar SOLO UNA VEZ para crear los datasets iniciales.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
from datasets import Dataset
|
| 9 |
+
from huggingface_hub import HfApi
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
+
|
| 12 |
+
load_dotenv()
|
| 13 |
+
|
| 14 |
+
# Configuración
|
| 15 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 16 |
+
USERNAME = "Edoruin" # Tu usuario de Hugging Face
|
| 17 |
+
|
| 18 |
+
# Datasets a crear
|
| 19 |
+
DATASETS = {
|
| 20 |
+
"users": {
|
| 21 |
+
"name": f"{USERNAME}/makerpage-users",
|
| 22 |
+
"data_key": "users",
|
| 23 |
+
"initial_data": []
|
| 24 |
+
},
|
| 25 |
+
"classrooms": {
|
| 26 |
+
"name": f"{USERNAME}/makerpage-classrooms",
|
| 27 |
+
"data_key": "classrooms",
|
| 28 |
+
"initial_data": []
|
| 29 |
+
},
|
| 30 |
+
"loans": {
|
| 31 |
+
"name": f"{USERNAME}/makerpage-loans",
|
| 32 |
+
"data_key": "loans",
|
| 33 |
+
"initial_data": []
|
| 34 |
+
},
|
| 35 |
+
"faces": {
|
| 36 |
+
"name": f"{USERNAME}/makerpage-faces",
|
| 37 |
+
"data_key": "faces",
|
| 38 |
+
"initial_data": []
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
def create_dataset(dataset_name, data_key, initial_data):
|
| 43 |
+
"""Crea un dataset vacío en Hugging Face Hub."""
|
| 44 |
+
try:
|
| 45 |
+
# Crear dataset con estructura inicial
|
| 46 |
+
dataset_dict = {data_key: [initial_data]}
|
| 47 |
+
dataset = Dataset.from_dict(dataset_dict)
|
| 48 |
+
|
| 49 |
+
# Subir a Hugging Face Hub
|
| 50 |
+
dataset.push_to_hub(
|
| 51 |
+
dataset_name,
|
| 52 |
+
token=HF_TOKEN,
|
| 53 |
+
private=True # Dataset privado
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
print(f"✅ Dataset creado: {dataset_name}")
|
| 57 |
+
return True
|
| 58 |
+
|
| 59 |
+
except Exception as e:
|
| 60 |
+
print(f"❌ Error creando {dataset_name}: {e}")
|
| 61 |
+
return False
|
| 62 |
+
|
| 63 |
+
def main():
|
| 64 |
+
if not HF_TOKEN:
|
| 65 |
+
print("❌ ERROR: No se encontró HF_TOKEN en las variables de entorno")
|
| 66 |
+
print(" Asegúrate de tener un archivo .env con HF_TOKEN=tu_token")
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
print(f"🚀 Iniciando creación de datasets para usuario: {USERNAME}\n")
|
| 70 |
+
|
| 71 |
+
for key, config in DATASETS.items():
|
| 72 |
+
print(f"📦 Creando dataset: {config['name']}")
|
| 73 |
+
create_dataset(
|
| 74 |
+
config['name'],
|
| 75 |
+
config['data_key'],
|
| 76 |
+
config['initial_data']
|
| 77 |
+
)
|
| 78 |
+
print()
|
| 79 |
+
|
| 80 |
+
print("✨ Proceso completado!")
|
| 81 |
+
print("\n📋 Próximos pasos:")
|
| 82 |
+
print("1. Ve a Hugging Face Spaces → Settings → Repository secrets")
|
| 83 |
+
print("2. Verifica que estos secretos estén configurados:")
|
| 84 |
+
print(f" - HF_TOKEN = {HF_TOKEN[:10]}...")
|
| 85 |
+
print(f" - HF_DATASET_USERS = {DATASETS['users']['name']}")
|
| 86 |
+
print(f" - HF_DATASET_CLASSROOMS = {DATASETS['classrooms']['name']}")
|
| 87 |
+
print(f" - HF_DATASET_LOANS = {DATASETS['loans']['name']}")
|
| 88 |
+
print(f" - HF_DATASET_FACES = {DATASETS['faces']['name']}")
|
| 89 |
+
print("\n3. Reinicia tu Space (NO factory rebuild todavía)")
|
| 90 |
+
print("4. Verifica que los logs muestren 'HF Datasets: ENABLED'")
|
| 91 |
+
|
| 92 |
+
if __name__ == "__main__":
|
| 93 |
+
main()
|