Spaces:
Running
Running
updating analyze.py
Browse files- app/routers/analyze.py +120 -22
app/routers/analyze.py
CHANGED
@@ -1,36 +1,97 @@
|
|
1 |
-
from fastapi import
|
2 |
-
from
|
3 |
-
from
|
4 |
-
from
|
5 |
-
from mediaunmasked.scrapers.article_scraper import ArticleScraper # Assuming you have a scraper module
|
6 |
-
from mediaunmasked.analyzers.scoring import MediaScorer # Assuming you have a scorer module
|
7 |
import logging
|
|
|
|
|
8 |
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
-
|
|
|
|
|
12 |
|
|
|
|
|
13 |
scraper = ArticleScraper()
|
14 |
scorer = MediaScorer()
|
15 |
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
try:
|
19 |
-
|
20 |
-
|
|
|
|
|
21 |
if not article:
|
22 |
raise HTTPException(
|
23 |
status_code=400,
|
24 |
detail="Failed to scrape article content"
|
25 |
)
|
26 |
-
|
27 |
-
#
|
28 |
analysis = scorer.calculate_media_score(
|
29 |
article["headline"],
|
30 |
article["content"]
|
31 |
)
|
32 |
-
|
33 |
-
#
|
|
|
|
|
|
|
|
|
|
|
34 |
response_dict = {
|
35 |
"headline": str(article['headline']),
|
36 |
"content": str(article['content']),
|
@@ -63,15 +124,52 @@ async def analyze_content(request: AnalyzeRequest):
|
|
63 |
}
|
64 |
}
|
65 |
}
|
66 |
-
|
|
|
67 |
logger.info("Final response structure:")
|
68 |
logger.info(response_dict)
|
69 |
-
|
70 |
-
return
|
71 |
-
|
72 |
except Exception as e:
|
73 |
-
logger.error(f"Analysis failed
|
74 |
raise HTTPException(
|
75 |
status_code=500,
|
76 |
-
detail=f"Analysis failed
|
77 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, HTTPException
|
2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
3 |
+
from pydantic import BaseModel, HttpUrl, Field
|
4 |
+
from typing import Dict, Any, List
|
|
|
|
|
5 |
import logging
|
6 |
+
import sys
|
7 |
+
import os
|
8 |
|
9 |
+
# Add src to Python path
|
10 |
+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
11 |
+
|
12 |
+
from mediaunmasked.scrapers.article_scraper import ArticleScraper
|
13 |
+
from mediaunmasked.analyzers.scoring import MediaScorer
|
14 |
+
from mediaunmasked.utils.logging_config import setup_logging
|
15 |
|
16 |
+
# Initialize logging
|
17 |
+
setup_logging()
|
18 |
+
logger = logging.getLogger(__name__)
|
19 |
|
20 |
+
# Initialize FastAPI with dependencies
|
21 |
+
app = FastAPI()
|
22 |
scraper = ArticleScraper()
|
23 |
scorer = MediaScorer()
|
24 |
|
25 |
+
# Configure CORS
|
26 |
+
app.add_middleware(
|
27 |
+
CORSMiddleware,
|
28 |
+
allow_origins=["*"],
|
29 |
+
allow_credentials=True,
|
30 |
+
allow_methods=["*"],
|
31 |
+
allow_headers=["*"],
|
32 |
+
)
|
33 |
+
|
34 |
+
class ArticleRequest(BaseModel):
|
35 |
+
url: HttpUrl
|
36 |
+
|
37 |
+
class MediaScoreDetails(BaseModel):
|
38 |
+
headline_analysis: Dict[str, Any]
|
39 |
+
sentiment_analysis: Dict[str, Any]
|
40 |
+
bias_analysis: Dict[str, Any]
|
41 |
+
evidence_analysis: Dict[str, Any]
|
42 |
+
|
43 |
+
class MediaScore(BaseModel):
|
44 |
+
media_unmasked_score: float
|
45 |
+
rating: str
|
46 |
+
details: MediaScoreDetails
|
47 |
+
|
48 |
+
class AnalysisResponse(BaseModel):
|
49 |
+
headline: str
|
50 |
+
content: str
|
51 |
+
sentiment: str
|
52 |
+
bias: str
|
53 |
+
bias_score: float
|
54 |
+
bias_percentage: float
|
55 |
+
flagged_phrases: List[str]
|
56 |
+
media_score: MediaScore
|
57 |
+
|
58 |
+
@app.post("/analyze", response_model=AnalysisResponse)
|
59 |
+
async def analyze_article(request: ArticleRequest) -> AnalysisResponse:
|
60 |
+
"""
|
61 |
+
Analyze an article for bias, sentiment, and credibility.
|
62 |
+
|
63 |
+
Args:
|
64 |
+
request: ArticleRequest containing the URL to analyze
|
65 |
+
|
66 |
+
Returns:
|
67 |
+
AnalysisResponse with complete analysis results
|
68 |
+
|
69 |
+
Raises:
|
70 |
+
HTTPException: If scraping or analysis fails
|
71 |
+
"""
|
72 |
try:
|
73 |
+
logger.info(f"Analyzing article: {request.url}")
|
74 |
+
|
75 |
+
# Scrape article
|
76 |
+
article = scraper.scrape_article(str(request.url))
|
77 |
if not article:
|
78 |
raise HTTPException(
|
79 |
status_code=400,
|
80 |
detail="Failed to scrape article content"
|
81 |
)
|
82 |
+
|
83 |
+
# Analyze content
|
84 |
analysis = scorer.calculate_media_score(
|
85 |
article["headline"],
|
86 |
article["content"]
|
87 |
)
|
88 |
+
|
89 |
+
# Log raw values for debugging
|
90 |
+
logger.info("Raw values:")
|
91 |
+
logger.info(f"media_unmasked_score type: {type(analysis['media_unmasked_score'])}")
|
92 |
+
logger.info(f"media_unmasked_score value: {analysis['media_unmasked_score']}")
|
93 |
+
|
94 |
+
# Ensure correct types in response
|
95 |
response_dict = {
|
96 |
"headline": str(article['headline']),
|
97 |
"content": str(article['content']),
|
|
|
124 |
}
|
125 |
}
|
126 |
}
|
127 |
+
|
128 |
+
# Log the final structure
|
129 |
logger.info("Final response structure:")
|
130 |
logger.info(response_dict)
|
131 |
+
|
132 |
+
return AnalysisResponse.parse_obj(response_dict)
|
133 |
+
|
134 |
except Exception as e:
|
135 |
+
logger.error(f"Analysis failed: {str(e)}", exc_info=True)
|
136 |
raise HTTPException(
|
137 |
status_code=500,
|
138 |
+
detail=f"Analysis failed: {str(e)}"
|
139 |
)
|
140 |
+
|
141 |
+
@app.get("/api/debug")
|
142 |
+
async def debug_response():
|
143 |
+
mock_analysis = {
|
144 |
+
"headline": "Test Headline",
|
145 |
+
"content": "Test content",
|
146 |
+
"sentiment": "Neutral",
|
147 |
+
"bias": "Neutral",
|
148 |
+
"bias_score": 0.75, # Note: 0-1 scale
|
149 |
+
"bias_percentage": 0,
|
150 |
+
"flagged_phrases": ["test phrase"],
|
151 |
+
"media_score": {
|
152 |
+
"media_unmasked_score": 75.5,
|
153 |
+
"rating": "Some Bias Present",
|
154 |
+
"details": {
|
155 |
+
"headline_analysis": {
|
156 |
+
"headline_vs_content_score": 20,
|
157 |
+
"contradictory_phrases": ["Sample contradiction"]
|
158 |
+
},
|
159 |
+
"sentiment_analysis": {
|
160 |
+
"sentiment": "Neutral",
|
161 |
+
"manipulation_score": 30,
|
162 |
+
"flagged_phrases": ["Sample manipulative phrase"]
|
163 |
+
},
|
164 |
+
"bias_analysis": {
|
165 |
+
"bias": "Neutral",
|
166 |
+
"bias_score": 0.75,
|
167 |
+
"bias_percentage": 0
|
168 |
+
},
|
169 |
+
"evidence_analysis": {
|
170 |
+
"evidence_based_score": 80
|
171 |
+
}
|
172 |
+
}
|
173 |
+
}
|
174 |
+
}
|
175 |
+
return AnalysisResponse.parse_obj(mock_analysis)
|