Base code

This commit is contained in:
Kunthawat Greethong
2026-01-08 22:39:53 +07:00
parent 697115c61a
commit c35fa52117
2169 changed files with 626670 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
"""
Constants for Content Planning API
Centralized constants and business rules extracted from the main content_planning.py file.
"""
from fastapi import status
# API Endpoints
API_PREFIX = "/api/content-planning"
API_TAGS = ["content-planning"]
# HTTP Status Codes
HTTP_STATUS_CODES = {
"OK": status.HTTP_200_OK,
"CREATED": status.HTTP_201_CREATED,
"NO_CONTENT": status.HTTP_204_NO_CONTENT,
"BAD_REQUEST": status.HTTP_400_BAD_REQUEST,
"UNAUTHORIZED": status.HTTP_401_UNAUTHORIZED,
"FORBIDDEN": status.HTTP_403_FORBIDDEN,
"NOT_FOUND": status.HTTP_404_NOT_FOUND,
"CONFLICT": status.HTTP_409_CONFLICT,
"UNPROCESSABLE_ENTITY": status.HTTP_422_UNPROCESSABLE_ENTITY,
"INTERNAL_SERVER_ERROR": status.HTTP_500_INTERNAL_SERVER_ERROR,
"SERVICE_UNAVAILABLE": status.HTTP_503_SERVICE_UNAVAILABLE
}
# Error Messages
ERROR_MESSAGES = {
"strategy_not_found": "Content strategy not found",
"calendar_event_not_found": "Calendar event not found",
"gap_analysis_not_found": "Content gap analysis not found",
"user_not_found": "User not found",
"invalid_request": "Invalid request data",
"database_connection": "Database connection failed",
"ai_service_unavailable": "AI service is currently unavailable",
"validation_failed": "Request validation failed",
"permission_denied": "Permission denied",
"rate_limit_exceeded": "Rate limit exceeded",
"internal_server_error": "Internal server error",
"service_unavailable": "Service temporarily unavailable"
}
# Success Messages
SUCCESS_MESSAGES = {
"strategy_created": "Content strategy created successfully",
"strategy_updated": "Content strategy updated successfully",
"strategy_deleted": "Content strategy deleted successfully",
"calendar_event_created": "Calendar event created successfully",
"calendar_event_updated": "Calendar event updated successfully",
"calendar_event_deleted": "Calendar event deleted successfully",
"gap_analysis_created": "Content gap analysis created successfully",
"gap_analysis_completed": "Content gap analysis completed successfully",
"ai_analytics_generated": "AI analytics generated successfully",
"calendar_generated": "Calendar generated successfully",
"content_optimized": "Content optimized successfully",
"performance_predicted": "Performance prediction completed successfully"
}
# Business Rules
BUSINESS_RULES = {
"max_strategies_per_user": 10,
"max_calendar_events_per_strategy": 100,
"max_gap_analyses_per_user": 5,
"max_ai_analytics_per_user": 20,
"default_page_size": 10,
"max_page_size": 100,
"cache_duration_hours": 24,
"max_processing_time_seconds": 30,
"min_confidence_score": 0.7,
"max_competitor_urls": 10,
"max_target_keywords": 50
}
# Content Types
CONTENT_TYPES = [
"blog_post",
"social_media_post",
"video",
"infographic",
"case_study",
"whitepaper",
"newsletter",
"webinar",
"podcast",
"live_stream"
]
# Platforms
PLATFORMS = [
"linkedin",
"twitter",
"facebook",
"instagram",
"youtube",
"tiktok",
"website",
"email",
"medium",
"quora"
]
# Industries
INDUSTRIES = [
"technology",
"healthcare",
"finance",
"education",
"retail",
"manufacturing",
"consulting",
"real_estate",
"legal",
"non_profit"
]
# Business Sizes
BUSINESS_SIZES = [
"startup",
"sme",
"enterprise"
]
# Calendar Types
CALENDAR_TYPES = [
"monthly",
"weekly",
"custom"
]
# Time Periods
TIME_PERIODS = [
"7d",
"30d",
"90d",
"1y"
]
# AI Service Status
AI_SERVICE_STATUS = {
"operational": "operational",
"degraded": "degraded",
"unavailable": "unavailable",
"fallback": "fallback"
}
# Data Sources
DATA_SOURCES = {
"ai_analysis": "ai_analysis",
"database_cache": "database_cache",
"fallback": "fallback"
}
# Priority Levels
PRIORITY_LEVELS = [
"high",
"medium",
"low"
]
# Content Pillars
DEFAULT_CONTENT_PILLARS = [
"Educational Content",
"Thought Leadership",
"Product Updates",
"Industry Insights",
"Customer Stories",
"Behind the Scenes"
]
# Performance Metrics
PERFORMANCE_METRICS = [
"engagement_rate",
"reach",
"conversion_rate",
"click_through_rate",
"time_on_page",
"bounce_rate",
"social_shares",
"comments",
"likes"
]
# Validation Rules
VALIDATION_RULES = {
"min_title_length": 3,
"max_title_length": 100,
"min_description_length": 10,
"max_description_length": 1000,
"min_url_length": 10,
"max_url_length": 500,
"min_keyword_length": 2,
"max_keyword_length": 50
}
# Logging Levels
LOGGING_LEVELS = {
"debug": "DEBUG",
"info": "INFO",
"warning": "WARNING",
"error": "ERROR",
"critical": "CRITICAL"
}
# Cache Keys
CACHE_KEYS = {
"strategies": "content_planning:strategies",
"calendar_events": "content_planning:calendar_events",
"gap_analyses": "content_planning:gap_analyses",
"ai_analytics": "content_planning:ai_analytics",
"calendar_generation": "content_planning:calendar_generation"
}
# API Rate Limits
RATE_LIMITS = {
"strategies_per_minute": 10,
"calendar_events_per_minute": 20,
"gap_analyses_per_hour": 5,
"ai_analytics_per_hour": 10,
"calendar_generation_per_hour": 3
}

