|
from fastapi import FastAPI, HTTPException, Response, File, UploadFile, Form
|
|
from fastapi.responses import FileResponse, JSONResponse
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from pydantic import BaseModel
|
|
from typing import List, Dict, Any, Optional
|
|
import os
|
|
import json
|
|
import uuid
|
|
import asyncio
|
|
import uvicorn
|
|
from utils import (search_news, analyze_article_sentiment, perform_comparative_analysis,
|
|
translate_to_hindi, text_to_speech, prepare_final_report, NewsArticle)
|
|
|
|
|
|
app = FastAPI(
|
|
title="News Summarization and TTS API",
|
|
description="API for extracting news, performing sentiment analysis, and generating Hindi TTS audio",
|
|
version="1.0.0"
|
|
)
|
|
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
class CompanyRequest(BaseModel):
|
|
company_name: str
|
|
|
|
class TextToSpeechRequest(BaseModel):
|
|
text: str
|
|
output_filename: Optional[str] = None
|
|
|
|
class SentimentAnalysisRequest(BaseModel):
|
|
articles: List[Dict[str, Any]]
|
|
|
|
class NewsResponse(BaseModel):
|
|
articles: List[Dict[str, Any]]
|
|
|
|
class SentimentResponse(BaseModel):
|
|
sentiment_analysis: Dict[str, Any]
|
|
|
|
class TextToSpeechResponse(BaseModel):
|
|
audio_file: str
|
|
text: str
|
|
|
|
|
|
os.makedirs("audio_files", exist_ok=True)
|
|
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
"""Root endpoint to check if API is running."""
|
|
return {"message": "News Summarization and TTS API is running"}
|
|
|
|
@app.post("/api/get_news", response_model=NewsResponse)
|
|
async def get_news(request: CompanyRequest):
|
|
"""Fetch news articles about a specific company."""
|
|
try:
|
|
company_name = request.company_name
|
|
articles = search_news(company_name)
|
|
|
|
if not articles:
|
|
raise HTTPException(status_code=404, detail=f"No news articles found for {company_name}")
|
|
|
|
|
|
article_data = [article.to_dict() for article in articles]
|
|
|
|
return {"articles": article_data}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
@app.post("/api/analyze_sentiment", response_model=SentimentResponse)
|
|
async def analyze_sentiment(request: SentimentAnalysisRequest):
|
|
"""Analyze sentiment of provided articles."""
|
|
try:
|
|
|
|
articles = []
|
|
for article_dict in request.articles:
|
|
article = NewsArticle(
|
|
title=article_dict["title"],
|
|
url=article_dict["url"],
|
|
content=article_dict["content"],
|
|
summary=article_dict.get("summary", ""),
|
|
source=article_dict.get("source", ""),
|
|
date=article_dict.get("date", ""),
|
|
sentiment=article_dict.get("sentiment", ""),
|
|
topics=article_dict.get("topics", [])
|
|
)
|
|
articles.append(article)
|
|
|
|
|
|
detailed_sentiment = [analyze_article_sentiment(article) for article in articles]
|
|
|
|
|
|
comparative_analysis = perform_comparative_analysis(articles)
|
|
|
|
return {
|
|
"sentiment_analysis": {
|
|
"detailed_sentiment": detailed_sentiment,
|
|
"comparative_analysis": comparative_analysis
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
@app.post("/api/generate_speech", response_model=TextToSpeechResponse)
|
|
async def generate_speech(request: TextToSpeechRequest):
|
|
"""Convert text to Hindi speech."""
|
|
try:
|
|
text = request.text
|
|
|
|
|
|
output_filename = request.output_filename
|
|
if not output_filename:
|
|
unique_id = uuid.uuid4().hex
|
|
output_filename = f"audio_files/{unique_id}.mp3"
|
|
elif not output_filename.startswith("audio_files/"):
|
|
output_filename = f"audio_files/{output_filename}"
|
|
|
|
|
|
hindi_text = translate_to_hindi(text)
|
|
|
|
|
|
audio_file = text_to_speech(hindi_text, output_filename)
|
|
|
|
if not audio_file:
|
|
raise HTTPException(status_code=500, detail="Failed to generate audio file")
|
|
|
|
return {
|
|
"audio_file": audio_file,
|
|
"text": hindi_text
|
|
}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
@app.post("/api/complete_analysis")
|
|
async def complete_analysis(request: CompanyRequest):
|
|
"""Perform complete analysis for a company."""
|
|
try:
|
|
company_name = request.company_name
|
|
|
|
|
|
print(f"Starting complete analysis for company: {company_name}")
|
|
|
|
|
|
print("Step 1: Fetching news articles...")
|
|
articles = search_news(company_name, num_articles=5)
|
|
print(f"Found {len(articles)} articles for {company_name}")
|
|
|
|
if not articles:
|
|
raise HTTPException(status_code=404, detail=f"No news articles found for {company_name}")
|
|
|
|
|
|
print("Step 2: Performing comparative analysis...")
|
|
comparative_analysis = perform_comparative_analysis(articles)
|
|
print("Comparative analysis completed")
|
|
|
|
|
|
print("Step 3: Preparing final report...")
|
|
final_report = prepare_final_report(company_name, articles, comparative_analysis)
|
|
print("Final report prepared")
|
|
|
|
|
|
print("Step 4: Generating Hindi TTS...")
|
|
unique_id = uuid.uuid4().hex
|
|
output_filename = f"audio_files/{unique_id}.mp3"
|
|
|
|
|
|
hindi_text = final_report["Hindi Summary"]
|
|
print(f"Converting Hindi text to speech (length: {len(hindi_text)} characters)")
|
|
|
|
audio_file = text_to_speech(hindi_text, output_filename)
|
|
|
|
|
|
formatted_response = {
|
|
"Company": company_name,
|
|
"Articles": final_report["Articles"],
|
|
"Comparative Sentiment Score": {
|
|
"Sentiment Distribution": comparative_analysis["Sentiment Distribution"],
|
|
"Coverage Differences": comparative_analysis["Coverage Differences"],
|
|
"Topic Overlap": {
|
|
"Common Topics": comparative_analysis["Topic Overlap"]["Common Topics Across All"],
|
|
}
|
|
},
|
|
"Final Sentiment Analysis": comparative_analysis["Final Sentiment Analysis"],
|
|
}
|
|
|
|
|
|
unique_topics = comparative_analysis["Topic Overlap"]["Unique Topics By Article"]
|
|
for article_idx, topics in unique_topics.items():
|
|
article_num = int(article_idx) + 1
|
|
formatted_response["Comparative Sentiment Score"]["Topic Overlap"][f"Unique Topics in Article {article_num}"] = topics
|
|
|
|
|
|
if len(articles) <= 1:
|
|
formatted_response["Comparative Sentiment Score"]["Coverage Differences"] = [
|
|
{
|
|
"Comparison": f"Only one article about {company_name} was found, limiting comparative analysis.",
|
|
"Impact": "Unable to compare coverage across multiple sources for more comprehensive insights."
|
|
}
|
|
]
|
|
|
|
|
|
if not audio_file:
|
|
print("Warning: Failed to generate audio file")
|
|
formatted_response["Audio"] = "Failed to generate audio"
|
|
else:
|
|
print(f"Audio file generated: {audio_file}")
|
|
formatted_response["Audio"] = f"[Play Hindi Speech]"
|
|
|
|
formatted_response["_audio_file_path"] = audio_file
|
|
|
|
|
|
formatted_response["Hindi Summary"] = final_report["Hindi Summary"]
|
|
|
|
print("Complete analysis finished successfully")
|
|
return formatted_response
|
|
|
|
except HTTPException as he:
|
|
|
|
print(f"HTTP Exception: {he.detail}")
|
|
raise
|
|
|
|
except Exception as e:
|
|
|
|
import traceback
|
|
error_trace = traceback.format_exc()
|
|
error_message = f"Error processing request: {str(e)}"
|
|
print(f"ERROR: {error_message}")
|
|
print(f"Traceback: {error_trace}")
|
|
|
|
|
|
user_message = "An error occurred during analysis. "
|
|
|
|
if "timeout" in str(e).lower():
|
|
user_message += "There was a timeout when connecting to news sources. Please try again or try another company name."
|
|
elif "connection" in str(e).lower():
|
|
user_message += "There was a connection issue with one of the news sources. Please check your internet connection."
|
|
elif "not found" in str(e).lower():
|
|
user_message += f"No information could be found for {company_name}. Please try another company name."
|
|
else:
|
|
user_message += "Please try again with a different company name or check the server logs for more details."
|
|
|
|
raise HTTPException(status_code=500, detail=user_message)
|
|
|
|
@app.get("/api/audio/{file_name}")
|
|
async def get_audio(file_name: str):
|
|
"""Serve audio files."""
|
|
file_path = f"audio_files/{file_name}"
|
|
|
|
|
|
os.makedirs("audio_files", exist_ok=True)
|
|
|
|
if not os.path.exists(file_path):
|
|
print(f"Audio file not found: {file_path}")
|
|
|
|
audio_files = os.listdir("audio_files") if os.path.exists("audio_files") else []
|
|
print(f"Available audio files: {audio_files}")
|
|
raise HTTPException(status_code=404, detail=f"Audio file {file_name} not found")
|
|
|
|
try:
|
|
|
|
with open(file_path, "rb") as f:
|
|
file_size = os.path.getsize(file_path)
|
|
print(f"Serving audio file: {file_path} (size: {file_size} bytes)")
|
|
if file_size == 0:
|
|
raise HTTPException(status_code=500, detail="Audio file is empty")
|
|
except Exception as e:
|
|
print(f"Error accessing audio file {file_path}: {str(e)}")
|
|
raise HTTPException(status_code=500, detail=f"Error accessing audio file: {str(e)}")
|
|
|
|
|
|
headers = {
|
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
"Pragma": "no-cache",
|
|
"Expires": "0",
|
|
"Content-Disposition": f"attachment; filename={file_name}"
|
|
}
|
|
|
|
|
|
media_type = "audio/mpeg"
|
|
if file_name.lower().endswith(".wav"):
|
|
media_type = "audio/wav"
|
|
|
|
return FileResponse(
|
|
path=file_path,
|
|
media_type=media_type,
|
|
headers=headers,
|
|
filename=file_name
|
|
)
|
|
|
|
@app.post("/api/example_format")
|
|
async def get_example_format(request: CompanyRequest):
|
|
"""
|
|
Get analysis results in the example format specified.
|
|
This endpoint provides results that exactly match the requested output format.
|
|
"""
|
|
try:
|
|
|
|
company_name = request.company_name
|
|
result = await complete_analysis(request)
|
|
|
|
|
|
formatted_output = {
|
|
"Company": result["Company"],
|
|
"Articles": result["Articles"],
|
|
"Comparative Sentiment Score": {
|
|
"Sentiment Distribution": result["Comparative Sentiment Score"]["Sentiment Distribution"],
|
|
"Coverage Differences": result["Comparative Sentiment Score"]["Coverage Differences"],
|
|
"Topic Overlap": result["Comparative Sentiment Score"]["Topic Overlap"]
|
|
},
|
|
"Final Sentiment Analysis": result["Final Sentiment Analysis"],
|
|
"Audio": "[Play Hindi Speech]" if result.get("Audio") else "No audio available"
|
|
}
|
|
|
|
return formatted_output
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Error generating example format: {str(e)}")
|
|
|
|
if __name__ == "__main__":
|
|
uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True) |