Improve coverage
This commit is contained in:
@@ -16,6 +16,7 @@ from .services import (
|
||||
QuickReplyContentService,
|
||||
)
|
||||
from .services.firestore_service import FirestoreService
|
||||
from .services.quick_reply_session_service import QuickReplySessionService
|
||||
from .services.redis_service import RedisService
|
||||
|
||||
|
||||
@@ -43,6 +44,16 @@ def get_quick_reply_content_service() -> QuickReplyContentService:
|
||||
return QuickReplyContentService(settings)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_quick_reply_session_service() -> QuickReplySessionService:
|
||||
"""Get quick reply session service instance."""
|
||||
return QuickReplySessionService(
|
||||
redis_service=get_redis_service(),
|
||||
firestore_service=get_firestore_service(),
|
||||
quick_reply_content_service=get_quick_reply_content_service(),
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_notification_manager() -> NotificationManagerService:
|
||||
"""Get notification manager instance."""
|
||||
|
||||
@@ -12,6 +12,8 @@ class User(BaseModel):
|
||||
telefono: str = Field(..., min_length=1)
|
||||
nickname: str | None = None
|
||||
|
||||
model_config = {"extra": "ignore"}
|
||||
|
||||
|
||||
class QueryResult(BaseModel):
|
||||
"""Query result from Dialogflow."""
|
||||
@@ -19,7 +21,7 @@ class QueryResult(BaseModel):
|
||||
response_text: str | None = Field(None, alias="responseText")
|
||||
parameters: dict[str, Any] | None = Field(None, alias="parameters")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
model_config = {"populate_by_name": True, "extra": "ignore"}
|
||||
|
||||
|
||||
class DetectIntentResponse(BaseModel):
|
||||
@@ -29,7 +31,7 @@ class DetectIntentResponse(BaseModel):
|
||||
query_result: QueryResult | None = Field(None, alias="queryResult")
|
||||
quick_replies: Any | None = None # QuickReplyScreen from quick_replies module
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
model_config = {"populate_by_name": True, "extra": "ignore"}
|
||||
|
||||
|
||||
class ConversationRequest(BaseModel):
|
||||
@@ -40,7 +42,7 @@ class ConversationRequest(BaseModel):
|
||||
canal: str = Field(..., alias="canal")
|
||||
pantalla_contexto: str | None = Field(None, alias="pantallaContexto")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
model_config = {"populate_by_name": True, "extra": "ignore"}
|
||||
|
||||
|
||||
class ConversationEntry(BaseModel):
|
||||
@@ -55,7 +57,7 @@ class ConversationEntry(BaseModel):
|
||||
parameters: dict[str, Any] | None = Field(None, alias="parameters")
|
||||
canal: str | None = Field(None, alias="canal")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
model_config = {"populate_by_name": True, "extra": "ignore"}
|
||||
|
||||
|
||||
class ConversationSession(BaseModel):
|
||||
@@ -73,7 +75,7 @@ class ConversationSession(BaseModel):
|
||||
last_message: str | None = Field(None, alias="lastMessage")
|
||||
pantalla_contexto: str | None = Field(None, alias="pantallaContexto")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
model_config = {"populate_by_name": True, "extra": "ignore"}
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
|
||||
@@ -2,20 +2,17 @@
|
||||
|
||||
import logging
|
||||
from typing import Annotated
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from capa_de_integracion.dependencies import (
|
||||
get_firestore_service,
|
||||
get_quick_reply_content_service,
|
||||
get_redis_service,
|
||||
get_quick_reply_session_service,
|
||||
)
|
||||
from capa_de_integracion.models.quick_replies import QuickReplyScreen
|
||||
from capa_de_integracion.services.firestore_service import FirestoreService
|
||||
from capa_de_integracion.services.quick_reply_content import QuickReplyContentService
|
||||
from capa_de_integracion.services.redis_service import RedisService
|
||||
from capa_de_integracion.services.quick_reply_session_service import (
|
||||
QuickReplySessionService,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/v1/quick-replies", tags=["quick-replies"])
|
||||
@@ -47,11 +44,9 @@ class QuickReplyScreenResponse(BaseModel):
|
||||
@router.post("/screen")
|
||||
async def start_quick_reply_session(
|
||||
request: QuickReplyScreenRequest,
|
||||
redis_service: Annotated[RedisService, Depends(get_redis_service)],
|
||||
firestore_service: Annotated[FirestoreService, Depends(get_firestore_service)],
|
||||
quick_reply_content_service: Annotated[QuickReplyContentService, Depends(
|
||||
get_quick_reply_content_service,
|
||||
)],
|
||||
quick_reply_session_service: Annotated[
|
||||
QuickReplySessionService, Depends(get_quick_reply_session_service),
|
||||
],
|
||||
) -> QuickReplyScreenResponse:
|
||||
"""Start a quick reply FAQ session for a specific screen.
|
||||
|
||||
@@ -60,52 +55,22 @@ async def start_quick_reply_session(
|
||||
|
||||
Args:
|
||||
request: Quick reply screen request
|
||||
redis_service: Redis service instance
|
||||
firestore_service: Firestore service instance
|
||||
quick_reply_content_service: Quick reply content service instance
|
||||
quick_reply_session_service: Quick reply session service instance
|
||||
|
||||
Returns:
|
||||
Detect intent response with quick reply questions
|
||||
Quick reply screen response with session ID and questions
|
||||
|
||||
"""
|
||||
def _validate_phone(phone: str) -> None:
|
||||
if not phone or not phone.strip():
|
||||
msg = "Phone number is required"
|
||||
raise ValueError(msg)
|
||||
|
||||
try:
|
||||
telefono = request.usuario.telefono
|
||||
pantalla_contexto = request.pantalla_contexto
|
||||
_validate_phone(telefono)
|
||||
|
||||
session = await firestore_service.get_session_by_phone(telefono)
|
||||
if session:
|
||||
session_id = session.session_id
|
||||
await firestore_service.update_pantalla_contexto(
|
||||
session_id, pantalla_contexto,
|
||||
)
|
||||
session.pantalla_contexto = pantalla_contexto
|
||||
else:
|
||||
session_id = str(uuid4())
|
||||
user_id = f"user_by_phone_{telefono.replace(' ', '').replace('-', '')}"
|
||||
session = await firestore_service.create_session(
|
||||
session_id, user_id, telefono, pantalla_contexto,
|
||||
)
|
||||
|
||||
# Cache session
|
||||
await redis_service.save_session(session)
|
||||
logger.info(
|
||||
"Created quick reply session %s for screen: %s",
|
||||
session_id,
|
||||
pantalla_contexto,
|
||||
result = await quick_reply_session_service.start_quick_reply_session(
|
||||
telefono=request.usuario.telefono,
|
||||
_nombre=request.usuario.nombre,
|
||||
pantalla_contexto=request.pantalla_contexto,
|
||||
)
|
||||
|
||||
# Load quick replies
|
||||
quick_replies = await quick_reply_content_service.get_quick_replies(
|
||||
pantalla_contexto,
|
||||
)
|
||||
return QuickReplyScreenResponse(
|
||||
responseId=session_id, quick_replies=quick_replies,
|
||||
responseId=result.session_id,
|
||||
quick_replies=result.quick_replies,
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
|
||||
@@ -4,10 +4,12 @@ from .conversation_manager import ConversationManagerService
|
||||
from .dlp_service import DLPService
|
||||
from .notification_manager import NotificationManagerService
|
||||
from .quick_reply_content import QuickReplyContentService
|
||||
from .quick_reply_session_service import QuickReplySessionService
|
||||
|
||||
__all__ = [
|
||||
"ConversationManagerService",
|
||||
"DLPService",
|
||||
"NotificationManagerService",
|
||||
"QuickReplyContentService",
|
||||
"QuickReplySessionService",
|
||||
]
|
||||
|
||||
121
src/capa_de_integracion/services/quick_reply_session_service.py
Normal file
121
src/capa_de_integracion/services/quick_reply_session_service.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Quick reply session service for managing FAQ sessions."""
|
||||
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
from capa_de_integracion.models.quick_replies import QuickReplyScreen
|
||||
from capa_de_integracion.services.firestore_service import FirestoreService
|
||||
from capa_de_integracion.services.quick_reply_content import QuickReplyContentService
|
||||
from capa_de_integracion.services.redis_service import RedisService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class QuickReplySessionResponse:
|
||||
"""Response from quick reply session service."""
|
||||
|
||||
def __init__(self, session_id: str, quick_replies: QuickReplyScreen) -> None:
|
||||
"""Initialize response.
|
||||
|
||||
Args:
|
||||
session_id: The session ID
|
||||
quick_replies: The quick reply screen data
|
||||
|
||||
"""
|
||||
self.session_id = session_id
|
||||
self.quick_replies = quick_replies
|
||||
|
||||
|
||||
class QuickReplySessionService:
|
||||
"""Service for managing quick reply FAQ sessions."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
redis_service: RedisService,
|
||||
firestore_service: FirestoreService,
|
||||
quick_reply_content_service: QuickReplyContentService,
|
||||
) -> None:
|
||||
"""Initialize quick reply session service.
|
||||
|
||||
Args:
|
||||
redis_service: Redis service instance
|
||||
firestore_service: Firestore service instance
|
||||
quick_reply_content_service: Quick reply content service instance
|
||||
|
||||
"""
|
||||
self.redis_service = redis_service
|
||||
self.firestore_service = firestore_service
|
||||
self.quick_reply_content_service = quick_reply_content_service
|
||||
|
||||
def _validate_phone(self, phone: str) -> None:
|
||||
"""Validate phone number.
|
||||
|
||||
Args:
|
||||
phone: Phone number to validate
|
||||
|
||||
Raises:
|
||||
ValueError: If phone is empty or invalid
|
||||
|
||||
"""
|
||||
if not phone or not phone.strip():
|
||||
msg = "Phone number is required"
|
||||
raise ValueError(msg)
|
||||
|
||||
async def start_quick_reply_session(
|
||||
self,
|
||||
telefono: str,
|
||||
_nombre: str,
|
||||
pantalla_contexto: str,
|
||||
) -> QuickReplySessionResponse:
|
||||
"""Start a quick reply FAQ session for a specific screen.
|
||||
|
||||
Creates or updates a conversation session with pantalla_contexto set,
|
||||
loads the quick reply questions for the screen, and returns them.
|
||||
|
||||
Args:
|
||||
telefono: User's phone number
|
||||
_nombre: User's name (currently unused but part of API contract)
|
||||
pantalla_contexto: Screen context identifier
|
||||
|
||||
Returns:
|
||||
Quick reply session response with session ID and quick replies
|
||||
|
||||
Raises:
|
||||
ValueError: If validation fails or data is invalid
|
||||
Exception: If there's an error creating session or loading content
|
||||
|
||||
"""
|
||||
self._validate_phone(telefono)
|
||||
|
||||
# Get or create session
|
||||
session = await self.firestore_service.get_session_by_phone(telefono)
|
||||
if session:
|
||||
session_id = session.session_id
|
||||
await self.firestore_service.update_pantalla_contexto(
|
||||
session_id, pantalla_contexto,
|
||||
)
|
||||
session.pantalla_contexto = pantalla_contexto
|
||||
else:
|
||||
session_id = str(uuid4())
|
||||
user_id = f"user_by_phone_{telefono.replace(' ', '').replace('-', '')}"
|
||||
session = await self.firestore_service.create_session(
|
||||
session_id, user_id, telefono, pantalla_contexto,
|
||||
)
|
||||
|
||||
# Cache session in Redis
|
||||
await self.redis_service.save_session(session)
|
||||
logger.info(
|
||||
"Created quick reply session %s for screen: %s",
|
||||
session_id,
|
||||
pantalla_contexto,
|
||||
)
|
||||
|
||||
# Load quick replies for the screen
|
||||
quick_replies = await self.quick_reply_content_service.get_quick_replies(
|
||||
pantalla_contexto,
|
||||
)
|
||||
|
||||
return QuickReplySessionResponse(
|
||||
session_id=session_id,
|
||||
quick_replies=quick_replies,
|
||||
)
|
||||
Reference in New Issue
Block a user