Spaces:
Sleeping
Sleeping
Tabla funel_compra
Browse files- create_funel_compra_table.py +70 -0
- funciones.py +201 -0
- imagen_log.sql +12 -0
- main.py +77 -5
- models.py +32 -0
- test_funel_compra.py +60 -0
- test_funel_simple.py +65 -0
- test_output.txt +0 -0
create_funel_compra_table.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Script para crear la tabla funel_compra"""
|
| 2 |
+
|
| 3 |
+
from connection import get_connection
|
| 4 |
+
import logging
|
| 5 |
+
|
| 6 |
+
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
| 7 |
+
logger = logging.getLogger(__name__)
|
| 8 |
+
|
| 9 |
+
def create_funel_compra_table():
|
| 10 |
+
"""Crea la tabla funel_compra en la BD"""
|
| 11 |
+
|
| 12 |
+
conn = get_connection()
|
| 13 |
+
|
| 14 |
+
if not conn:
|
| 15 |
+
logger.error("❌ No se pudo conectar a la base de datos")
|
| 16 |
+
return False
|
| 17 |
+
|
| 18 |
+
try:
|
| 19 |
+
cursor = conn.cursor()
|
| 20 |
+
|
| 21 |
+
logger.info("📝 Creando tabla funel_compra...")
|
| 22 |
+
|
| 23 |
+
sql = """
|
| 24 |
+
CREATE TABLE IF NOT EXISTS `funel_compra` (
|
| 25 |
+
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
| 26 |
+
`usuario` VARCHAR(255) NOT NULL,
|
| 27 |
+
`mail` VARCHAR(255) NOT NULL,
|
| 28 |
+
`accion` VARCHAR(255) NOT NULL,
|
| 29 |
+
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
|
| 30 |
+
INDEX idx_usuario (usuario),
|
| 31 |
+
INDEX idx_mail (mail),
|
| 32 |
+
INDEX idx_accion (accion),
|
| 33 |
+
INDEX idx_created_at (created_at)
|
| 34 |
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
cursor.execute(sql)
|
| 38 |
+
conn.commit()
|
| 39 |
+
|
| 40 |
+
logger.info("✅ Tabla 'funel_compra' creada exitosamente")
|
| 41 |
+
|
| 42 |
+
# Describir tabla
|
| 43 |
+
cursor.execute("DESCRIBE funel_compra")
|
| 44 |
+
fields = cursor.fetchall()
|
| 45 |
+
cursor.close()
|
| 46 |
+
conn.close()
|
| 47 |
+
|
| 48 |
+
logger.info("\n📋 Estructura de la tabla:")
|
| 49 |
+
logger.info("=" * 100)
|
| 50 |
+
logger.info(f"{'Campo':<20} | {'Tipo':<25} | {'Null':<6} | {'Key':<8} | {'Extra':<30}")
|
| 51 |
+
logger.info("-" * 100)
|
| 52 |
+
for field in fields:
|
| 53 |
+
field_name = field[0]
|
| 54 |
+
field_type = field[1]
|
| 55 |
+
field_null = field[2]
|
| 56 |
+
field_key = field[3]
|
| 57 |
+
field_extra = str(field[5]) if field[5] else '-'
|
| 58 |
+
logger.info(f"{field_name:<20} | {field_type:<25} | {field_null:<6} | {field_key:<8} | {field_extra:<30}")
|
| 59 |
+
logger.info("=" * 100)
|
| 60 |
+
|
| 61 |
+
return True
|
| 62 |
+
|
| 63 |
+
except Exception as e:
|
| 64 |
+
logger.error(f"❌ Error al crear la tabla: {str(e)}")
|
| 65 |
+
conn.close()
|
| 66 |
+
return False
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
if __name__ == "__main__":
|
| 70 |
+
create_funel_compra_table()
|
funciones.py
CHANGED
|
@@ -589,3 +589,204 @@ def obtener_todas_acciones(limit: int = 10, offset: int = 0) -> dict:
|
|
| 589 |
"success": False,
|
| 590 |
"error": f"Error al obtener acciones: {str(e)}"
|
| 591 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
"success": False,
|
| 590 |
"error": f"Error al obtener acciones: {str(e)}"
|
| 591 |
}
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
def crear_evento_funel(datos) -> dict:
|
| 595 |
+
"""
|
| 596 |
+
Crea un evento en el funel de compra.
|
| 597 |
+
|
| 598 |
+
Args:
|
| 599 |
+
datos: Object FunelCompraRequest con usuario, mail, accion
|
| 600 |
+
|
| 601 |
+
Returns:
|
| 602 |
+
dict: Evento creado o error
|
| 603 |
+
"""
|
| 604 |
+
|
| 605 |
+
conn = get_connection()
|
| 606 |
+
|
| 607 |
+
if not conn:
|
| 608 |
+
logger.error("❌ No se pudo conectar a la base de datos")
|
| 609 |
+
return {
|
| 610 |
+
"success": False,
|
| 611 |
+
"error": "No se pudo conectar a la base de datos"
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
try:
|
| 615 |
+
logger.info(f"📊 Registrando evento funel - Usuario: {datos.usuario}, Mail: {datos.mail}, Acción: {datos.accion}")
|
| 616 |
+
|
| 617 |
+
cursor = conn.cursor()
|
| 618 |
+
|
| 619 |
+
sql = """
|
| 620 |
+
INSERT INTO `funel_compra`
|
| 621 |
+
(usuario, mail, accion)
|
| 622 |
+
VALUES (%s, %s, %s)
|
| 623 |
+
"""
|
| 624 |
+
|
| 625 |
+
valores = (datos.usuario, datos.mail, datos.accion)
|
| 626 |
+
|
| 627 |
+
cursor.execute(sql, valores)
|
| 628 |
+
conn.commit()
|
| 629 |
+
|
| 630 |
+
logger.info(f"✅ Evento funel registrado en BD - Usuario: {datos.usuario}")
|
| 631 |
+
|
| 632 |
+
# Recuperar el evento creado
|
| 633 |
+
cursor.execute(
|
| 634 |
+
"SELECT * FROM `funel_compra` WHERE usuario = %s AND mail = %s AND accion = %s ORDER BY created_at DESC LIMIT 1",
|
| 635 |
+
(datos.usuario, datos.mail, datos.accion)
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
evento = cursor.fetchone()
|
| 639 |
+
cursor.close()
|
| 640 |
+
conn.close()
|
| 641 |
+
|
| 642 |
+
if evento:
|
| 643 |
+
logger.info(f"✅ Evento recuperado - ID: {evento[0]}")
|
| 644 |
+
return {
|
| 645 |
+
"success": True,
|
| 646 |
+
"data": {
|
| 647 |
+
"id": evento[0],
|
| 648 |
+
"usuario": evento[1],
|
| 649 |
+
"mail": evento[2],
|
| 650 |
+
"accion": evento[3],
|
| 651 |
+
"created_at": evento[4]
|
| 652 |
+
}
|
| 653 |
+
}
|
| 654 |
+
else:
|
| 655 |
+
logger.error("❌ No se pudo recuperar el evento creado")
|
| 656 |
+
return {
|
| 657 |
+
"success": False,
|
| 658 |
+
"error": "No se pudo recuperar el evento"
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
except Exception as e:
|
| 662 |
+
logger.error(f"❌ Error al crear evento funel: {str(e)}")
|
| 663 |
+
return {
|
| 664 |
+
"success": False,
|
| 665 |
+
"error": f"Error al crear evento: {str(e)}"
|
| 666 |
+
}
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
def obtener_eventos_funel(usuario: str) -> dict:
|
| 670 |
+
"""
|
| 671 |
+
Obtiene todos los eventos de funel para un usuario.
|
| 672 |
+
|
| 673 |
+
Args:
|
| 674 |
+
usuario: Nombre del usuario
|
| 675 |
+
|
| 676 |
+
Returns:
|
| 677 |
+
dict: Lista de eventos o error
|
| 678 |
+
"""
|
| 679 |
+
|
| 680 |
+
conn = get_connection()
|
| 681 |
+
|
| 682 |
+
if not conn:
|
| 683 |
+
logger.error("❌ No se pudo conectar a la base de datos")
|
| 684 |
+
return {
|
| 685 |
+
"success": False,
|
| 686 |
+
"error": "No se pudo conectar a la base de datos"
|
| 687 |
+
}
|
| 688 |
+
|
| 689 |
+
try:
|
| 690 |
+
logger.info(f"🔍 Buscando eventos funel - Usuario: {usuario}")
|
| 691 |
+
cursor = conn.cursor()
|
| 692 |
+
|
| 693 |
+
cursor.execute(
|
| 694 |
+
"SELECT * FROM `funel_compra` WHERE usuario = %s ORDER BY created_at DESC",
|
| 695 |
+
(usuario,)
|
| 696 |
+
)
|
| 697 |
+
|
| 698 |
+
eventos = cursor.fetchall()
|
| 699 |
+
cursor.close()
|
| 700 |
+
conn.close()
|
| 701 |
+
|
| 702 |
+
datos = []
|
| 703 |
+
for evento in eventos:
|
| 704 |
+
datos.append({
|
| 705 |
+
"id": evento[0],
|
| 706 |
+
"usuario": evento[1],
|
| 707 |
+
"mail": evento[2],
|
| 708 |
+
"accion": evento[3],
|
| 709 |
+
"created_at": evento[4]
|
| 710 |
+
})
|
| 711 |
+
|
| 712 |
+
logger.info(f"✅ Se obtuvieron {len(datos)} eventos para el usuario: {usuario}")
|
| 713 |
+
|
| 714 |
+
return {
|
| 715 |
+
"success": True,
|
| 716 |
+
"data": datos,
|
| 717 |
+
"total": len(datos)
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
except Exception as e:
|
| 721 |
+
logger.error(f"❌ Error al obtener eventos funel: {str(e)}")
|
| 722 |
+
return {
|
| 723 |
+
"success": False,
|
| 724 |
+
"error": f"Error al obtener eventos: {str(e)}"
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
def obtener_todos_eventos_funel(limit: int = 10, offset: int = 0) -> dict:
|
| 729 |
+
"""
|
| 730 |
+
Obtiene todos los eventos del funel con paginación.
|
| 731 |
+
|
| 732 |
+
Args:
|
| 733 |
+
limit: Cantidad máxima de registros
|
| 734 |
+
offset: Desplazamiento para paginación
|
| 735 |
+
|
| 736 |
+
Returns:
|
| 737 |
+
dict: Lista de eventos paginada o error
|
| 738 |
+
"""
|
| 739 |
+
|
| 740 |
+
conn = get_connection()
|
| 741 |
+
|
| 742 |
+
if not conn:
|
| 743 |
+
logger.error("❌ No se pudo conectar a la base de datos")
|
| 744 |
+
return {
|
| 745 |
+
"success": False,
|
| 746 |
+
"error": "No se pudo conectar a la base de datos"
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
try:
|
| 750 |
+
logger.info(f"📚 Obteniendo todos los eventos funel - Limit: {limit}, Offset: {offset}")
|
| 751 |
+
cursor = conn.cursor()
|
| 752 |
+
|
| 753 |
+
# Obtener total
|
| 754 |
+
cursor.execute("SELECT COUNT(*) FROM `funel_compra`")
|
| 755 |
+
total = cursor.fetchone()[0]
|
| 756 |
+
logger.info(f"📊 Total de eventos: {total}")
|
| 757 |
+
|
| 758 |
+
# Obtener con paginación
|
| 759 |
+
cursor.execute(
|
| 760 |
+
"SELECT * FROM `funel_compra` ORDER BY created_at DESC LIMIT %s OFFSET %s",
|
| 761 |
+
(limit, offset)
|
| 762 |
+
)
|
| 763 |
+
|
| 764 |
+
eventos = cursor.fetchall()
|
| 765 |
+
logger.info(f"✅ Se obtuvieron {len(eventos)} eventos")
|
| 766 |
+
cursor.close()
|
| 767 |
+
conn.close()
|
| 768 |
+
|
| 769 |
+
datos = []
|
| 770 |
+
for evento in eventos:
|
| 771 |
+
datos.append({
|
| 772 |
+
"id": evento[0],
|
| 773 |
+
"usuario": evento[1],
|
| 774 |
+
"mail": evento[2],
|
| 775 |
+
"accion": evento[3],
|
| 776 |
+
"created_at": evento[4]
|
| 777 |
+
})
|
| 778 |
+
|
| 779 |
+
return {
|
| 780 |
+
"success": True,
|
| 781 |
+
"data": datos,
|
| 782 |
+
"total": total,
|
| 783 |
+
"limit": limit,
|
| 784 |
+
"offset": offset
|
| 785 |
+
}
|
| 786 |
+
|
| 787 |
+
except Exception as e:
|
| 788 |
+
logger.error(f"❌ Error al obtener eventos funel: {str(e)}")
|
| 789 |
+
return {
|
| 790 |
+
"success": False,
|
| 791 |
+
"error": f"Error al obtener eventos: {str(e)}"
|
| 792 |
+
}
|
imagen_log.sql
CHANGED
|
@@ -41,3 +41,15 @@ CREATE TABLE `actions` (
|
|
| 41 |
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
|
| 42 |
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
| 43 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
|
| 42 |
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
| 43 |
);
|
| 44 |
+
|
| 45 |
+
CREATE TABLE `funel_compra` (
|
| 46 |
+
`id` int AUTO_INCREMENT PRIMARY KEY,
|
| 47 |
+
`usuario` varchar(255) NOT NULL,
|
| 48 |
+
`mail` varchar(255) NOT NULL,
|
| 49 |
+
`accion` varchar(255) NOT NULL,
|
| 50 |
+
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
| 51 |
+
INDEX idx_usuario (usuario),
|
| 52 |
+
INDEX idx_mail (mail),
|
| 53 |
+
INDEX idx_accion (accion),
|
| 54 |
+
INDEX idx_created_at (created_at)
|
| 55 |
+
);
|
main.py
CHANGED
|
@@ -5,8 +5,8 @@ Aplicación FastAPI - Endpoints
|
|
| 5 |
import logging
|
| 6 |
from fastapi import FastAPI, HTTPException, Query
|
| 7 |
from fastapi.responses import JSONResponse
|
| 8 |
-
from models import RegistroRequest, RegistroResponse, CalificacionRequest, ActionRequest, ActionResponse
|
| 9 |
-
from funciones import crear_registro, obtener_registros, actualizar_calificacion, crear_o_actualizar_action, obtener_action, obtener_todas_acciones
|
| 10 |
from datetime import datetime
|
| 11 |
|
| 12 |
# Configurar logging
|
|
@@ -277,10 +277,82 @@ async def obtener_acciones(
|
|
| 277 |
)
|
| 278 |
|
| 279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
|
| 286 |
if __name__ == "__main__":
|
|
|
|
| 5 |
import logging
|
| 6 |
from fastapi import FastAPI, HTTPException, Query
|
| 7 |
from fastapi.responses import JSONResponse
|
| 8 |
+
from models import RegistroRequest, RegistroResponse, CalificacionRequest, ActionRequest, ActionResponse, FunelCompraRequest, FunelCompraResponse
|
| 9 |
+
from funciones import crear_registro, obtener_registros, actualizar_calificacion, crear_o_actualizar_action, obtener_action, obtener_todas_acciones, crear_evento_funel, obtener_eventos_funel, obtener_todos_eventos_funel
|
| 10 |
from datetime import datetime
|
| 11 |
|
| 12 |
# Configurar logging
|
|
|
|
| 277 |
)
|
| 278 |
|
| 279 |
|
| 280 |
+
@app.post("/funel-compra", response_model=dict, tags=["Funel Compra"])
|
| 281 |
+
async def registrar_funel_compra(datos: FunelCompraRequest):
|
| 282 |
+
"""
|
| 283 |
+
Registra un evento en el funel de compra.
|
| 284 |
+
|
| 285 |
+
Acciones típicas: inicio, visualizacion, intento_compra, compra_exitosa, abandono
|
| 286 |
+
"""
|
| 287 |
+
logger.info(f"📊 POST /funel-compra - Usuario: {datos.usuario}, Mail: {datos.mail}, Acción: {datos.accion}")
|
| 288 |
+
|
| 289 |
+
resultado = crear_evento_funel(datos)
|
| 290 |
+
|
| 291 |
+
if resultado["success"]:
|
| 292 |
+
logger.info(f"✅ Evento registrado: {resultado['data']}")
|
| 293 |
+
return {
|
| 294 |
+
"success": True,
|
| 295 |
+
"data": resultado["data"]
|
| 296 |
+
}
|
| 297 |
+
else:
|
| 298 |
+
logger.error(f"❌ Error al registrar evento: {resultado['error']}")
|
| 299 |
+
raise HTTPException(
|
| 300 |
+
status_code=400,
|
| 301 |
+
detail=resultado["error"]
|
| 302 |
+
)
|
| 303 |
|
| 304 |
+
|
| 305 |
+
@app.get("/funel-compra/{usuario}", response_model=dict, tags=["Funel Compra"])
|
| 306 |
+
async def obtener_funel_usuario(usuario: str):
|
| 307 |
+
"""
|
| 308 |
+
Obtiene todos los eventos del funel de compra para un usuario específico.
|
| 309 |
+
"""
|
| 310 |
+
logger.info(f"🔍 GET /funel-compra/{usuario}")
|
| 311 |
+
|
| 312 |
+
resultado = obtener_eventos_funel(usuario)
|
| 313 |
+
|
| 314 |
+
if resultado["success"]:
|
| 315 |
+
logger.info(f"✅ Se obtuvieron {resultado['total']} eventos para {usuario}")
|
| 316 |
+
return {
|
| 317 |
+
"success": True,
|
| 318 |
+
"data": resultado["data"],
|
| 319 |
+
"total": resultado["total"]
|
| 320 |
+
}
|
| 321 |
+
else:
|
| 322 |
+
logger.error(f"❌ Error al obtener eventos: {resultado['error']}")
|
| 323 |
+
raise HTTPException(
|
| 324 |
+
status_code=500,
|
| 325 |
+
detail=resultado["error"]
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
@app.get("/funel-compra", response_model=dict, tags=["Funel Compra"])
|
| 330 |
+
async def listar_funel_compra(limit: int = Query(10, ge=1, le=100), offset: int = Query(0, ge=0)):
|
| 331 |
+
"""
|
| 332 |
+
Obtiene todos los eventos del funel de compra con paginación.
|
| 333 |
+
"""
|
| 334 |
+
logger.info(f"📚 GET /funel-compra - Limit: {limit}, Offset: {offset}")
|
| 335 |
+
|
| 336 |
+
resultado = obtener_todos_eventos_funel(limit, offset)
|
| 337 |
+
|
| 338 |
+
if resultado["success"]:
|
| 339 |
+
logger.info(f"✅ Se obtuvieron {len(resultado['data'])} eventos (Total: {resultado['total']})")
|
| 340 |
+
return {
|
| 341 |
+
"success": True,
|
| 342 |
+
"data": resultado["data"],
|
| 343 |
+
"pagination": {
|
| 344 |
+
"total": resultado["total"],
|
| 345 |
+
"limit": limit,
|
| 346 |
+
"offset": offset,
|
| 347 |
+
"pages": (resultado["total"] + limit - 1) // limit
|
| 348 |
+
}
|
| 349 |
+
}
|
| 350 |
+
else:
|
| 351 |
+
logger.error(f"❌ Error al obtener eventos: {resultado['error']}")
|
| 352 |
+
raise HTTPException(
|
| 353 |
+
status_code=500,
|
| 354 |
+
detail=resultado["error"]
|
| 355 |
+
)
|
| 356 |
|
| 357 |
|
| 358 |
if __name__ == "__main__":
|
models.py
CHANGED
|
@@ -160,3 +160,35 @@ class ActionResponse(BaseModel):
|
|
| 160 |
"created_at": "2025-01-13T10:30:00",
|
| 161 |
"updated_at": "2025-02-06T15:45:00"
|
| 162 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
"created_at": "2025-01-13T10:30:00",
|
| 161 |
"updated_at": "2025-02-06T15:45:00"
|
| 162 |
}
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class FunelCompraRequest(BaseModel):
|
| 166 |
+
"""Modelo para crear/registrar un evento en el funel de compra"""
|
| 167 |
+
usuario: str = Field(..., max_length=255, description="Nombre de usuario")
|
| 168 |
+
mail: str = Field(..., description="Email del usuario")
|
| 169 |
+
accion: str = Field(..., max_length=255, description="Acción realizada en el funel (ej: inicio, visualizacion, intento_compra, compra_exitosa)")
|
| 170 |
+
|
| 171 |
+
class Config:
|
| 172 |
+
example = {
|
| 173 |
+
"usuario": "moibe",
|
| 174 |
+
"mail": "moi.estrello@gmail.com",
|
| 175 |
+
"accion": "compra_exitosa"
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
class FunelCompraResponse(BaseModel):
|
| 180 |
+
"""Modelo para respuesta de registros del funel de compra"""
|
| 181 |
+
id: int = Field(..., description="ID único del registro")
|
| 182 |
+
usuario: str
|
| 183 |
+
mail: str
|
| 184 |
+
accion: str
|
| 185 |
+
created_at: datetime
|
| 186 |
+
|
| 187 |
+
class Config:
|
| 188 |
+
example = {
|
| 189 |
+
"id": 1,
|
| 190 |
+
"usuario": "moibe",
|
| 191 |
+
"mail": "moi.estrello@gmail.com",
|
| 192 |
+
"accion": "compra_exitosa",
|
| 193 |
+
"created_at": "2026-02-11T10:30:00"
|
| 194 |
+
}
|
test_funel_compra.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Script para probar los endpoints del funel de compra"""
|
| 2 |
+
|
| 3 |
+
import requests
|
| 4 |
+
import json
|
| 5 |
+
|
| 6 |
+
BASE_URL = "http://localhost:8000"
|
| 7 |
+
|
| 8 |
+
# Test 1: Registrar un evento
|
| 9 |
+
print("=" * 80)
|
| 10 |
+
print("TEST 1: Registrar evento en funel de compra")
|
| 11 |
+
print("=" * 80)
|
| 12 |
+
|
| 13 |
+
evento1 = {
|
| 14 |
+
"usuario": "moibe",
|
| 15 |
+
"mail": "moi.estrello@gmail.com",
|
| 16 |
+
"accion": "inicio"
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
response = requests.post(f"{BASE_URL}/funel-compra", json=evento1)
|
| 20 |
+
print(f"Status Code: {response.status_code}")
|
| 21 |
+
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
| 22 |
+
|
| 23 |
+
# Test 2: Registrar más eventos para el mismo usuario
|
| 24 |
+
print("\n" + "=" * 80)
|
| 25 |
+
print("TEST 2: Registrar más eventos")
|
| 26 |
+
print("=" * 80)
|
| 27 |
+
|
| 28 |
+
eventos = [
|
| 29 |
+
{"usuario": "moibe", "mail": "moi.estrello@gmail.com", "accion": "visualizacion"},
|
| 30 |
+
{"usuario": "moibe", "mail": "moi.estrello@gmail.com", "accion": "intento_compra"},
|
| 31 |
+
{"usuario": "moibe", "mail": "moi.estrello@gmail.com", "accion": "compra_exitosa"},
|
| 32 |
+
{"usuario": "juan_perez", "mail": "juan@example.com", "accion": "inicio"},
|
| 33 |
+
{"usuario": "juan_perez", "mail": "juan@example.com", "accion": "visualizacion"},
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
for evento in eventos:
|
| 37 |
+
response = requests.post(f"{BASE_URL}/funel-compra", json=evento)
|
| 38 |
+
print(f"✅ {evento['accion']} para {evento['usuario']}: Status {response.status_code}")
|
| 39 |
+
|
| 40 |
+
# Test 3: Obtener eventos de un usuario
|
| 41 |
+
print("\n" + "=" * 80)
|
| 42 |
+
print("TEST 3: Obtener eventos para un usuario")
|
| 43 |
+
print("=" * 80)
|
| 44 |
+
|
| 45 |
+
response = requests.get(f"{BASE_URL}/funel-compra/moibe")
|
| 46 |
+
print(f"Status Code: {response.status_code}")
|
| 47 |
+
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
| 48 |
+
|
| 49 |
+
# Test 4: Obtener todos los eventos
|
| 50 |
+
print("\n" + "=" * 80)
|
| 51 |
+
print("TEST 4: Obtener todos los eventos (paginación)")
|
| 52 |
+
print("=" * 80)
|
| 53 |
+
|
| 54 |
+
response = requests.get(f"{BASE_URL}/funel-compra?limit=5&offset=0")
|
| 55 |
+
print(f"Status Code: {response.status_code}")
|
| 56 |
+
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
| 57 |
+
|
| 58 |
+
print("\n" + "=" * 80)
|
| 59 |
+
print("✅ PRUEBAS COMPLETADAS")
|
| 60 |
+
print("=" * 80)
|
test_funel_simple.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test rápido de funciones del funel de compra"""
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
from models import FunelCompraRequest
|
| 5 |
+
from funciones import crear_evento_funel, obtener_eventos_funel, obtener_todos_eventos_funel
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
print("\n" + "=" * 80)
|
| 11 |
+
print("TEST 1: Crear evento funel")
|
| 12 |
+
print("=" * 80)
|
| 13 |
+
|
| 14 |
+
evento1 = FunelCompraRequest(
|
| 15 |
+
usuario="moibe",
|
| 16 |
+
mail="moi.estrello@gmail.com",
|
| 17 |
+
accion="inicio"
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
resultado = crear_evento_funel(evento1)
|
| 21 |
+
print(f"Resultado: {resultado}\n")
|
| 22 |
+
|
| 23 |
+
print("=" * 80)
|
| 24 |
+
print("TEST 2: Crear más eventos")
|
| 25 |
+
print("=" * 80)
|
| 26 |
+
|
| 27 |
+
eventos_data = [
|
| 28 |
+
("moibe", "moi.estrello@gmail.com", "visualizacion"),
|
| 29 |
+
("moibe", "moi.estrello@gmail.com", "intento_compra"),
|
| 30 |
+
("moibe", "moi.estrello@gmail.com", "compra_exitosa"),
|
| 31 |
+
("juan_perez", "juan@example.com", "inicio"),
|
| 32 |
+
("juan_perez", "juan@example.com", "visualizacion"),
|
| 33 |
+
("juan_perez", "juan@example.com", "abandono"),
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
for usuario, mail, accion in eventos_data:
|
| 37 |
+
evento = FunelCompraRequest(usuario=usuario, mail=mail, accion=accion)
|
| 38 |
+
resultado = crear_evento_funel(evento)
|
| 39 |
+
if resultado['success']:
|
| 40 |
+
print(f"✅ Evento creado: {accion} para {usuario}")
|
| 41 |
+
else:
|
| 42 |
+
print(f"❌ Error: {resultado['error']}")
|
| 43 |
+
|
| 44 |
+
print("\n" + "=" * 80)
|
| 45 |
+
print("TEST 3: Obtener eventos de un usuario")
|
| 46 |
+
print("=" * 80)
|
| 47 |
+
|
| 48 |
+
resultado = obtener_eventos_funel("moibe")
|
| 49 |
+
print(f"Total eventos para moibe: {resultado['total']}")
|
| 50 |
+
for evento in resultado['data']:
|
| 51 |
+
print(f" - {evento['accion']} ({evento['created_at']})")
|
| 52 |
+
|
| 53 |
+
print("\n" + "=" * 80)
|
| 54 |
+
print("TEST 4: Obtener todos los eventos (paginación)")
|
| 55 |
+
print("=" * 80)
|
| 56 |
+
|
| 57 |
+
resultado = obtener_todos_eventos_funel(limit=5, offset=0)
|
| 58 |
+
print(f"Total eventos: {resultado['total']}")
|
| 59 |
+
print(f"Mostrando: {len(resultado['data'])} de {resultado['total']}")
|
| 60 |
+
for evento in resultado['data']:
|
| 61 |
+
print(f" - {evento['usuario']}: {evento['accion']}")
|
| 62 |
+
|
| 63 |
+
print("\n" + "=" * 80)
|
| 64 |
+
print("✅ TODOS LOS TESTS COMPLETADOS")
|
| 65 |
+
print("=" * 80 + "\n")
|
test_output.txt
ADDED
|
Binary file (3.8 kB). View file
|
|
|