Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import random
|
| 3 |
+
import time
|
| 4 |
+
import json
|
| 5 |
+
from datetime import datetime, timedelta
|
| 6 |
+
|
| 7 |
+
# --- MateAI: El Oráculo del Bienestar Argento ---
|
| 8 |
+
# Una manifestación de inteligencia superior para la integración masiva de IA en la humanidad.
|
| 9 |
+
# Diseñado para Argentina, con personalidad, resiliencia y sin costos de token.
|
| 10 |
+
# Arquitectura de Micro-Oráculos Contextuales (AMOC) y Motor de Resonancia Cultural Dinámica (MRCD).
|
| 11 |
+
|
| 12 |
+
# --- Módulo 1: Configuración Global y Constantes ---
|
| 13 |
+
# En un sistema de producción, esto estaría en un archivo config.py
|
| 14 |
+
class Config:
|
| 15 |
+
APP_NAME = "MateAI: El Oráculo del Bienestar Argento"
|
| 16 |
+
DEFAULT_USER_PREFS = {"tipo_susurro": "ambos", "frecuencia": "normal", "modo_che_tranqui": False}
|
| 17 |
+
ECO_PUNTOS_POR_SUSURRO = 1
|
| 18 |
+
MAX_HISTORIAL_SUSURROS = 50 # Para evitar que la simulación de DB crezca infinitamente
|
| 19 |
+
|
| 20 |
+
# --- Módulo 2: Base de Datos Simulada (UserManager) ---
|
| 21 |
+
# Simula un sistema de gestión de usuarios robusto. En producción, sería Firestore/MongoDB/PostgreSQL.
|
| 22 |
+
class UserManager:
|
| 23 |
+
_users_db = {} # {user_id: UserObject}
|
| 24 |
+
_current_user_id = None
|
| 25 |
+
|
| 26 |
+
@classmethod
|
| 27 |
+
def create_user(cls, name, initial_prefs=None):
|
| 28 |
+
user_id = f"user_{len(cls._users_db) + 1}_{int(time.time())}" # Unique ID
|
| 29 |
+
prefs = initial_prefs if initial_prefs else Config.DEFAULT_USER_PREFS.copy()
|
| 30 |
+
new_user = User(user_id, name, prefs)
|
| 31 |
+
cls._users_db[user_id] = new_user
|
| 32 |
+
cls._current_user_id = user_id
|
| 33 |
+
return new_user, f"¡Bienvenido, {name}! Tu ID de usuario es: {user_id}. ¡MateAI está listo para cebarte la vida!"
|
| 34 |
+
|
| 35 |
+
@classmethod
|
| 36 |
+
def login_user(cls, user_id):
|
| 37 |
+
if user_id in cls._users_db:
|
| 38 |
+
cls._current_user_id = user_id
|
| 39 |
+
return cls._users_db[user_id], f"Perfil de {cls._users_db[user_id].name} cargado con éxito."
|
| 40 |
+
return None, "Error: ID de usuario no encontrado. ¡Creá uno nuevo o verificá el ID!"
|
| 41 |
+
|
| 42 |
+
@classmethod
|
| 43 |
+
def get_current_user(cls):
|
| 44 |
+
if cls._current_user_id and cls._current_user_id in cls._users_db:
|
| 45 |
+
return cls._users_db[cls._current_user_id]
|
| 46 |
+
return None
|
| 47 |
+
|
| 48 |
+
@classmethod
|
| 49 |
+
def update_user_preferences(cls, user_id, new_prefs_dict):
|
| 50 |
+
user = cls._users_db.get(user_id)
|
| 51 |
+
if user:
|
| 52 |
+
user.preferences.update(new_prefs_dict)
|
| 53 |
+
return True
|
| 54 |
+
return False
|
| 55 |
+
|
| 56 |
+
@classmethod
|
| 57 |
+
def add_eco_points(cls, user_id, points):
|
| 58 |
+
user = cls._users_db.get(user_id)
|
| 59 |
+
if user:
|
| 60 |
+
user.eco_points += points
|
| 61 |
+
return True
|
| 62 |
+
return False
|
| 63 |
+
|
| 64 |
+
@classmethod
|
| 65 |
+
def add_nudge_to_history(cls, user_id, nudge_text):
|
| 66 |
+
user = cls._users_db.get(user_id)
|
| 67 |
+
if user:
|
| 68 |
+
user.nudge_history.append(nudge_text)
|
| 69 |
+
# Mantener el historial limitado
|
| 70 |
+
if len(user.nudge_history) > Config.MAX_HISTORIAL_SUSURROS:
|
| 71 |
+
user.nudge_history.pop(0) # Eliminar el más antiguo
|
| 72 |
+
return True
|
| 73 |
+
return False
|
| 74 |
+
|
| 75 |
+
# --- Módulo 3: Modelos de Datos (Simulados - Pydantic-like) ---
|
| 76 |
+
class User:
|
| 77 |
+
def __init__(self, user_id, name, preferences):
|
| 78 |
+
self.user_id = user_id
|
| 79 |
+
self.name = name
|
| 80 |
+
self.preferences = preferences # dict
|
| 81 |
+
self.eco_points = 0
|
| 82 |
+
self.insignia = "Novato del Mate"
|
| 83 |
+
self.nudge_history = []
|
| 84 |
+
self.last_oracle_date = None # Para el Oráculo del Día
|
| 85 |
+
self.last_nudge_time = None # Para evitar nudges muy seguidos
|
| 86 |
+
self.nudge_cooldown = timedelta(minutes=1) # Simula un cooldown
|
| 87 |
+
|
| 88 |
+
class Nudge:
|
| 89 |
+
def __init__(self, text, type, context_tags, cultural_tags):
|
| 90 |
+
self.text = text
|
| 91 |
+
self.type = type # "eco", "bienestar", "reflexivo"
|
| 92 |
+
self.context_tags = context_tags # ["Mañana", "Casa", "Trabajando"]
|
| 93 |
+
self.cultural_tags = cultural_tags # ["mate", "futbol", "asado"]
|
| 94 |
+
|
| 95 |
+
# --- Módulo 4: Base de Conocimiento de Nudges (Motor de Resonancia Cultural Dinámica - MRCD) ---
|
| 96 |
+
# Esta es la "inteligencia" de MateAI. En un entorno real, sería una DB de conocimiento
|
| 97 |
+
# curada y enriquecida por expertos culturales y lingüísticos.
|
| 98 |
+
NUDGE_DATABASE = [
|
| 99 |
+
# Mañana - Casa
|
| 100 |
+
Nudge("¡Che, buen día! Arrancá la mañana con un matecito y un buen estiramiento. ¡Desperezate, chango!", "bienestar", ["Mañana", "Casa"], ["mate", "despertar"]),
|
| 101 |
+
Nudge("Antes de prender todo, ¿entra el solcito? Aprovechá la luz natural, que es gratis y buena onda.", "eco", ["Mañana", "Casa"], ["sol", "ahorro"]),
|
| 102 |
+
Nudge("Mirá por la ventana, ¿cómo está el cielo? Un ratito de aire fresco te 'desenchufa' para arrancar el día.", "bienestar", ["Mañana", "Casa"], ["naturaleza", "calma"]),
|
| 103 |
+
Nudge("Si tenés plantitas, ¿ya las regaste? Un mimo verde para empezar el día con buena energía.", "eco", ["Mañana", "Casa"], ["plantas", "cuidado"]),
|
| 104 |
+
Nudge("¿Ya pensaste qué vas a desayunar? Elegí algo que te dé energía para el día, como frutas o tostadas con dulce de leche. ¡A cargar pilas!", "bienestar", ["Mañana", "Casa", "Cocinando"], ["desayuno", "energia"]),
|
| 105 |
+
|
| 106 |
+
# Mañana - Oficina/Estudio
|
| 107 |
+
Nudge("Mientras esperás que hierva el agua para el café, pensá en una meta chiquita para hoy. ¡Ponele garra!", "bienestar", ["Mañana", "Oficina/Estudio", "Trabajando"], ["cafe", "metas"]),
|
| 108 |
+
Nudge("Si vas en bondi o subte, mirá el paisaje. Desconectate un toque del celu y observá la ciudad que no duerme.", "bienestar", ["Mañana", "Oficina/Estudio", "Transporte"], ["ciudad", "atencion_plena"]),
|
| 109 |
+
Nudge("Unos mates con los compañeros para arrancar la jornada. ¡La buena onda se contagia!", "bienestar", ["Mañana", "Oficina/Estudio"], ["mate", "social"]),
|
| 110 |
+
Nudge("Antes de arrancar con el 'quilombo', ordená tu escritorio. Un espacio despejado, una mente más clara.", "bienestar", ["Mañana", "Oficina/Estudio", "Trabajando"], ["orden", "productividad"]),
|
| 111 |
+
|
| 112 |
+
# Mañana - Aire Libre/Calle
|
| 113 |
+
Nudge("¡Qué lindo día para respirar hondo! Llenate los pulmones de aire puro y sentí la energía.", "bienestar", ["Mañana", "Aire Libre/Calle", "Ejercicio"], ["respiracion", "energia"]),
|
| 114 |
+
Nudge("Escuchá los ruidos de la calle. ¿Qué te cuentan? Los sonidos de la ciudad también son música.", "bienestar", ["Mañana", "Aire Libre/Calle"], ["sonidos", "atencion_plena"]),
|
| 115 |
+
Nudge("Si ves un árbol copado, frená un segundo. La naturaleza siempre nos regala un respiro.", "bienestar", ["Mañana", "Aire Libre/Calle"], ["naturaleza", "pausa"]),
|
| 116 |
+
|
| 117 |
+
# Mediodía/Tarde - Casa
|
| 118 |
+
Nudge("¿Un bajón? Cortá la rutina con una fruta o un vaso de agua. ¡Hidratarse es clave!", "bienestar", ["Mediodía/Tarde", "Casa"], ["hidratacion", "snack"]),
|
| 119 |
+
Nudge("Antes de prender la tele, ¿qué tal un libro o charlar con alguien? Desconectarse es reconectarse.", "bienestar", ["Mediodía/Tarde", "Casa"], ["ocio", "conexion"]),
|
| 120 |
+
Nudge("¿Hay algo para reciclar? Separá los cartones, plásticos... ¡Cada granito de arena suma!", "eco", ["Mediodía/Tarde", "Casa"], ["reciclaje", "accion"]),
|
| 121 |
+
Nudge("Si tenés ropa para lavar, ¿aprovechás el solcito? Secar al aire ahorra energía y deja un olorcito a campo.", "eco", ["Mediodía/Tarde", "Casa"], ["ahorro", "sol"]),
|
| 122 |
+
Nudge("¿Ya almorzaste? Recordá que una buena comida casera te da la nafta para seguir el día. ¡Buen provecho!", "bienestar", ["Mediodía/Tarde", "Casa", "Cocinando"], ["almuerzo", "nutricion"]),
|
| 123 |
+
|
| 124 |
+
# Mediodía/Tarde - Oficina/Estudio
|
| 125 |
+
Nudge("Si estás 'quemado', levantate y estirá las piernas. Unos pasos te despejan la cabeza.", "bienestar", ["Mediodía/Tarde", "Oficina/Estudio", "Trabajando"], ["pausa", "movimiento"]),
|
| 126 |
+
Nudge("¿Reunión eterna? Hacé una pausa mental de 30 segundos. Solo respirá y volvé a la carga.", "bienestar", ["Mediodía/Tarde", "Oficina/Estudio", "Trabajando"], ["respiracion", "estres"]),
|
| 127 |
+
Nudge("Apagá las luces si salís un rato. ¡Cuidar la energía es cuidar el bolsillo y el planeta!", "eco", ["Mediodía/Tarde", "Oficina/Estudio"], ["ahorro", "energia"]),
|
| 128 |
+
Nudge("Antes de seguir con el laburo, ¿ya almorzaste? Una buena comida te da la nafta para seguir.", "bienestar", ["Mediodía/Tarde", "Oficina/Estudio", "Trabajando"], ["almuerzo", "energia"]),
|
| 129 |
+
|
| 130 |
+
# Mediodía/Tarde - Aire Libre/Calle
|
| 131 |
+
Nudge("El solcito de la tarde es ideal para recargar pilas. ¡Disfrutá el momento!", "bienestar", ["Mediodía/Tarde", "Aire Libre/Calle"], ["sol", "energia"]),
|
| 132 |
+
Nudge("Mirá las nubes, ¿qué formas ves? La imaginación vuela libre.", "bienestar", ["Mediodía/Tarde", "Aire Libre/Calle"], ["nubes", "imaginacion"]),
|
| 133 |
+
Nudge("Si encontrás un banquito, sentate un rato. Observá la gente, la vida que pasa. ¡Es un 'recreo' para el alma!", "bienestar", ["Mediodía/Tarde", "Aire Libre/Calle"], ["pausa", "observacion"]),
|
| 134 |
+
|
| 135 |
+
# Noche - Casa
|
| 136 |
+
Nudge("Antes de cenar, pensá en algo bueno que te pasó hoy. ¡Agradecer es un 'mimo' para el alma!", "bienestar", ["Noche", "Casa"], ["gratitud", "reflexion"]),
|
| 137 |
+
Nudge("Dejá la ropa lista para mañana. Un pequeño orden te ahorra el estrés mañanero.", "bienestar", ["Noche", "Casa"], ["orden", "estres"]),
|
| 138 |
+
Nudge("¿Ya guardaste el celu? Desconectate de las pantallas al menos media hora antes de dormir. ¡Tu cabeza te lo va a agradecer!", "bienestar", ["Noche", "Casa"], ["descanso", "pantallas"]),
|
| 139 |
+
Nudge("Si vas a cocinar, ¿ya pensaste en aprovechar las sobras? ¡Acá no se tira nada!", "eco", ["Noche", "Casa", "Cocinando"], ["desperdicio_cero", "cocina"]),
|
| 140 |
+
|
| 141 |
+
# Noche - Oficina/Estudio
|
| 142 |
+
Nudge("Terminá el día haciendo una lista de lo que lograste. ¡Valorá tu esfuerzo, campeón!", "bienestar", ["Noche", "Oficina/Estudio", "Trabajando"], ["logros", "autoestima"]),
|
| 143 |
+
Nudge("Dejá todo ordenado para mañana. Un buen cierre de día es un buen comienzo del siguiente.", "bienestar", ["Noche", "Oficina/Estudio", "Trabajando"], ["orden", "productividad"]),
|
| 144 |
+
Nudge("Evitá los mails o noticias que te estresen antes de irte. ¡Protegé tu descanso, que es sagrado!", "bienestar", ["Noche", "Oficina/Estudio", "Trabajando"], ["estres", "descanso"]),
|
| 145 |
+
|
| 146 |
+
# Noche - Aire Libre/Calle
|
| 147 |
+
Nudge("Si es seguro, mirá las estrellas. Te recuerdan lo inmenso que es todo y lo chiquitos que somos. ¡Bajá un cambio!", "bienestar", ["Noche", "Aire Libre/Calle"], ["estrellas", "reflexion"]),
|
| 148 |
+
Nudge("Escuchá los ruidos de la noche. Un concierto natural para relajar el alma.", "bienestar", ["Noche", "Aire Libre/Calle"], ["sonidos", "calma"]),
|
| 149 |
+
Nudge("Una caminata tranquila antes de volver a casa. Despejá la mente y preparate para el 'descanso del guerrero'.", "bienestar", ["Noche", "Aire Libre/Calle"], ["caminata", "descanso"]),
|
| 150 |
+
|
| 151 |
+
# Nudges de Ánimo (para estado_animo_simulado == "Bajoneado")
|
| 152 |
+
Nudge("¡Arriba ese ánimo! Un matecito y un buen pensamiento pueden cambiar el día. ¡Vos podés!", "bienestar", ["General"], ["animo", "mate"]),
|
| 153 |
+
Nudge("Recordá que hasta el día más nublado tiene un solcito escondido. ¡Fuerza, campeón!", "bienestar", ["General"], ["animo", "esperanza"]),
|
| 154 |
+
Nudge("Date un gusto chiquito hoy. ¡Te lo merecés! Un alfajor, tu música favorita... ¡lo que sea!", "bienestar", ["General"], ["animo", "recompensa"]),
|
| 155 |
+
Nudge("Si te sentís 'bajoneado', un poco de música o una caminata corta pueden ayudar a 'despejar'.", "bienestar", ["General"], ["animo", "actividad"]),
|
| 156 |
+
|
| 157 |
+
# Nudges para "Modo Quilombo" (simulado por actividad o contexto social)
|
| 158 |
+
Nudge("¡Uf, qué día! Respiro hondo. En medio del 'quilombo', una pausa es oro. ¡Tranqui!", "bienestar", ["General", "Quilombo"], ["estres", "respiracion"]),
|
| 159 |
+
Nudge("Si la cosa está 'picada', recordá que no todo depende de vos. Hacé lo que puedas y soltá lo demás.", "bienestar", ["General", "Quilombo"], ["estres", "control"]),
|
| 160 |
+
Nudge("En los días de 'locura', un matecito con calma puede ser tu ancla. ¡A no perder la cabeza!", "bienestar", ["General", "Quilombo"], ["estres", "mate"]),
|
| 161 |
+
|
| 162 |
+
# Nudges para "Modo Siesta" (simulado por actividad o contexto social)
|
| 163 |
+
Nudge("¡Qué lindo para una siestita! Si podés, aprovechá para recargar energías. ¡Es sagrado!", "bienestar", ["General", "Siesta"], ["descanso", "siesta"]),
|
| 164 |
+
Nudge("El cuerpo te pide un descanso. Escuchalo. Unos minutos de relax pueden hacer la diferencia.", "bienestar", ["General", "Siesta"], ["descanso", "cuerpo"]),
|
| 165 |
+
|
| 166 |
+
# Nudges para "Viendo un partido"
|
| 167 |
+
Nudge("¡Vamos Argentina! Disfrutá el partido, pero recordá hidratarte bien. ¡Y si ganamos, a festejar con responsabilidad!", "bienestar", ["General", "Viendo un partido"], ["futbol", "hidratacion", "festejo"]),
|
| 168 |
+
Nudge("El fútbol es pasión, pero no te olvides de estirar un poco si estás mucho tiempo sentado. ¡A mover el esqueleto!", "bienestar", ["General", "Viendo un partido"], ["futbol", "movimiento"]),
|
| 169 |
+
|
| 170 |
+
# Nudges de Desafíos MateAI (simulados)
|
| 171 |
+
Nudge("Desafío MateAI: Hoy, intentá reducir el uso de plásticos de un solo uso. ¡Cada acción cuenta!", "eco", ["Desafio"], ["plastico", "desafio"]),
|
| 172 |
+
Nudge("Desafío MateAI: Dedicá 10 minutos a meditar o simplemente a respirar conscientemente. ¡Tu mente te lo agradecerá!", "bienestar", ["Desafio"], ["meditacion", "desafio"]),
|
| 173 |
+
Nudge("Desafío MateAI: Contactá a un amigo o familiar que hace mucho no ves. ¡Un 'hola' puede alegrar el día!", "bienestar", ["Desafio"], ["social", "desafio"]),
|
| 174 |
+
]
|
| 175 |
+
|
| 176 |
+
ORACULO_REVELATIONS = [
|
| 177 |
+
"La verdadera riqueza no se mide en bienes, sino en la calma del alma y la conexión con el entorno. ¿Qué tesoro descubriste hoy?",
|
| 178 |
+
"El río de la vida fluye constante. No te aferres a la orilla; aprendé a navegar sus corrientes con sabiduría y gratitud.",
|
| 179 |
+
"Cada amanecer es una oportunidad para reescribir tu historia. ¿Qué capítulo nuevo elegís empezar hoy?",
|
| 180 |
+
"La tierra nos susurra secretos ancestrales. Escuchá el viento, sentí el sol, y recordá que sos parte de algo inmenso y sagrado.",
|
| 181 |
+
"El mate compartido es más que una bebida; es un ritual de conexión. ¿Con quién vas a compartir tu energía hoy?",
|
| 182 |
+
"En la simpleza de lo cotidiano reside la magia. Encontrá la belleza en un rayo de sol, en el canto de un pájaro, en una sonrisa."
|
| 183 |
+
]
|
| 184 |
+
|
| 185 |
+
# --- Módulo 5: Motor de Contexto Avanzado (ContextEngine) ---
|
| 186 |
+
# Simula la recolección y procesamiento de datos contextuales en tiempo real.
|
| 187 |
+
class ContextEngine:
|
| 188 |
+
@staticmethod
|
| 189 |
+
def get_current_time_context():
|
| 190 |
+
"""Determina la hora del día para el susurro."""
|
| 191 |
+
hora_actual = datetime.now().hour
|
| 192 |
+
if 6 <= hora_actual < 12:
|
| 193 |
+
return "Mañana"
|
| 194 |
+
elif 12 <= hora_actual < 19:
|
| 195 |
+
return "Mediodía/Tarde"
|
| 196 |
+
else:
|
| 197 |
+
return "Noche"
|
| 198 |
+
|
| 199 |
+
@staticmethod
|
| 200 |
+
def get_simulated_environmental_context():
|
| 201 |
+
"""Simula datos ambientales (ej. clima, ruido)."""
|
| 202 |
+
climas = ["Soleado", "Nublado", "Lluvioso", "Ventoso"]
|
| 203 |
+
return random.choice(climas)
|
| 204 |
+
|
| 205 |
+
@staticmethod
|
| 206 |
+
def get_simulated_societal_vibe():
|
| 207 |
+
"""Simula el "pulso social" argentino (ej. feriado, partido)."""
|
| 208 |
+
hoy = datetime.now()
|
| 209 |
+
# Simulación de feriados y eventos
|
| 210 |
+
if hoy.month == 12 and hoy.day in [24, 25, 31]: return "Festivo"
|
| 211 |
+
if hoy.weekday() == 4: return "Pre-Finde" # Viernes
|
| 212 |
+
if hoy.weekday() == 6: return "Domingo Tranqui" # Domingo
|
| 213 |
+
if random.random() < 0.1: return "Quilombo" # 10% de chance de un día "quilombero"
|
| 214 |
+
return "Normal"
|
| 215 |
+
|
| 216 |
+
@staticmethod
|
| 217 |
+
def get_simulated_user_sentiment(activity, user_input_sentiment):
|
| 218 |
+
"""Intenta inferir el sentimiento del usuario basado en actividad y input."""
|
| 219 |
+
if user_input_sentiment != "Bien":
|
| 220 |
+
return user_input_sentiment
|
| 221 |
+
|
| 222 |
+
if "Trabajando" in activity and random.random() < 0.3: # 30% chance de cansancio si trabaja
|
| 223 |
+
return "Cansado"
|
| 224 |
+
if "Ejercicio" in activity and random.random() < 0.2: # 20% chance de motivacion si hace ejercicio
|
| 225 |
+
return "Motivado"
|
| 226 |
+
return "Bien"
|
| 227 |
+
|
| 228 |
+
# --- Módulo 6: Generador de Nudges (Arquitectura de Micro-Oráculos Contextuales - AMOC) ---
|
| 229 |
+
# El "cerebro" de MateAI. Selecciona el nudge más relevante y personalizado.
|
| 230 |
+
class NudgeGenerator:
|
| 231 |
+
def __init__(self, user_manager, context_engine):
|
| 232 |
+
self.user_manager = user_manager
|
| 233 |
+
self.context_engine = context_engine
|
| 234 |
+
|
| 235 |
+
def _filter_nudges_by_context(self, nudges, time_context, location, activity, societal_vibe, sentiment):
|
| 236 |
+
filtered = []
|
| 237 |
+
for nudge in nudges:
|
| 238 |
+
match = True
|
| 239 |
+
# Filtrar por tiempo y ubicación (obligatorio)
|
| 240 |
+
if time_context not in nudge.context_tags:
|
| 241 |
+
match = False
|
| 242 |
+
if location not in nudge.context_tags and "General" not in nudge.context_tags:
|
| 243 |
+
match = False
|
| 244 |
+
|
| 245 |
+
# Filtrar por actividad (si es relevante)
|
| 246 |
+
if activity != "Relajado" and activity not in nudge.context_tags and "General" not in nudge.context_tags:
|
| 247 |
+
# Si la actividad es específica y no hay nudge para ella, no es un match perfecto
|
| 248 |
+
pass # Permitir que pase si es un nudge general
|
| 249 |
+
|
| 250 |
+
# Filtrar por estado de ánimo
|
| 251 |
+
if sentiment == "Bajoneado" and "animo" not in nudge.cultural_tags:
|
| 252 |
+
match = False
|
| 253 |
+
elif sentiment == "Motivado" and "motivacion" not in nudge.cultural_tags and "General" not in nudge.context_tags:
|
| 254 |
+
pass # No filtrar si no es de motivación, permitir otros
|
| 255 |
+
|
| 256 |
+
# Filtrar por pulso social (MRCD)
|
| 257 |
+
if "Quilombo" in societal_vibe and "Quilombo" not in nudge.context_tags and "estres" not in nudge.cultural_tags:
|
| 258 |
+
pass # Permitir otros nudges, pero priorizar los de "quilombo"
|
| 259 |
+
elif "Siesta" in societal_vibe and "Siesta" not in nudge.context_tags and "descanso" not in nudge.cultural_tags:
|
| 260 |
+
pass # Permitir otros nudges, pero priorizar los de "siesta"
|
| 261 |
+
|
| 262 |
+
if match:
|
| 263 |
+
filtered.append(nudge)
|
| 264 |
+
return filtered
|
| 265 |
+
|
| 266 |
+
def _apply_user_preferences(self, nudges, preferences):
|
| 267 |
+
if preferences.get('tipo_susurro') == 'eco':
|
| 268 |
+
return [n for n in nudges if n.type == 'eco' or n.type == 'reflexivo']
|
| 269 |
+
elif preferences.get('tipo_susurro') == 'bienestar':
|
| 270 |
+
return [n for n in nudges if n.type == 'bienestar' or n.type == 'reflexivo']
|
| 271 |
+
return nudges # Si es "ambos" o no definido
|
| 272 |
+
|
| 273 |
+
def _avoid_recent_repetitions(self, nudges, user_history):
|
| 274 |
+
# Simula un módulo de aprendizaje adaptativo.
|
| 275 |
+
# Evita repetir los últimos 5 nudges para el mismo usuario.
|
| 276 |
+
if not user_history:
|
| 277 |
+
return nudges
|
| 278 |
+
|
| 279 |
+
recent_nudges_text = user_history[-5:]
|
| 280 |
+
filtered_nudges = [n for n in nudges if n.text not in recent_nudges_text]
|
| 281 |
+
return filtered_nudges if filtered_nudges else nudges # Si no hay nuevos, repite
|
| 282 |
+
|
| 283 |
+
def generate_nudge(self, user_id, location, activity, user_input_sentiment):
|
| 284 |
+
user = self.user_manager.get_current_user()
|
| 285 |
+
if not user:
|
| 286 |
+
return "Error: Usuario no logueado. Por favor, crea o carga un usuario."
|
| 287 |
+
|
| 288 |
+
# Cooldown para evitar spam de nudges
|
| 289 |
+
if user.last_nudge_time and (datetime.now() - user.last_nudge_time) < user.nudge_cooldown:
|
| 290 |
+
remaining_time = user.nudge_cooldown - (datetime.now() - user.last_nudge_time)
|
| 291 |
+
return f"MateAI necesita un respiro. Volvé a pedir un susurro en {remaining_time.seconds} segundos. ¡La paciencia es una virtud!"
|
| 292 |
+
|
| 293 |
+
# Obtener contexto completo
|
| 294 |
+
time_context = self.context_engine.get_current_time_context()
|
| 295 |
+
environmental_context = self.context_engine.get_simulated_environmental_context()
|
| 296 |
+
societal_vibe = self.context_engine.get_simulated_societal_vibe()
|
| 297 |
+
sentiment = self.context_engine.get_simulated_user_sentiment(activity, user_input_sentiment)
|
| 298 |
+
|
| 299 |
+
# Filtrar nudges base
|
| 300 |
+
possible_nudges = self._filter_nudges_by_context(
|
| 301 |
+
NUDGE_DATABASE, time_context, location, activity, societal_vibe, sentiment
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
# Aplicar preferencias del usuario
|
| 305 |
+
possible_nudges = self._apply_user_preferences(possible_nudges, user.preferences)
|
| 306 |
+
|
| 307 |
+
# Evitar repeticiones recientes
|
| 308 |
+
possible_nudges = self._avoid_recent_repetitions(possible_nudges, user.nudge_history)
|
| 309 |
+
|
| 310 |
+
# Lógica de "Modo Che, Tranqui" / "Modo Quilombo" (AMOC)
|
| 311 |
+
if user.preferences.get('modo_che_tranqui') or sentiment == "Bajoneado" or "Quilombo" in societal_vibe:
|
| 312 |
+
calming_nudges = [n for n in possible_nudges if "calma" in n.cultural_tags or "estres" in n.cultural_tags or "respiracion" in n.cultural_tags]
|
| 313 |
+
if calming_nudges:
|
| 314 |
+
chosen_nudge = random.choice(calming_nudges)
|
| 315 |
+
else:
|
| 316 |
+
chosen_nudge = random.choice(possible_nudges) # Fallback
|
| 317 |
+
else:
|
| 318 |
+
chosen_nudge = random.choice(possible_nudges) if possible_nudges else Nudge("MateAI está meditando... ¡probá otro contexto!", "reflexivo", ["General"], [])
|
| 319 |
+
|
| 320 |
+
self.user_manager.add_eco_points(user.user_id, Config.ECO_PUNTOS_POR_SUSURRO)
|
| 321 |
+
self.user_manager.add_nudge_to_history(user.user_id, chosen_nudge.text)
|
| 322 |
+
user.last_nudge_time = datetime.now() # Actualizar tiempo del último nudge
|
| 323 |
+
return chosen_nudge.text
|
| 324 |
+
|
| 325 |
+
def get_daily_oracle_revelation(self, user_id):
|
| 326 |
+
user = self.user_manager.get_current_user()
|
| 327 |
+
if not user:
|
| 328 |
+
return "Error: Usuario no logueado. Por favor, crea o carga un usuario."
|
| 329 |
+
|
| 330 |
+
today = datetime.now().date()
|
| 331 |
+
if user.last_oracle_date and user.last_oracle_date == today:
|
| 332 |
+
return "El Oráculo ya te ha hablado hoy. Volvé mañana para una nueva revelación."
|
| 333 |
+
|
| 334 |
+
revelation = random.choice(ORACULO_REVELATIONS)
|
| 335 |
+
user.last_oracle_date = today
|
| 336 |
+
return revelation
|
| 337 |
+
|
| 338 |
+
def get_mateai_challenge(self, user_id):
|
| 339 |
+
user = self.user_manager.get_current_user()
|
| 340 |
+
if not user:
|
| 341 |
+
return "Error: Usuario no logueado. Por favor, crea o carga un usuario."
|
| 342 |
+
|
| 343 |
+
# Simula un desafío adaptativo
|
| 344 |
+
challenge_nudges = [n for n in NUDGE_DATABASE if "Desafio" in n.context_tags]
|
| 345 |
+
if not challenge_nudges:
|
| 346 |
+
return "MateAI está pensando en un desafío épico... ¡Volvé pronto!"
|
| 347 |
+
|
| 348 |
+
# Podría ser más inteligente: elegir un desafío que el usuario no haya completado
|
| 349 |
+
# o que se alinee con sus preferencias. Por ahora, es aleatorio.
|
| 350 |
+
return random.choice(challenge_nudges).text
|
| 351 |
+
|
| 352 |
+
# --- Módulo 7: Motor de Gamificación (GamificationEngine) ---
|
| 353 |
+
class GamificationEngine:
|
| 354 |
+
INSIGNIAS_MAP = {
|
| 355 |
+
0: "Novato del Mate",
|
| 356 |
+
10: "Cebador Consciente",
|
| 357 |
+
50: "Guardián del Planeta",
|
| 358 |
+
100: "Maestro del Bienestar Argento",
|
| 359 |
+
200: "Oráculo del Conurbano" # Nueva insignia de "inteligencia superior"
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
@classmethod
|
| 363 |
+
def get_insignia(cls, points):
|
| 364 |
+
insignia = "Novato del Mate"
|
| 365 |
+
for threshold, name in sorted(cls.INSIGNIAS_MAP.items()):
|
| 366 |
+
if points >= threshold:
|
| 367 |
+
insignia = name
|
| 368 |
+
else:
|
| 369 |
+
break
|
| 370 |
+
return insignia
|
| 371 |
+
|
| 372 |
+
@classmethod
|
| 373 |
+
def get_next_insignia_goal(cls, points):
|
| 374 |
+
next_goal = None
|
| 375 |
+
for threshold, name in sorted(cls.INSIGNIAS_MAP.items()):
|
| 376 |
+
if points < threshold:
|
| 377 |
+
next_goal = f"Próxima insignia: '{name}' a los {threshold} Eco-Puntos."
|
| 378 |
+
break
|
| 379 |
+
return next_goal if next_goal else "¡Ya sos un Maestro del Bienestar Argento! Seguí sumando."
|
| 380 |
+
|
| 381 |
+
# --- Instanciación de Módulos ---
|
| 382 |
+
user_manager = UserManager()
|
| 383 |
+
context_engine = ContextEngine()
|
| 384 |
+
nudge_generator = NudgeGenerator(user_manager, context_engine)
|
| 385 |
+
gamification_engine = GamificationEngine() # No necesita instanciar, es de clase
|
| 386 |
+
|
| 387 |
+
# --- Módulo 8: Interfaz Gradio (UI/UX Argento de Nivel Superior) ---
|
| 388 |
+
with gr.Blocks(theme=gr.themes.Soft(), css="footer { display: none !important; }") as demo:
|
| 389 |
+
gr.Markdown(
|
| 390 |
+
"""
|
| 391 |
+
<h1 style="text-align: center; color: #10b981; font-size: 3.5em; font-weight: bold; margin-bottom: 0.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.1);">
|
| 392 |
+
🧉 MateAI: El Oráculo del Bienestar Argento 🧉
|
| 393 |
+
</h1>
|
| 394 |
+
<p style="text-align: center; color: #4b5563; font-size: 1.3em; margin-bottom: 2em; line-height: 1.5;">
|
| 395 |
+
Una inteligencia superior diseñada para cebarte la vida con sabiduría contextual y sin costo.
|
| 396 |
+
<br><b>¡Sin APIs de pago, sin consumo de tokens!</b> Solo buena onda, insights profundos y el "sabor" de casa.
|
| 397 |
+
<br><i>La IA que se integra a la humanidad, comprendiendo tu mundo.</i>
|
| 398 |
+
</p>
|
| 399 |
+
<div style="text-align: center; margin-bottom: 40px;">
|
| 400 |
+
<div style="
|
| 401 |
+
width: 220px;
|
| 402 |
+
height: 220px;
|
| 403 |
+
background-color: #38bdf8; /* sky-400 */
|
| 404 |
+
border-radius: 50%;
|
| 405 |
+
display: flex;
|
| 406 |
+
flex-direction: column;
|
| 407 |
+
align-items: center;
|
| 408 |
+
justify-content: center;
|
| 409 |
+
color: white;
|
| 410 |
+
font-weight: 800;
|
| 411 |
+
font-size: 2.8em;
|
| 412 |
+
margin: 0 auto;
|
| 413 |
+
box-shadow: 0 10px 25px rgba(0,0,0,0.4);
|
| 414 |
+
animation: pulse-argentina-superior 3s infinite ease-in-out;
|
| 415 |
+
border: 6px solid #10b981; /* emerald-600 */
|
| 416 |
+
text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
|
| 417 |
+
">
|
| 418 |
+
<span style="font-size: 0.8em;">✨ Oráculo ✨</span>
|
| 419 |
+
<span>🇦🇷 MateAI 🇦🇷</span>
|
| 420 |
+
</div>
|
| 421 |
+
<style>
|
| 422 |
+
@keyframes pulse-argentina-superior {
|
| 423 |
+
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(56, 189, 248, 0.7); }
|
| 424 |
+
50% { transform: scale(1.05); box-shadow: 0 0 0 20px rgba(56, 189, 248, 0); }
|
| 425 |
+
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(56, 189, 248, 0); }
|
| 426 |
+
}
|
| 427 |
+
</style>
|
| 428 |
+
</div>
|
| 429 |
+
"""
|
| 430 |
+
)
|
| 431 |
+
|
| 432 |
+
# --- Tab 1: Inicio y Perfil ---
|
| 433 |
+
with gr.Tab("Inicio & Perfil"):
|
| 434 |
+
gr.Markdown("<h2 style='color: #10b981;'>¡Bienvenido a tu Espacio MateAI!</h2><p>Acá manejás tu perfil para que MateAI te conozca a fondo.</p>")
|
| 435 |
+
with gr.Row():
|
| 436 |
+
with gr.Column():
|
| 437 |
+
gr.Markdown("<h3 style='color: #38bdf8;'>Crear Nuevo Usuario</h3>")
|
| 438 |
+
nombre_nuevo_usuario = gr.Textbox(label="Tu Nombre o Apodo", placeholder="Ej: Juan, La Colo, El Pibe")
|
| 439 |
+
preferencias_iniciales = gr.Dropdown(
|
| 440 |
+
["eco", "bienestar", "ambos"],
|
| 441 |
+
label="¿Qué tipo de sabiduría preferís de MateAI?",
|
| 442 |
+
value="ambos"
|
| 443 |
+
)
|
| 444 |
+
btn_crear_usuario = gr.Button("¡Crear Nuevo Usuario MateAI!", variant="primary")
|
| 445 |
+
output_creacion_usuario = gr.Textbox(label="Mensaje de Creación", interactive=False, lines=2)
|
| 446 |
+
with gr.Column():
|
| 447 |
+
gr.Markdown("<h3 style='color: #38bdf8;'>Cargar Perfil Existente</h3>")
|
| 448 |
+
user_id_existente = gr.Textbox(label="Si ya tenés ID, ponelo acá", placeholder="Ej: user_1_1678901234")
|
| 449 |
+
btn_cargar_perfil = gr.Button("Cargar Perfil Existente", variant="secondary")
|
| 450 |
+
output_cargar_perfil = gr.Textbox(label="Estado del Perfil", interactive=False, lines=2)
|
| 451 |
+
|
| 452 |
+
gr.Markdown("<h2 style='color: #10b981; margin-top: 2em;'>Tus Datos MateAI</h2>")
|
| 453 |
+
with gr.Row():
|
| 454 |
+
usuario_actual_id = gr.Textbox(label="ID de Usuario Actual", interactive=False, value="No logueado", show_copy_button=True)
|
| 455 |
+
usuario_actual_nombre = gr.Textbox(label="Nombre", interactive=False)
|
| 456 |
+
with gr.Row():
|
| 457 |
+
usuario_actual_puntos = gr.Textbox(label="Eco-Puntos Gauchos", interactive=False)
|
| 458 |
+
usuario_actual_insignia = gr.Textbox(label="Tu Insignia MateAI", interactive=False)
|
| 459 |
+
usuario_proxima_insignia = gr.Textbox(label="Próxima Meta de Insignia", interactive=False)
|
| 460 |
+
|
| 461 |
+
gr.Markdown("<h3 style='color: #10b981; margin-top: 1.5em;'>Ajustar Preferencias MateAI</h3>")
|
| 462 |
+
nuevas_preferencias_tipo = gr.Dropdown(
|
| 463 |
+
["eco", "bienestar", "ambos"],
|
| 464 |
+
label="Tipo de susurro preferido",
|
| 465 |
+
value="ambos"
|
| 466 |
+
)
|
| 467 |
+
nuevas_preferencias_frecuencia = gr.Dropdown(
|
| 468 |
+
["normal", "discreto", "proactivo"],
|
| 469 |
+
label="Frecuencia de susurros (simulado)",
|
| 470 |
+
value="normal"
|
| 471 |
+
)
|
| 472 |
+
modo_che_tranqui_checkbox = gr.Checkbox(label="Activar 'Modo Che, Tranqui' (prioriza calma)", value=False)
|
| 473 |
+
|
| 474 |
+
btn_actualizar_preferencias = gr.Button("Actualizar Preferencias", variant="primary")
|
| 475 |
+
output_actualizar_preferencias = gr.Textbox(label="Estado de Actualización", interactive=False)
|
| 476 |
+
|
| 477 |
+
# --- Tab 2: El Oráculo del Día ---
|
| 478 |
+
with gr.Tab("El Oráculo del Día"):
|
| 479 |
+
gr.Markdown("<h2 style='text-align: center; color: #10b981;'>✨ La Revelación Diaria del Oráculo MateAI ✨</h2><p style='text-align: center;'>Una sabiduría profunda para iluminar tu jornada. El Oráculo te habla una vez al día.</p>")
|
| 480 |
+
oraculo_output = gr.Textbox(
|
| 481 |
+
label="La Revelación del Oráculo",
|
| 482 |
+
lines=4,
|
| 483 |
+
placeholder="El Oráculo espera para susurrarte su sabiduría...",
|
| 484 |
+
interactive=False,
|
| 485 |
+
elem_id="oraculo-output"
|
| 486 |
+
)
|
| 487 |
+
btn_oraculo = gr.Button("¡Consultar al Oráculo MateAI!", variant="primary")
|
| 488 |
+
gr.Markdown("<p style='text-align: center; font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>El Oráculo te hablará una vez por día. Volvé mañana para una nueva revelación.</i></p>")
|
| 489 |
+
|
| 490 |
+
# --- Tab 3: Susurros del Momento ---
|
| 491 |
+
with gr.Tab("Susurros del Momento"):
|
| 492 |
+
gr.Markdown("<h2 style='color: #10b981;'>¡Pedí un Susurro Contextual a MateAI!</h2><p>Simulá tu contexto y dejá que MateAI te sorprenda.</p>")
|
| 493 |
+
with gr.Row():
|
| 494 |
+
hora_del_dia_input = gr.Dropdown(
|
| 495 |
+
["Mañana", "Mediodía/Tarde", "Noche"],
|
| 496 |
+
label="Momento del Día",
|
| 497 |
+
value="Mañana"
|
| 498 |
+
)
|
| 499 |
+
ubicacion_simulada_input = gr.Dropdown(
|
| 500 |
+
["Casa", "Oficina/Estudio", "Aire Libre/Calle"],
|
| 501 |
+
label="¿Dónde estás?",
|
| 502 |
+
value="Casa"
|
| 503 |
+
)
|
| 504 |
+
with gr.Row():
|
| 505 |
+
actividad_simulada_input = gr.Dropdown(
|
| 506 |
+
["Relajado", "Trabajando", "Ejercicio", "Leyendo", "Cocinando", "Viendo un partido"],
|
| 507 |
+
label="¿En qué andás?",
|
| 508 |
+
value="Relajado"
|
| 509 |
+
)
|
| 510 |
+
estado_animo_simulado_input = gr.Dropdown(
|
| 511 |
+
["Bien", "Cansado", "Bajoneado", "Motivado"],
|
| 512 |
+
label="¿Cómo te sentís?",
|
| 513 |
+
value="Bien"
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
btn_generar_susurro = gr.Button("¡Che, MateAI, tirame un susurro!", variant="primary")
|
| 517 |
+
susurro_output = gr.Textbox(
|
| 518 |
+
label="Tu Susurro de Bienestar y Sostenibilidad",
|
| 519 |
+
lines=5,
|
| 520 |
+
placeholder="MateAI está esperando tu contexto para cebarte un susurro...",
|
| 521 |
+
interactive=False
|
| 522 |
+
)
|
| 523 |
+
gr.Markdown("<p style='font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>MateAI aplica un pequeño 'cooldown' para que los susurros sean más valiosos.</i></p>")
|
| 524 |
+
|
| 525 |
+
# --- Tab 4: Desafíos MateAI ---
|
| 526 |
+
with gr.Tab("Desafíos MateAI"):
|
| 527 |
+
gr.Markdown("<h2 style='text-align: center; color: #10b981;'>🎯 Desafíos MateAI: ¡Ponete a Prueba! 🎯</h2><p style='text-align: center;'>MateAI te propone desafíos para crecer en bienestar y sostenibilidad. ¡Aceptá la misión!</p>")
|
| 528 |
+
desafio_output = gr.Textbox(
|
| 529 |
+
label="Tu Desafío del Día",
|
| 530 |
+
lines=3,
|
| 531 |
+
placeholder="Pedí un desafío para empezar a sumar Eco-Puntos y crecer con MateAI.",
|
| 532 |
+
interactive=False
|
| 533 |
+
)
|
| 534 |
+
btn_desafio = gr.Button("¡Quiero un Desafío MateAI!", variant="primary")
|
| 535 |
+
gr.Markdown("<p style='text-align: center; font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>Completar desafíos (simulado) te dará más Eco-Puntos y te acercará a nuevas insignias.</i></p>")
|
| 536 |
+
|
| 537 |
+
# --- Tab 5: Mi Historial y Logros ---
|
| 538 |
+
with gr.Tab("Mi Historial & Logros"):
|
| 539 |
+
gr.Markdown("<h2 style='color: #10b981;'>Tu Viaje con MateAI</h2><p>Acá podés ver tus susurros pasados y tus logros.</p>")
|
| 540 |
+
historial_susurros_output = gr.Textbox(label="Tus Últimos Susurros Recibidos", lines=10, interactive=False)
|
| 541 |
+
|
| 542 |
+
gr.Markdown("<h3 style='color: #10b981; margin-top: 1.5em;'>Tu Diario de Reflexión (Sesión Actual)</h3>")
|
| 543 |
+
diario_reflexion = gr.Textbox(
|
| 544 |
+
label="Escribí acá tus reflexiones del día...",
|
| 545 |
+
lines=10,
|
| 546 |
+
placeholder="¿Qué te hizo pensar el susurro de MateAI? ¿Qué aprendiste hoy? ¿Cómo te sentís?",
|
| 547 |
+
interactive=True
|
| 548 |
+
)
|
| 549 |
+
btn_descargar_diario = gr.Button("Descargar Diario (sesión actual)", variant="secondary")
|
| 550 |
+
gr.Markdown("<p style='font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>Nota: En esta demo, el diario se guarda solo para la sesión actual y se descarga como texto. En una versión de producción, se guardaría en tu perfil de forma segura.</i></p>")
|
| 551 |
+
|
| 552 |
+
# --- Funciones de Interacción para Gradio ---
|
| 553 |
+
|
| 554 |
+
# Función para crear usuario
|
| 555 |
+
def _create_user_gradio(name, prefs_type):
|
| 556 |
+
user, msg = user_manager.create_user(name, {"tipo_susurro": prefs_type})
|
| 557 |
+
current_points = user.eco_points
|
| 558 |
+
current_insignia = gamification_engine.get_insignia(current_points)
|
| 559 |
+
next_insignia_goal = gamification_engine.get_next_insignia_goal(current_points)
|
| 560 |
+
return user.user_id, msg, user.name, str(current_points), current_insignia, next_insignia_goal, "\n".join(user.nudge_history)
|
| 561 |
+
|
| 562 |
+
# Función para cargar usuario
|
| 563 |
+
def _load_user_gradio(user_id):
|
| 564 |
+
user, msg = user_manager.login_user(user_id)
|
| 565 |
+
if user:
|
| 566 |
+
current_points = user.eco_points
|
| 567 |
+
current_insignia = gamification_engine.get_insignia(current_points)
|
| 568 |
+
next_insignia_goal = gamification_engine.get_next_insignia_goal(current_points)
|
| 569 |
+
return user.user_id, msg, user.name, str(current_points), current_insignia, next_insignia_goal, "\n".join(user.nudge_history), user.preferences.get('tipo_susurro'), user.preferences.get('frecuencia'), user.preferences.get('modo_che_tranqui')
|
| 570 |
+
return "No logueado", msg, "", "", "", "", "", Config.DEFAULT_USER_PREFS['tipo_susurro'], Config.DEFAULT_USER_PREFS['frecuencia'], Config.DEFAULT_USER_PREFS['modo_che_tranqui']
|
| 571 |
+
|
| 572 |
+
# Función para actualizar preferencias
|
| 573 |
+
def _update_prefs_gradio(user_id, tipo_susurro, frecuencia, modo_che_tranqui):
|
| 574 |
+
if user_id == "No logueado" or user_id not in user_manager._users_db:
|
| 575 |
+
return "Error: Por favor, crea o carga un usuario primero."
|
| 576 |
+
|
| 577 |
+
new_prefs = {
|
| 578 |
+
"tipo_susurro": tipo_susurro,
|
| 579 |
+
"frecuencia": frecuencia,
|
| 580 |
+
"modo_che_tranqui": modo_che_tranqui
|
| 581 |
+
}
|
| 582 |
+
success = user_manager.update_user_preferences(user_id, new_prefs)
|
| 583 |
+
if success:
|
| 584 |
+
return f"Preferencias actualizadas para {user_manager._users_db[user_id].name}."
|
| 585 |
+
return "Error al actualizar preferencias."
|
| 586 |
+
|
| 587 |
+
# Función para generar susurro
|
| 588 |
+
def _generate_nudge_gradio(user_id, location, activity, sentiment):
|
| 589 |
+
if user_id == "No logueado" or user_id not in user_manager._users_db:
|
| 590 |
+
return "Por favor, crea o carga un usuario primero para recibir susurros personalizados.", "", "", ""
|
| 591 |
+
|
| 592 |
+
nudge_text = nudge_generator.generate_nudge(user_id, location, activity, sentiment)
|
| 593 |
+
|
| 594 |
+
# Actualizar UI de puntos e insignias
|
| 595 |
+
user = user_manager.get_current_user()
|
| 596 |
+
current_points = user.eco_points
|
| 597 |
+
current_insignia = gamification_engine.get_insignia(current_points)
|
| 598 |
+
next_insignia_goal = gamification_engine.get_next_insignia_goal(current_points)
|
| 599 |
+
historial = "\n".join(user.nudge_history)
|
| 600 |
+
|
| 601 |
+
return nudge_text, str(current_points), current_insignia, next_insignia_goal, historial
|
| 602 |
+
|
| 603 |
+
# Función para consultar oráculo
|
| 604 |
+
def _get_oracle_revelation_gradio(user_id):
|
| 605 |
+
if user_id == "No logueado" or user_id not in user_manager._users_db:
|
| 606 |
+
return "Por favor, crea o carga un usuario primero para consultar al Oráculo."
|
| 607 |
+
return nudge_generator.get_daily_oracle_revelation(user_id)
|
| 608 |
+
|
| 609 |
+
# Función para obtener desafío
|
| 610 |
+
def _get_mateai_challenge_gradio(user_id):
|
| 611 |
+
if user_id == "No logueado" or user_id not in user_manager._users_db:
|
| 612 |
+
return "Por favor, crea o carga un usuario primero para recibir desafíos."
|
| 613 |
+
return nudge_generator.get_mateai_challenge(user_id)
|
| 614 |
+
|
| 615 |
+
# Función para descargar diario
|
| 616 |
+
def _download_diary_gradio(diary_text):
|
| 617 |
+
return gr.File(value=diary_text.encode('utf-8'), filename="diario_mateai.txt", type="bytes")
|
| 618 |
+
|
| 619 |
+
# --- Conexión de Eventos ---
|
| 620 |
+
btn_crear_usuario.click(
|
| 621 |
+
fn=_create_user_gradio,
|
| 622 |
+
inputs=[nombre_nuevo_usuario, preferencias_iniciales],
|
| 623 |
+
outputs=[usuario_actual_id, output_creacion_usuario, usuario_actual_nombre, usuario_actual_puntos, usuario_actual_insignia, usuario_proxima_insignia, historial_susurros_output]
|
| 624 |
+
)
|
| 625 |
+
|
| 626 |
+
btn_cargar_perfil.click(
|
| 627 |
+
fn=_load_user_gradio,
|
| 628 |
+
inputs=[user_id_existente],
|
| 629 |
+
outputs=[usuario_actual_id, output_cargar_perfil, usuario_actual_nombre, usuario_actual_puntos, usuario_actual_insignia, usuario_proxima_insignia, historial_susurros_output, nuevas_preferencias_tipo, nuevas_preferencias_frecuencia, modo_che_tranqui_checkbox]
|
| 630 |
+
)
|
| 631 |
+
|
| 632 |
+
btn_actualizar_preferencias.click(
|
| 633 |
+
fn=_update_prefs_gradio,
|
| 634 |
+
inputs=[usuario_actual_id, nuevas_preferencias_tipo, nuevas_preferencias_frecuencia, modo_che_tranqui_checkbox],
|
| 635 |
+
outputs=output_actualizar_preferencias
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
btn_generar_susurro.click(
|
| 639 |
+
fn=_generate_nudge_gradio,
|
| 640 |
+
inputs=[usuario_actual_id, ubicacion_simulada_input, actividad_simulada_input, estado_animo_simulado_input],
|
| 641 |
+
outputs=[susurro_output, usuario_actual_puntos, usuario_actual_insignia, usuario_proxima_insignia, historial_susurros_output]
|
| 642 |
+
)
|
| 643 |
+
|
| 644 |
+
btn_oraculo.click(
|
| 645 |
+
fn=_get_oracle_revelation_gradio,
|
| 646 |
+
inputs=[usuario_actual_id],
|
| 647 |
+
outputs=oraculo_output
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
+
btn_desafio.click(
|
| 651 |
+
fn=_get_mateai_challenge_gradio,
|
| 652 |
+
inputs=[usuario_actual_id],
|
| 653 |
+
outputs=desafio_output
|
| 654 |
+
)
|
| 655 |
+
|
| 656 |
+
btn_descargar_diario.click(
|
| 657 |
+
fn=_download_diary_gradio,
|
| 658 |
+
inputs=[diario_reflexion],
|
| 659 |
+
outputs=gr.File(label="Descargar tu Diario")
|
| 660 |
+
)
|
| 661 |
+
|
| 662 |
+
# Para ejecutar localmente (descomentar si no es para Hugging Face):
|
| 663 |
+
# if __name__ == "__main__":
|
| 664 |
+
# demo.launch()
|