Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -66,17 +66,13 @@ def upload_to_sharepoint(file_path):
|
|
| 66 |
except Exception as e:
|
| 67 |
return f"Error Sharepoint (Verificar credenciales Azure/HF): {str(e)}"
|
| 68 |
|
| 69 |
-
# NUEVO: Módulo de Integración con Entidades Públicas (SARLAFT)
|
| 70 |
def check_antecedentes_publicos(cedula):
|
| 71 |
"""
|
| 72 |
Estructura preparada para conectar con el proveedor de APIs del Estado.
|
| 73 |
Actualmente simula la validación mientras se conecta el servicio definitivo.
|
| 74 |
"""
|
| 75 |
-
# TODO: Reemplazar con request a API de proveedor (ej. TusDatos)
|
| 76 |
print(f"Consultando antecedentes para la cédula: {cedula}")
|
| 77 |
-
time.sleep(1)
|
| 78 |
-
|
| 79 |
-
# Estructura de respuesta de las listas restrictivas
|
| 80 |
reporte = {
|
| 81 |
"Procuraduria": "Sin antecedentes",
|
| 82 |
"Policia_Nacional": "Sin requerimientos",
|
|
@@ -91,15 +87,25 @@ def run_full_onboarding(video, doc_img, full_name):
|
|
| 91 |
if not video or not doc_img or not full_name:
|
| 92 |
return None, {"Estatus": "ERROR", "Detalle": "Campos incompletos."}
|
| 93 |
|
| 94 |
-
# 1. Extracción de cédula
|
| 95 |
try:
|
| 96 |
img_pil = Image.open(doc_img)
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
except Exception as e:
|
| 100 |
-
return None, {"Estatus": "ERROR", "Detalle": f"Fallo en extracción IA: {str(e)}"}
|
| 101 |
|
| 102 |
-
# 2. Validación de Listas Restrictivas
|
| 103 |
df_bn = pd.read_csv(LISTA_NEGRA_FILE)
|
| 104 |
if ocr_res in df_bn['cedula'].astype(str).values:
|
| 105 |
return None, {"Estatus": "BLOQUEADO", "Motivo": "Cédula detectada en Lista Negra Interna."}
|
|
@@ -148,7 +154,7 @@ def run_full_onboarding(video, doc_img, full_name):
|
|
| 148 |
if not (blink and motion):
|
| 149 |
return None, {"Estatus": "RECHAZADO", "Detalle": "Prueba de vida fallida (Faltó parpadeo o giro frontal)."}
|
| 150 |
|
| 151 |
-
# 4. Match Biométrico
|
| 152 |
try:
|
| 153 |
doc_bytes = face_recognition.load_image_file(doc_img)
|
| 154 |
doc_encodings = face_recognition.face_encodings(doc_bytes)
|
|
@@ -157,7 +163,6 @@ def run_full_onboarding(video, doc_img, full_name):
|
|
| 157 |
if not doc_encodings or not live_encodings:
|
| 158 |
return None, {"Estatus": "ERROR", "Detalle": "No se pudo mapear la geometría facial de la imagen o del video."}
|
| 159 |
|
| 160 |
-
# Tolerancia ajustada a 0.6 para compensar los hologramas del documento físico
|
| 161 |
match = bool(face_recognition.compare_faces([doc_encodings[0]], live_encodings[0], tolerance=0.6)[0])
|
| 162 |
except Exception as e:
|
| 163 |
return None, {"Estatus": "ERROR", "Detalle": f"Error del motor C++: {str(e)}"}
|
|
@@ -166,6 +171,7 @@ def run_full_onboarding(video, doc_img, full_name):
|
|
| 166 |
res = {
|
| 167 |
"Estatus": "APROBADO" if match else "RECHAZADO",
|
| 168 |
"Cédula_Extraída": ocr_res,
|
|
|
|
| 169 |
"Coincidencia_Facial": match,
|
| 170 |
"Validación_Antecedentes": "Completada (Sin reportes)",
|
| 171 |
"Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
@@ -184,7 +190,9 @@ def run_full_onboarding(video, doc_img, full_name):
|
|
| 184 |
# Título
|
| 185 |
pdf.set_text_color(0, 51, 102)
|
| 186 |
pdf.set_font("Arial", "B", 16)
|
| 187 |
-
pdf.cell(200, 10, f"Certificado de Seguridad y SARLAFT
|
|
|
|
|
|
|
| 188 |
|
| 189 |
# Contenido
|
| 190 |
pdf.set_text_color(0, 0, 0)
|
|
@@ -197,7 +205,7 @@ def run_full_onboarding(video, doc_img, full_name):
|
|
| 197 |
pdf.output(path_pdf)
|
| 198 |
res["Auditoría_SharePoint"] = upload_to_sharepoint(path_pdf)
|
| 199 |
|
| 200 |
-
if match: send_teams_alert(f"✅ ONBOARDING EXITOSO: {full_name} | ID: {ocr_res}")
|
| 201 |
else: send_teams_alert(f"🚨 FRAUDE DETECTADO: El rostro de la cédula y el video no coinciden.", "ALERTA")
|
| 202 |
|
| 203 |
return path_pdf, res
|
|
|
|
| 66 |
except Exception as e:
|
| 67 |
return f"Error Sharepoint (Verificar credenciales Azure/HF): {str(e)}"
|
| 68 |
|
|
|
|
| 69 |
def check_antecedentes_publicos(cedula):
|
| 70 |
"""
|
| 71 |
Estructura preparada para conectar con el proveedor de APIs del Estado.
|
| 72 |
Actualmente simula la validación mientras se conecta el servicio definitivo.
|
| 73 |
"""
|
|
|
|
| 74 |
print(f"Consultando antecedentes para la cédula: {cedula}")
|
| 75 |
+
time.sleep(1)
|
|
|
|
|
|
|
| 76 |
reporte = {
|
| 77 |
"Procuraduria": "Sin antecedentes",
|
| 78 |
"Policia_Nacional": "Sin requerimientos",
|
|
|
|
| 87 |
if not video or not doc_img or not full_name:
|
| 88 |
return None, {"Estatus": "ERROR", "Detalle": "Campos incompletos."}
|
| 89 |
|
| 90 |
+
# 1. Extracción INTELIGENTE de cédula, nombres y apellidos
|
| 91 |
try:
|
| 92 |
img_pil = Image.open(doc_img)
|
| 93 |
+
prompt_ocr = """Extrae de esta cédula colombiana la siguiente información.
|
| 94 |
+
Retorna ESTRICTAMENTE un JSON con las llaves: 'cedula' (solo números sin puntos), 'apellidos' y 'nombres'."""
|
| 95 |
+
|
| 96 |
+
res_ai = ai_model.generate_content([prompt_ocr, img_pil])
|
| 97 |
+
raw_ai = res_ai.text.replace('```json', '').replace('```', '').strip()
|
| 98 |
+
datos_cedula = json.loads(raw_ai)
|
| 99 |
+
|
| 100 |
+
ocr_res = str(datos_cedula.get("cedula", "")).strip()
|
| 101 |
+
nombres_doc = datos_cedula.get("nombres", "").strip()
|
| 102 |
+
apellidos_doc = datos_cedula.get("apellidos", "").strip()
|
| 103 |
+
nombre_completo_doc = f"{nombres_doc} {apellidos_doc}"
|
| 104 |
+
|
| 105 |
except Exception as e:
|
| 106 |
+
return None, {"Estatus": "ERROR", "Detalle": f"Fallo en extracción IA documental: {str(e)}"}
|
| 107 |
|
| 108 |
+
# 2. Validación de Listas Restrictivas
|
| 109 |
df_bn = pd.read_csv(LISTA_NEGRA_FILE)
|
| 110 |
if ocr_res in df_bn['cedula'].astype(str).values:
|
| 111 |
return None, {"Estatus": "BLOQUEADO", "Motivo": "Cédula detectada en Lista Negra Interna."}
|
|
|
|
| 154 |
if not (blink and motion):
|
| 155 |
return None, {"Estatus": "RECHAZADO", "Detalle": "Prueba de vida fallida (Faltó parpadeo o giro frontal)."}
|
| 156 |
|
| 157 |
+
# 4. Match Biométrico
|
| 158 |
try:
|
| 159 |
doc_bytes = face_recognition.load_image_file(doc_img)
|
| 160 |
doc_encodings = face_recognition.face_encodings(doc_bytes)
|
|
|
|
| 163 |
if not doc_encodings or not live_encodings:
|
| 164 |
return None, {"Estatus": "ERROR", "Detalle": "No se pudo mapear la geometría facial de la imagen o del video."}
|
| 165 |
|
|
|
|
| 166 |
match = bool(face_recognition.compare_faces([doc_encodings[0]], live_encodings[0], tolerance=0.6)[0])
|
| 167 |
except Exception as e:
|
| 168 |
return None, {"Estatus": "ERROR", "Detalle": f"Error del motor C++: {str(e)}"}
|
|
|
|
| 171 |
res = {
|
| 172 |
"Estatus": "APROBADO" if match else "RECHAZADO",
|
| 173 |
"Cédula_Extraída": ocr_res,
|
| 174 |
+
"Nombre_en_Cédula": nombre_completo_doc,
|
| 175 |
"Coincidencia_Facial": match,
|
| 176 |
"Validación_Antecedentes": "Completada (Sin reportes)",
|
| 177 |
"Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
| 190 |
# Título
|
| 191 |
pdf.set_text_color(0, 51, 102)
|
| 192 |
pdf.set_font("Arial", "B", 16)
|
| 193 |
+
pdf.cell(200, 10, f"Certificado de Seguridad y SARLAFT", ln=True, align='C')
|
| 194 |
+
pdf.set_font("Arial", "B", 12)
|
| 195 |
+
pdf.cell(200, 10, f"Usuario Registrado: {full_name}", ln=True, align='C')
|
| 196 |
|
| 197 |
# Contenido
|
| 198 |
pdf.set_text_color(0, 0, 0)
|
|
|
|
| 205 |
pdf.output(path_pdf)
|
| 206 |
res["Auditoría_SharePoint"] = upload_to_sharepoint(path_pdf)
|
| 207 |
|
| 208 |
+
if match: send_teams_alert(f"✅ ONBOARDING EXITOSO: {full_name} ({nombre_completo_doc}) | ID: {ocr_res}")
|
| 209 |
else: send_teams_alert(f"🚨 FRAUDE DETECTADO: El rostro de la cédula y el video no coinciden.", "ALERTA")
|
| 210 |
|
| 211 |
return path_pdf, res
|