474 lines
16 KiB
Python
474 lines
16 KiB
Python
"""Enhanced Export API endpoints for Story 4.4."""
|
|
|
|
import asyncio
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import Dict, Any, List, Optional
|
|
from fastapi import APIRouter, HTTPException, Depends, BackgroundTasks, Query
|
|
from pydantic import BaseModel, Field
|
|
import uuid
|
|
|
|
from ..services.executive_summary_generator import ExecutiveSummaryGenerator
|
|
from ..services.timestamp_processor import TimestampProcessor
|
|
from ..services.enhanced_markdown_formatter import EnhancedMarkdownFormatter, MarkdownExportConfig
|
|
from ..services.enhanced_template_manager import EnhancedTemplateManager, DomainCategory, PromptTemplate
|
|
from ..core.dependencies import get_current_user
|
|
from ..models.user import User
|
|
from ..core.exceptions import ServiceError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/export", tags=["Enhanced Export"])
|
|
|
|
# Initialize services
|
|
executive_generator = ExecutiveSummaryGenerator()
|
|
timestamp_processor = TimestampProcessor()
|
|
markdown_formatter = EnhancedMarkdownFormatter(executive_generator, timestamp_processor)
|
|
template_manager = EnhancedTemplateManager()
|
|
|
|
|
|
# Request/Response Models
|
|
class EnhancedExportRequest(BaseModel):
|
|
"""Request model for enhanced export generation."""
|
|
summary_id: str
|
|
export_config: Optional[Dict[str, Any]] = None
|
|
template_id: Optional[str] = None
|
|
format: str = Field(default="markdown", description="Export format (markdown, pdf, html)")
|
|
include_executive_summary: bool = True
|
|
include_timestamps: bool = True
|
|
include_toc: bool = True
|
|
section_detail_level: str = Field(default="standard", description="brief, standard, detailed")
|
|
|
|
|
|
class ExportConfigResponse(BaseModel):
|
|
"""Available export configuration options."""
|
|
available_formats: List[str]
|
|
section_detail_levels: List[str]
|
|
default_config: Dict[str, Any]
|
|
|
|
|
|
class EnhancedExportResponse(BaseModel):
|
|
"""Response model for enhanced export."""
|
|
export_id: str
|
|
summary_id: str
|
|
export_format: str
|
|
content: str
|
|
metadata: Dict[str, Any]
|
|
quality_score: float
|
|
processing_time_seconds: float
|
|
created_at: str
|
|
config_used: Dict[str, Any]
|
|
|
|
|
|
class TemplateCreateRequest(BaseModel):
|
|
"""Request model for creating custom templates."""
|
|
name: str
|
|
description: str
|
|
prompt_text: str
|
|
domain_category: DomainCategory
|
|
model_config: Optional[Dict[str, Any]] = None
|
|
is_public: bool = False
|
|
tags: Optional[List[str]] = None
|
|
|
|
|
|
class TemplateResponse(BaseModel):
|
|
"""Response model for template data."""
|
|
id: str
|
|
name: str
|
|
description: str
|
|
domain_category: str
|
|
is_public: bool
|
|
usage_count: int
|
|
rating: float
|
|
version: str
|
|
created_at: str
|
|
tags: List[str]
|
|
|
|
|
|
class TemplateExecuteRequest(BaseModel):
|
|
"""Request model for executing a template."""
|
|
template_id: str
|
|
variables: Dict[str, Any]
|
|
override_config: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
# Enhanced Export Endpoints
|
|
|
|
@router.post("/enhanced", response_model=EnhancedExportResponse)
|
|
async def generate_enhanced_export(
|
|
request: EnhancedExportRequest,
|
|
background_tasks: BackgroundTasks,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Generate enhanced markdown export with executive summary and timestamped sections."""
|
|
|
|
try:
|
|
# TODO: Get summary data from database using summary_id
|
|
# For now, using placeholder data
|
|
video_title = "Sample Video Title"
|
|
video_url = "https://youtube.com/watch?v=sample"
|
|
content = "This is sample content for enhanced export generation."
|
|
transcript_data = [] # TODO: Get real transcript data
|
|
|
|
# Create export configuration
|
|
export_config = MarkdownExportConfig(
|
|
include_executive_summary=request.include_executive_summary,
|
|
include_timestamps=request.include_timestamps,
|
|
include_toc=request.include_toc,
|
|
section_detail_level=request.section_detail_level,
|
|
custom_template_id=request.template_id
|
|
)
|
|
|
|
# Generate enhanced export
|
|
export_result = await markdown_formatter.create_enhanced_export(
|
|
video_title=video_title,
|
|
video_url=video_url,
|
|
content=content,
|
|
transcript_data=transcript_data,
|
|
export_config=export_config
|
|
)
|
|
|
|
# TODO: Save export metadata to database
|
|
export_id = str(uuid.uuid4())
|
|
|
|
# Background task: Update template usage statistics
|
|
if request.template_id:
|
|
background_tasks.add_task(
|
|
_update_template_usage_stats,
|
|
request.template_id,
|
|
export_result.processing_time_seconds,
|
|
len(export_result.markdown_content)
|
|
)
|
|
|
|
return EnhancedExportResponse(
|
|
export_id=export_id,
|
|
summary_id=request.summary_id,
|
|
export_format=request.format,
|
|
content=export_result.markdown_content,
|
|
metadata=export_result.metadata,
|
|
quality_score=export_result.quality_score,
|
|
processing_time_seconds=export_result.processing_time_seconds,
|
|
created_at=export_result.created_at.isoformat(),
|
|
config_used=request.dict()
|
|
)
|
|
|
|
except ServiceError as e:
|
|
logger.error(f"Enhanced export generation failed: {e}")
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error in enhanced export: {e}")
|
|
raise HTTPException(status_code=500, detail="Export generation failed")
|
|
|
|
|
|
@router.get("/config", response_model=ExportConfigResponse)
|
|
async def get_export_config():
|
|
"""Get available export configuration options."""
|
|
|
|
return ExportConfigResponse(
|
|
available_formats=["markdown", "pdf", "html", "json"],
|
|
section_detail_levels=["brief", "standard", "detailed"],
|
|
default_config={
|
|
"include_executive_summary": True,
|
|
"include_timestamps": True,
|
|
"include_toc": True,
|
|
"section_detail_level": "standard",
|
|
"format": "markdown"
|
|
}
|
|
)
|
|
|
|
|
|
@router.get("/{export_id}/download")
|
|
async def download_export(
|
|
export_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Download a previously generated export."""
|
|
|
|
# TODO: Implement export download from storage
|
|
# For now, return placeholder response
|
|
|
|
raise HTTPException(status_code=501, detail="Export download not yet implemented")
|
|
|
|
|
|
# Template Management Endpoints
|
|
|
|
@router.post("/templates", response_model=TemplateResponse)
|
|
async def create_template(
|
|
request: TemplateCreateRequest,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Create a custom prompt template."""
|
|
|
|
try:
|
|
template = await template_manager.create_template(
|
|
name=request.name,
|
|
description=request.description,
|
|
prompt_text=request.prompt_text,
|
|
domain_category=request.domain_category,
|
|
model_config=None, # Will use defaults
|
|
is_public=request.is_public,
|
|
created_by=current_user.id,
|
|
tags=request.tags or []
|
|
)
|
|
|
|
return TemplateResponse(
|
|
id=template.id,
|
|
name=template.name,
|
|
description=template.description,
|
|
domain_category=template.domain_category.value,
|
|
is_public=template.is_public,
|
|
usage_count=template.usage_count,
|
|
rating=template.rating,
|
|
version=template.version,
|
|
created_at=template.created_at.isoformat(),
|
|
tags=template.tags
|
|
)
|
|
|
|
except ServiceError as e:
|
|
logger.error(f"Template creation failed: {e}")
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error creating template: {e}")
|
|
raise HTTPException(status_code=500, detail="Template creation failed")
|
|
|
|
|
|
@router.get("/templates", response_model=List[TemplateResponse])
|
|
async def list_templates(
|
|
domain_category: Optional[DomainCategory] = Query(None),
|
|
is_public: Optional[bool] = Query(None),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""List available prompt templates."""
|
|
|
|
try:
|
|
templates = await template_manager.list_templates(
|
|
domain_category=domain_category,
|
|
is_public=is_public,
|
|
created_by=current_user.id if is_public is False else None
|
|
)
|
|
|
|
return [
|
|
TemplateResponse(
|
|
id=template.id,
|
|
name=template.name,
|
|
description=template.description,
|
|
domain_category=template.domain_category.value,
|
|
is_public=template.is_public,
|
|
usage_count=template.usage_count,
|
|
rating=template.rating,
|
|
version=template.version,
|
|
created_at=template.created_at.isoformat(),
|
|
tags=template.tags
|
|
)
|
|
for template in templates
|
|
]
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error listing templates: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to list templates")
|
|
|
|
|
|
@router.get("/templates/{template_id}", response_model=TemplateResponse)
|
|
async def get_template(
|
|
template_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get a specific template by ID."""
|
|
|
|
try:
|
|
template = await template_manager.get_template(template_id)
|
|
|
|
if not template:
|
|
raise HTTPException(status_code=404, detail="Template not found")
|
|
|
|
# Check permissions
|
|
if not template.is_public and template.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
return TemplateResponse(
|
|
id=template.id,
|
|
name=template.name,
|
|
description=template.description,
|
|
domain_category=template.domain_category.value,
|
|
is_public=template.is_public,
|
|
usage_count=template.usage_count,
|
|
rating=template.rating,
|
|
version=template.version,
|
|
created_at=template.created_at.isoformat(),
|
|
tags=template.tags
|
|
)
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error getting template: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to get template")
|
|
|
|
|
|
@router.post("/templates/{template_id}/execute")
|
|
async def execute_template(
|
|
template_id: str,
|
|
request: TemplateExecuteRequest,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Execute a template with provided variables."""
|
|
|
|
try:
|
|
result = await template_manager.execute_template(
|
|
template_id=template_id,
|
|
variables=request.variables,
|
|
override_config=None
|
|
)
|
|
|
|
return {
|
|
"template_id": template_id,
|
|
"execution_result": result,
|
|
"executed_at": datetime.now().isoformat(),
|
|
"user_id": current_user.id
|
|
}
|
|
|
|
except ServiceError as e:
|
|
logger.error(f"Template execution failed: {e}")
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error executing template: {e}")
|
|
raise HTTPException(status_code=500, detail="Template execution failed")
|
|
|
|
|
|
@router.delete("/templates/{template_id}")
|
|
async def delete_template(
|
|
template_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Delete a custom template."""
|
|
|
|
try:
|
|
template = await template_manager.get_template(template_id)
|
|
|
|
if not template:
|
|
raise HTTPException(status_code=404, detail="Template not found")
|
|
|
|
# Check permissions
|
|
if template.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Can only delete your own templates")
|
|
|
|
success = await template_manager.delete_template(template_id)
|
|
|
|
if success:
|
|
return {"message": "Template deleted successfully", "template_id": template_id}
|
|
else:
|
|
raise HTTPException(status_code=404, detail="Template not found")
|
|
|
|
except HTTPException:
|
|
raise
|
|
except ServiceError as e:
|
|
logger.error(f"Template deletion failed: {e}")
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error deleting template: {e}")
|
|
raise HTTPException(status_code=500, detail="Template deletion failed")
|
|
|
|
|
|
# Domain-Specific Recommendations
|
|
|
|
@router.post("/recommendations")
|
|
async def get_domain_recommendations(
|
|
content_sample: str = Query(..., description="Sample content for analysis"),
|
|
max_recommendations: int = Query(3, description="Maximum number of recommendations")
|
|
):
|
|
"""Get domain template recommendations based on content."""
|
|
|
|
try:
|
|
recommendations = await template_manager.get_domain_recommendations(
|
|
content_sample=content_sample,
|
|
max_recommendations=max_recommendations
|
|
)
|
|
|
|
return {
|
|
"content_analyzed": content_sample[:100] + "..." if len(content_sample) > 100 else content_sample,
|
|
"recommendations": recommendations,
|
|
"generated_at": datetime.now().isoformat()
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting recommendations: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to get recommendations")
|
|
|
|
|
|
# Analytics and Statistics
|
|
|
|
@router.get("/templates/{template_id}/analytics")
|
|
async def get_template_analytics(
|
|
template_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get analytics for a specific template."""
|
|
|
|
try:
|
|
analytics = await template_manager.get_template_analytics(template_id)
|
|
return analytics
|
|
|
|
except ServiceError as e:
|
|
logger.error(f"Template analytics failed: {e}")
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Error getting template analytics: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to get analytics")
|
|
|
|
|
|
@router.get("/system/stats")
|
|
async def get_system_stats():
|
|
"""Get overall system statistics."""
|
|
|
|
try:
|
|
stats = await template_manager.get_system_stats()
|
|
return stats
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting system stats: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to get system stats")
|
|
|
|
|
|
# Background task helpers
|
|
|
|
async def _update_template_usage_stats(
|
|
template_id: str,
|
|
processing_time: float,
|
|
response_length: int
|
|
):
|
|
"""Background task to update template usage statistics."""
|
|
try:
|
|
await template_manager._update_template_usage(
|
|
template_id, processing_time, response_length
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Failed to update template usage stats: {e}")
|
|
|
|
|
|
# Health check
|
|
|
|
@router.get("/health")
|
|
async def health_check():
|
|
"""Enhanced export service health check."""
|
|
|
|
try:
|
|
# Test service availability
|
|
executive_stats = executive_generator.get_executive_summary_stats()
|
|
timestamp_stats = timestamp_processor.get_processor_stats()
|
|
formatter_stats = markdown_formatter.get_formatter_stats()
|
|
system_stats = await template_manager.get_system_stats()
|
|
|
|
return {
|
|
"status": "healthy",
|
|
"services": {
|
|
"executive_summary_generator": executive_stats,
|
|
"timestamp_processor": timestamp_stats,
|
|
"markdown_formatter": formatter_stats,
|
|
"template_manager": system_stats
|
|
},
|
|
"timestamp": datetime.now().isoformat()
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Health check failed: {e}")
|
|
raise HTTPException(status_code=503, detail="Service unhealthy") |