View File

@@ -0,0 +1,152 @@
"""
Centralized Error Handlers for Content Planning Module
Standardized error handling patterns extracted from the main content planning file.
"""
from typing import Dict, Any, Optional
from fastapi import HTTPException, status
from loguru import logger
import traceback
class ContentPlanningErrorHandler:
"""Centralized error handling for content planning operations."""
@staticmethod
def handle_database_error(error: Exception, operation: str) -> HTTPException:
"""Handle database-related errors."""
logger.error(f"Database error during {operation}: {str(error)}")
logger.error(f"Traceback: {traceback.format_exc()}")
return HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Database operation failed during {operation}: {str(error)}"
)
@staticmethod
def handle_validation_error(error: Exception, field: str) -> HTTPException:
"""Handle validation errors."""
logger.error(f"Validation error for field '{field}': {str(error)}")
return HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=f"Validation error for {field}: {str(error)}"
)
@staticmethod
def handle_not_found_error(resource_type: str, resource_id: Any) -> HTTPException:
"""Handle resource not found errors."""
logger.warning(f"{resource_type} not found: {resource_id}")
return HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"{resource_type} with id {resource_id} not found"
)
@staticmethod
def handle_ai_service_error(error: Exception, service: str) -> HTTPException:
"""Handle AI service errors."""
logger.error(f"AI service error in {service}: {str(error)}")
return HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"AI service {service} is currently unavailable: {str(error)}"
)
@staticmethod
def handle_api_key_error(missing_keys: list) -> HTTPException:
"""Handle API key configuration errors."""
logger.error(f"Missing API keys: {missing_keys}")
return HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"AI services are not properly configured. Missing keys: {', '.join(missing_keys)}"
)
@staticmethod
def handle_general_error(error: Exception, operation: str) -> HTTPException:
"""Handle general errors."""
logger.error(f"General error during {operation}: {str(error)}")
logger.error(f"Exception type: {type(error)}")
logger.error(f"Traceback: {traceback.format_exc()}")
return HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error during {operation}: {str(error)}"
)
@staticmethod
def create_error_response(
status_code: int,
message: str,
error_type: str = "general",
details: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Create standardized error response."""
error_response = {
"status": "error",
"error_type": error_type,
"message": message,
"status_code": status_code,
"timestamp": "2024-08-01T10:00:00Z" # This should be dynamic
}
if details:
error_response["details"] = details
return error_response
# Common error messages
ERROR_MESSAGES = {
"strategy_not_found": "Content strategy not found",
"calendar_event_not_found": "Calendar event not found",
"gap_analysis_not_found": "Content gap analysis not found",
"user_not_found": "User not found",
"invalid_request": "Invalid request data",
"database_connection": "Database connection failed",
"ai_service_unavailable": "AI service is currently unavailable",
"validation_failed": "Request validation failed",
"permission_denied": "Permission denied",
"rate_limit_exceeded": "Rate limit exceeded",
"internal_server_error": "Internal server error",
"service_unavailable": "Service temporarily unavailable"
}
# Error status codes mapping
ERROR_STATUS_CODES = {
"not_found": status.HTTP_404_NOT_FOUND,
"validation_error": status.HTTP_422_UNPROCESSABLE_ENTITY,
"bad_request": status.HTTP_400_BAD_REQUEST,
"unauthorized": status.HTTP_401_UNAUTHORIZED,
"forbidden": status.HTTP_403_FORBIDDEN,
"not_found": status.HTTP_404_NOT_FOUND,
"conflict": status.HTTP_409_CONFLICT,
"internal_error": status.HTTP_500_INTERNAL_SERVER_ERROR,
"service_unavailable": status.HTTP_503_SERVICE_UNAVAILABLE
}
def log_error(error: Exception, context: str, user_id: Optional[int] = None):
"""Log error with context information."""
logger.error(f"Error in {context}: {str(error)}")
if user_id:
logger.error(f"User ID: {user_id}")
logger.error(f"Exception type: {type(error)}")
logger.error(f"Traceback: {traceback.format_exc()}")
def create_http_exception(
error_type: str,
message: str,
status_code: Optional[int] = None,
details: Optional[Dict[str, Any]] = None
) -> HTTPException:
"""Create HTTP exception with standardized error handling."""
if status_code is None:
status_code = ERROR_STATUS_CODES.get(error_type, status.HTTP_500_INTERNAL_SERVER_ERROR)
logger.error(f"HTTP Exception: {error_type} - {message}")
if details:
logger.error(f"Error details: {details}")
return HTTPException(
status_code=status_code,
detail=message
)

View File

@@ -0,0 +1,193 @@
"""
Response Builders for Content Planning API
Standardized response formatting utilities extracted from the main content_planning.py file.
"""
from typing import Dict, Any, List, Optional
from datetime import datetime
from fastapi import status
import json
class ResponseBuilder:
"""Standardized response building utilities."""
@staticmethod
def create_success_response(
data: Any,
message: str = "Operation completed successfully",
status_code: int = 200
) -> Dict[str, Any]:
"""Create a standardized success response."""
return {
"status": "success",
"message": message,
"data": data,
"status_code": status_code,
"timestamp": datetime.utcnow().isoformat()
}
@staticmethod
def create_error_response(
message: str,
error_type: str = "general",
status_code: int = 500,
details: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Create a standardized error response."""
response = {
"status": "error",
"error_type": error_type,
"message": message,
"status_code": status_code,
"timestamp": datetime.utcnow().isoformat()
}
if details:
response["details"] = details
return response
@staticmethod
def create_paginated_response(
data: List[Any],
total_count: int,
page: int = 1,
page_size: int = 10,
message: str = "Data retrieved successfully"
) -> Dict[str, Any]:
"""Create a standardized paginated response."""
return {
"status": "success",
"message": message,
"data": data,
"pagination": {
"total_count": total_count,
"page": page,
"page_size": page_size,
"total_pages": (total_count + page_size - 1) // page_size
},
"timestamp": datetime.utcnow().isoformat()
}
@staticmethod
def create_health_response(
service_name: str,
status: str,
services: Dict[str, Any],
timestamp: Optional[datetime] = None
) -> Dict[str, Any]:
"""Create a standardized health check response."""
return {
"service": service_name,
"status": status,
"timestamp": (timestamp or datetime.utcnow()).isoformat(),
"services": services
}
@staticmethod
def create_ai_analytics_response(
insights: List[Dict[str, Any]],
recommendations: List[Dict[str, Any]],
total_insights: int,
total_recommendations: int,
generated_at: datetime,
ai_service_status: str = "operational",
processing_time: Optional[float] = None,
personalized_data_used: bool = True,
data_source: str = "ai_analysis"
) -> Dict[str, Any]:
"""Create a standardized AI analytics response."""
response = {
"insights": insights,
"recommendations": recommendations,
"total_insights": total_insights,
"total_recommendations": total_recommendations,
"generated_at": generated_at.isoformat(),
"ai_service_status": ai_service_status,
"personalized_data_used": personalized_data_used,
"data_source": data_source
}
if processing_time is not None:
response["processing_time"] = f"{processing_time:.2f}s"
return response
@staticmethod
def create_gap_analysis_response(
gap_analyses: List[Dict[str, Any]],
total_gaps: int,
generated_at: datetime,
ai_service_status: str = "operational",
personalized_data_used: bool = True,
data_source: str = "ai_analysis"
) -> Dict[str, Any]:
"""Create a standardized gap analysis response."""
return {
"gap_analyses": gap_analyses,
"total_gaps": total_gaps,
"generated_at": generated_at.isoformat(),
"ai_service_status": ai_service_status,
"personalized_data_used": personalized_data_used,
"data_source": data_source
}
@staticmethod
def create_strategy_response(
strategies: List[Dict[str, Any]],
total_count: int,
user_id: Optional[int] = None,
analysis_date: Optional[datetime] = None
) -> Dict[str, Any]:
"""Create a standardized strategy response."""
response = {
"status": "success",
"message": "Content strategy retrieved successfully",
"data": {
"strategies": strategies,
"total_count": total_count
}
}
if user_id is not None:
response["data"]["user_id"] = user_id
if analysis_date is not None:
response["data"]["analysis_date"] = analysis_date.isoformat()
return response
# Common response patterns
RESPONSE_PATTERNS = {
"success": {
"status": "success",
"message": "Operation completed successfully"
},
"error": {
"status": "error",
"message": "Operation failed"
},
"not_found": {
"status": "error",
"message": "Resource not found"
},
"validation_error": {
"status": "error",
"message": "Validation failed"
}
}
# Response status codes
RESPONSE_STATUS_CODES = {
"success": 200,
"created": 201,
"no_content": 204,
"bad_request": 400,
"unauthorized": 401,
"forbidden": 403,
"not_found": 404,
"conflict": 409,
"unprocessable_entity": 422,
"internal_error": 500,
"service_unavailable": 503
}