Update app.py
Browse files
app.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
import os
|
2 |
-
import io
|
3 |
import random
|
4 |
import pandas as pd
|
5 |
import chainlit as cl
|
@@ -15,15 +14,10 @@ EXCEL_PATH = "MasterData SISO - Actualizada - Jan-29-2025.xlsx"
|
|
15 |
AUDIO_PATH = "Manual del Operador de Tienda.wav"
|
16 |
CERTIFICATE_TEMPLATE = "certificate_template_.pdf"
|
17 |
|
18 |
-
# Convertimos la ruta del audio a un path tipo URL (opcional)
|
19 |
abs_path = os.path.abspath(AUDIO_PATH).replace("\\", "/")
|
20 |
AUDIO_URL = f"file:///{quote(abs_path)}"
|
21 |
|
22 |
-
# Carpeta de salida para “generar” el PDF
|
23 |
-
OUTPUT_FOLDER = "Certificates"
|
24 |
-
if not os.path.exists(OUTPUT_FOLDER):
|
25 |
-
os.makedirs(OUTPUT_FOLDER)
|
26 |
-
|
27 |
# ===== PREGUNTAS DEL EXAMEN =====
|
28 |
PREGUNTAS = [
|
29 |
{
|
@@ -93,7 +87,7 @@ def cargar_datos():
|
|
93 |
],
|
94 |
dtype={'SAPId': 'int32'}
|
95 |
)
|
96 |
-
#
|
97 |
for col in ['Fecha', 'Calificacion', 'Curso']:
|
98 |
if col not in df.columns:
|
99 |
df[col] = None
|
@@ -102,7 +96,7 @@ def cargar_datos():
|
|
102 |
print(f"Error cargando datos: {str(e)}")
|
103 |
return {}
|
104 |
|
105 |
-
def actualizar_excel(sap_id, aprobado
|
106 |
"""Marca en Excel la fecha, el resultado y el curso para el SAP ID dado."""
|
107 |
try:
|
108 |
df = pd.read_excel(EXCEL_PATH)
|
@@ -114,34 +108,7 @@ def actualizar_excel(sap_id, aprobado: bool):
|
|
114 |
except Exception as e:
|
115 |
print(f"Error actualizando Excel: {str(e)}")
|
116 |
|
117 |
-
# =====
|
118 |
-
def generar_pdf_sin_texto(sap_id: int) -> str:
|
119 |
-
"""
|
120 |
-
Retorna la ruta de un PDF “generado” a partir de la plantilla original.
|
121 |
-
Aquí NO dibujamos ni agregamos texto. Simplemente copiamos la plantilla
|
122 |
-
y la renombramos con el ID de la persona.
|
123 |
-
"""
|
124 |
-
# Lee la plantilla
|
125 |
-
try:
|
126 |
-
with open(CERTIFICATE_TEMPLATE, "rb") as f_in:
|
127 |
-
pdf_data = f_in.read()
|
128 |
-
except Exception as e:
|
129 |
-
print(f"Error leyendo la plantilla: {e}")
|
130 |
-
return None
|
131 |
-
|
132 |
-
# Crea un nuevo archivo .pdf para este SAP
|
133 |
-
final_name = f"{sap_id}_certificate.pdf"
|
134 |
-
final_path = os.path.join(OUTPUT_FOLDER, final_name)
|
135 |
-
try:
|
136 |
-
with open(final_path, "wb") as f_out:
|
137 |
-
f_out.write(pdf_data)
|
138 |
-
except Exception as e:
|
139 |
-
print(f"Error escribiendo el PDF final: {e}")
|
140 |
-
return None
|
141 |
-
|
142 |
-
return final_path
|
143 |
-
|
144 |
-
# ===== CHAINLIT: LÓGICA DE CONVERSACIÓN =====
|
145 |
@cl.on_chat_start
|
146 |
async def inicio():
|
147 |
SAP_DICT = cargar_datos()
|
@@ -153,12 +120,12 @@ async def manejar_mensaje(message: cl.Message):
|
|
153 |
SAP_DICT = cl.user_session.get("SAP_DICT")
|
154 |
user_data = cl.user_session.get("user_data")
|
155 |
|
156 |
-
# Si ya
|
157 |
if user_data and user_data.get("en_examen"):
|
158 |
await procesar_respuesta(message)
|
159 |
return
|
160 |
|
161 |
-
#
|
162 |
try:
|
163 |
sap_id = int(message.content)
|
164 |
if sap_id not in SAP_DICT:
|
@@ -173,7 +140,7 @@ async def manejar_mensaje(message: cl.Message):
|
|
173 |
└ Centro Coste: {usuario['Centro de coste']}
|
174 |
"""
|
175 |
|
176 |
-
# Guardamos en sesión
|
177 |
cl.user_session.set("user_data", {
|
178 |
"sap_id": sap_id,
|
179 |
"puntaje": 0,
|
@@ -191,13 +158,13 @@ async def mostrar_pregunta():
|
|
191 |
user_data = cl.user_session.get("user_data")
|
192 |
num_pregunta = user_data["pregunta_actual"]
|
193 |
|
194 |
-
# Si ya no hay más preguntas, mostramos resultado
|
195 |
if num_pregunta >= len(PREGUNTAS):
|
196 |
await mostrar_resultado()
|
197 |
return
|
198 |
|
199 |
-
|
200 |
-
mensaje = f"📝 **Pregunta {num_pregunta + 1}**\n{
|
201 |
await cl.Message(content=mensaje + "\n\n🔘 Respuesta (A/B/C/D):").send()
|
202 |
|
203 |
async def procesar_respuesta(message: cl.Message):
|
@@ -206,12 +173,9 @@ async def procesar_respuesta(message: cl.Message):
|
|
206 |
|
207 |
respuesta = message.content.strip().upper()
|
208 |
|
209 |
-
# Validar la respuesta
|
210 |
if len(respuesta) != 1 or respuesta not in ['A', 'B', 'C', 'D']:
|
211 |
-
await cl.Message(content=""
|
212 |
-
⚠️ **Formato incorrecto** ⚠️
|
213 |
-
Por favor ingrese solo la letra correspondiente (A/B/C/D).
|
214 |
-
""").send()
|
215 |
return
|
216 |
|
217 |
correcta = PREGUNTAS[num_pregunta]['respuesta']
|
@@ -224,7 +188,7 @@ async def procesar_respuesta(message: cl.Message):
|
|
224 |
user_data["pregunta_actual"] += 1
|
225 |
cl.user_session.set("user_data", user_data)
|
226 |
|
227 |
-
# Si quedan preguntas, mostramos la siguiente
|
228 |
if user_data["pregunta_actual"] < len(PREGUNTAS):
|
229 |
await mostrar_pregunta()
|
230 |
else:
|
@@ -233,8 +197,8 @@ async def procesar_respuesta(message: cl.Message):
|
|
233 |
async def mostrar_resultado():
|
234 |
user_data = cl.user_session.get("user_data")
|
235 |
SAP_DICT = cl.user_session.get("SAP_DICT")
|
236 |
-
|
237 |
-
# Nota de aprobación
|
238 |
aprobado = user_data['puntaje'] >= 3
|
239 |
actualizar_excel(user_data['sap_id'], aprobado)
|
240 |
|
@@ -244,23 +208,18 @@ async def mostrar_resultado():
|
|
244 |
"❌ **LO SENTIMOS, REPROBADO**\n"
|
245 |
)
|
246 |
mensaje += f"Puntaje Final: {user_data['puntaje']}/5"
|
247 |
-
|
248 |
if aprobado:
|
249 |
-
#
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
]
|
258 |
-
).send()
|
259 |
-
else:
|
260 |
-
# En caso de error leyendo/escribiendo PDF
|
261 |
-
await cl.Message(content=mensaje + "\n\n(⚠️ Error al generar el PDF)").send()
|
262 |
else:
|
263 |
-
# Si
|
264 |
await cl.Message(
|
265 |
content=mensaje,
|
266 |
elements=[
|
@@ -272,8 +231,8 @@ async def mostrar_resultado():
|
|
272 |
)
|
273 |
]
|
274 |
).send()
|
275 |
-
|
276 |
-
# Reiniciamos
|
277 |
cl.user_session.set("user_data", None)
|
278 |
await cl.Message(content="\nIngrese nuevo SAP ID para otro examen:").send()
|
279 |
|
|
|
1 |
import os
|
|
|
2 |
import random
|
3 |
import pandas as pd
|
4 |
import chainlit as cl
|
|
|
14 |
AUDIO_PATH = "Manual del Operador de Tienda.wav"
|
15 |
CERTIFICATE_TEMPLATE = "certificate_template_.pdf"
|
16 |
|
17 |
+
# Convertimos la ruta del audio a un path tipo URL (opcional, útil localmente)
|
18 |
abs_path = os.path.abspath(AUDIO_PATH).replace("\\", "/")
|
19 |
AUDIO_URL = f"file:///{quote(abs_path)}"
|
20 |
|
|
|
|
|
|
|
|
|
|
|
21 |
# ===== PREGUNTAS DEL EXAMEN =====
|
22 |
PREGUNTAS = [
|
23 |
{
|
|
|
87 |
],
|
88 |
dtype={'SAPId': 'int32'}
|
89 |
)
|
90 |
+
# Si faltan columnas, las agregamos
|
91 |
for col in ['Fecha', 'Calificacion', 'Curso']:
|
92 |
if col not in df.columns:
|
93 |
df[col] = None
|
|
|
96 |
print(f"Error cargando datos: {str(e)}")
|
97 |
return {}
|
98 |
|
99 |
+
def actualizar_excel(sap_id, aprobado):
|
100 |
"""Marca en Excel la fecha, el resultado y el curso para el SAP ID dado."""
|
101 |
try:
|
102 |
df = pd.read_excel(EXCEL_PATH)
|
|
|
108 |
except Exception as e:
|
109 |
print(f"Error actualizando Excel: {str(e)}")
|
110 |
|
111 |
+
# ===== CHAINLIT =====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
@cl.on_chat_start
|
113 |
async def inicio():
|
114 |
SAP_DICT = cargar_datos()
|
|
|
120 |
SAP_DICT = cl.user_session.get("SAP_DICT")
|
121 |
user_data = cl.user_session.get("user_data")
|
122 |
|
123 |
+
# Si el usuario ya está en examen, verificamos la respuesta
|
124 |
if user_data and user_data.get("en_examen"):
|
125 |
await procesar_respuesta(message)
|
126 |
return
|
127 |
|
128 |
+
# Sino, interpretamos como SAP ID
|
129 |
try:
|
130 |
sap_id = int(message.content)
|
131 |
if sap_id not in SAP_DICT:
|
|
|
140 |
└ Centro Coste: {usuario['Centro de coste']}
|
141 |
"""
|
142 |
|
143 |
+
# Guardamos datos en la sesión
|
144 |
cl.user_session.set("user_data", {
|
145 |
"sap_id": sap_id,
|
146 |
"puntaje": 0,
|
|
|
158 |
user_data = cl.user_session.get("user_data")
|
159 |
num_pregunta = user_data["pregunta_actual"]
|
160 |
|
161 |
+
# Si ya no hay más preguntas, mostramos resultado final
|
162 |
if num_pregunta >= len(PREGUNTAS):
|
163 |
await mostrar_resultado()
|
164 |
return
|
165 |
|
166 |
+
p = PREGUNTAS[num_pregunta]
|
167 |
+
mensaje = f"📝 **Pregunta {num_pregunta + 1}**\n{p['pregunta']}\n\n" + "\n".join(p['opciones'])
|
168 |
await cl.Message(content=mensaje + "\n\n🔘 Respuesta (A/B/C/D):").send()
|
169 |
|
170 |
async def procesar_respuesta(message: cl.Message):
|
|
|
173 |
|
174 |
respuesta = message.content.strip().upper()
|
175 |
|
176 |
+
# Validar la respuesta
|
177 |
if len(respuesta) != 1 or respuesta not in ['A', 'B', 'C', 'D']:
|
178 |
+
await cl.Message(content="⚠️ **Formato incorrecto**. Respuesta = A/B/C/D").send()
|
|
|
|
|
|
|
179 |
return
|
180 |
|
181 |
correcta = PREGUNTAS[num_pregunta]['respuesta']
|
|
|
188 |
user_data["pregunta_actual"] += 1
|
189 |
cl.user_session.set("user_data", user_data)
|
190 |
|
191 |
+
# Si quedan preguntas, mostramos la siguiente
|
192 |
if user_data["pregunta_actual"] < len(PREGUNTAS):
|
193 |
await mostrar_pregunta()
|
194 |
else:
|
|
|
197 |
async def mostrar_resultado():
|
198 |
user_data = cl.user_session.get("user_data")
|
199 |
SAP_DICT = cl.user_session.get("SAP_DICT")
|
200 |
+
|
201 |
+
# Nota de aprobación (3/5)
|
202 |
aprobado = user_data['puntaje'] >= 3
|
203 |
actualizar_excel(user_data['sap_id'], aprobado)
|
204 |
|
|
|
208 |
"❌ **LO SENTIMOS, REPROBADO**\n"
|
209 |
)
|
210 |
mensaje += f"Puntaje Final: {user_data['puntaje']}/5"
|
211 |
+
|
212 |
if aprobado:
|
213 |
+
# Solo mostramos la plantilla PDF tal cual
|
214 |
+
await cl.Message(
|
215 |
+
content=mensaje,
|
216 |
+
elements=[
|
217 |
+
# Se envía la plantilla como archivo descargable
|
218 |
+
cl.File(path=CERTIFICATE_TEMPLATE, name="Certificado de Aprobación")
|
219 |
+
]
|
220 |
+
).send()
|
|
|
|
|
|
|
|
|
|
|
221 |
else:
|
222 |
+
# Si no aprueba, muestra el audio
|
223 |
await cl.Message(
|
224 |
content=mensaje,
|
225 |
elements=[
|
|
|
231 |
)
|
232 |
]
|
233 |
).send()
|
234 |
+
|
235 |
+
# Reiniciamos para un nuevo examen
|
236 |
cl.user_session.set("user_data", None)
|
237 |
await cl.Message(content="\nIngrese nuevo SAP ID para otro examen:").send()
|
238 |
|