From 9f87639b51d9f3846795d9d3c0b65a2a8b0b7500 Mon Sep 17 00:00:00 2001 From: utshodeytech Date: Mon, 9 Dec 2024 17:18:09 +0600 Subject: [PATCH] base code added --- .gitignore | 4 + Dockerfile | 0 app/__init__.py | 0 app/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 147 bytes app/__pycache__/config.cpython-312.pyc | Bin 0 -> 519 bytes app/api/__init__.py | 0 app/api/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 151 bytes .../__pycache__/fact_check.cpython-312.pyc | Bin 0 -> 11344 bytes app/api/fact_check.py | 291 ++++++++++++++++++ app/config.py | 10 + main.py | 49 +++ requirements.txt | 6 + 12 files changed, 360 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-312.pyc create mode 100644 app/__pycache__/config.cpython-312.pyc create mode 100644 app/api/__init__.py create mode 100644 app/api/__pycache__/__init__.cpython-312.pyc create mode 100644 app/api/__pycache__/fact_check.cpython-312.pyc create mode 100644 app/api/fact_check.py create mode 100644 app/config.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21d6e87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +env +.env +test.py +/__pycache__/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e69de29 diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/__pycache__/__init__.cpython-312.pyc b/app/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba12e2f8b81a4d298ac0fe8ffaaf83d795a34fb8 GIT binary patch literal 147 zcmX@j%ge<81X8=h(n0iN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!GSbh;&rQ`YEh)~( zPf4xRPfRH;$jr$}EYeR)OfGTGNKMYxPb?_VkB`sH%PfhH*DI*}#bJ}1pHiBWYFESx UG=dR`i$RQ!%#4hTMa)1J00c)Ob^rhX literal 0 HcmV?d00001 diff --git a/app/__pycache__/config.cpython-312.pyc b/app/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53d89a61cbd15bc0b5b4e4eca7e37df2c7c4ddfe GIT binary patch literal 519 zcmX@j%ge<81kAg_(xn&~7#@Q-Fu(+5e4YnnOlL@8NMX!jhyu|}IgGhXQA~^sObn?^ ztD(vm8B!Ut*kGCwbPBU1kQ2p%LzV?B%Zfvm6)ek!LzWFJ%U;Q-$^H^#p`Rw>E$*EB z#FY4y{F2nXvRjhw{{HShuJLY;&LQ#69;nURt4HUsBf2Dyg}+%3EhWaQ?1&GfpU z?sQSc`2vIF4H)0~qKwN02B`;v!qcTDN?nlAxhSaHz;}aRu)nIaYKGoLe$7UnA|9ZL E0F&8t`Tzg` literal 0 HcmV?d00001 diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/__pycache__/__init__.cpython-312.pyc b/app/api/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e21318e0a89031f9dc8115f60d51daa71a2b94e GIT binary patch literal 151 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 X?_roXy9#;dKtqTV-mPm0nN0r&zV3hw literal 0 HcmV?d00001 diff --git a/app/api/fact_check.py b/app/api/fact_check.py new file mode 100644 index 0000000..3e7a12d --- /dev/null +++ b/app/api/fact_check.py @@ -0,0 +1,291 @@ +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel, Field, HttpUrl, validator, ConfigDict +from typing import Dict, List, Optional, Union +import requests +from enum import Enum +from datetime import datetime +import json +from app.config import GOOGLE_FACT_CHECK_API_KEY, GOOGLE_FACT_CHECK_BASE_URL + +fact_check_router = APIRouter() + +class CustomJSONEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, datetime): + return obj.isoformat() + return super().default(obj) + +class ErrorResponse(BaseModel): + detail: str + error_code: str = Field(..., description="Unique error code for this type of error") + timestamp: str = Field(default_factory=lambda: datetime.now().isoformat()) + path: Optional[str] = Field(None, description="The endpoint path where error occurred") + + model_config = ConfigDict(json_schema_extra={ + "example": { + "detail": "Error description", + "error_code": "ERROR_CODE", + "timestamp": "2024-12-09T16:49:30.905765", + "path": "/check-facts" + } + }) + +class RequestValidationError(BaseModel): + loc: List[str] + msg: str + type: str + +class Publisher(BaseModel): + name: str + site: Optional[str] = Field(None, description="Publisher's website") + + @validator('site') + def validate_site(cls, v): + if v and not (v.startswith('http://') or v.startswith('https://')): + return f"https://{v}" + return v + +class ClaimReview(BaseModel): + publisher: Publisher + url: Optional[HttpUrl] = None + title: Optional[str] = None + reviewDate: Optional[str] = None + textualRating: Optional[str] = None + languageCode: str = Field(default="en-US") + +class Claim(BaseModel): + text: str + claimant: Optional[str] = None + claimDate: Optional[str] = None + claimReview: List[ClaimReview] + +class FactCheckResponse(BaseModel): + query: str = Field(..., description="Original query that was fact-checked") + total_claims_found: int = Field(..., ge=0) + results: List[Claim] = Field(default_factory=list) + summary: Dict[str, int] = Field(...) + + model_config = ConfigDict(json_schema_extra={ + "example": { + "query": "Example claim", + "total_claims_found": 1, + "results": [{ + "text": "Example claim text", + "claimant": "Source name", + "claimReview": [{ + "publisher": { + "name": "Fact Checker", + "site": "factchecker.com" + }, + "textualRating": "True" + }] + }], + "summary": { + "total_sources": 1, + "fact_checking_sites_queried": 10 + } + } + }) + +class SourceType(str, Enum): + FACT_CHECKER = "fact_checker" + NEWS_SITE = "news_site" + +class FactCheckSource(BaseModel): + domain: str + type: SourceType + priority: int = Field(default=1, ge=1, le=10) + + model_config = ConfigDict(json_schema_extra={ + "example": { + "domain": "factcheck.org", + "type": "fact_checker", + "priority": 1 + } + }) + +# Sources configuration with validation +SOURCES = { + "fact_checkers": [ + FactCheckSource(domain=domain, type=SourceType.FACT_CHECKER, priority=1) + for domain in [ + "factcheck.org", + "snopes.com", + "politifact.com", + "reuters.com", + "bbc.com", + "apnews.com", + "usatoday.com", + "nytimes.com", + "washingtonpost.com", + "afp.com", + "fullfact.org", + "truthorfiction.com", + "leadstories.com", + "altnews.in", + "boomlive.in", + "en.prothomalo.com" + ] + ], + "news_sites": [ + FactCheckSource(domain=domain, type=SourceType.NEWS_SITE, priority=2) + for domain in [ + "www.thedailystar.net", + "www.thefinancialexpress.com.bd", + "www.theindependentbd.com", + "www.dhakatribune.com", + "www.newagebd.net", + "www.observerbd.com", + "www.daily-sun.com", + "www.tbsnews.net", + "www.businesspostbd.com", + "www.banglanews24.com/english", + "www.bdnews24.com/english", + "www.risingbd.com/english", + "www.dailyindustry.news", + "www.bangladeshpost.net", + "www.daily-bangladesh.com/english" + ] + ] +} + +class FactCheckRequest(BaseModel): + content: str = Field( + ..., + min_length=10, + max_length=1000, + description="The claim to be fact-checked" + ) + language: str = Field(default="en-US", pattern="^[a-z]{2}-[A-Z]{2}$") + max_results_per_source: int = Field(default=10, ge=1, le=50) + + @validator('content') + def validate_content(cls, v): + if not v.strip(): + raise ValueError("Content cannot be empty or just whitespace") + return v.strip() + +async def fetch_fact_checks( + api_key: str, + base_url: str, + query: str, + site: FactCheckSource +) -> Dict: + """ + Fetch fact checks from a specific site using the Google Fact Check API + """ + try: + if not api_key or not base_url: + raise ValueError("API key or base URL not configured") + + params = { + "key": api_key, + "query": query, + "languageCode": "en-US", + "reviewPublisherSiteFilter": site.domain, + "pageSize": 10 + } + + response = requests.get(base_url, params=params) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + raise HTTPException( + status_code=503, + detail=ErrorResponse( + detail=f"Error fetching from {site.domain}: {str(e)}", + error_code="FACT_CHECK_SERVICE_ERROR", + path="/check-facts" + ).dict() + ) + except ValueError as e: + raise HTTPException( + status_code=500, + detail=ErrorResponse( + detail=str(e), + error_code="CONFIGURATION_ERROR", + path="/check-facts" + ).dict() + ) + +@fact_check_router.post( + "/check-facts", + response_model=FactCheckResponse, + responses={ + 400: {"model": ErrorResponse}, + 404: {"model": ErrorResponse}, + 500: {"model": ErrorResponse}, + 503: {"model": ErrorResponse} + } +) +async def check_facts(request: FactCheckRequest) -> FactCheckResponse: + """ + Check facts using multiple fact-checking sources + """ + all_results = [] + + # Validate configuration + if not GOOGLE_FACT_CHECK_API_KEY or not GOOGLE_FACT_CHECK_BASE_URL: + raise HTTPException( + status_code=500, + detail=ErrorResponse( + detail="API configuration is missing", + error_code="CONFIGURATION_ERROR", + path="/check-facts" + ).dict() + ) + + # Check all sources in priority order + all_sources = ( + SOURCES["fact_checkers"] + + SOURCES["news_sites"] + ) + all_sources.sort(key=lambda x: x.priority) + + for source in all_sources: + try: + result = await fetch_fact_checks( + GOOGLE_FACT_CHECK_API_KEY, + GOOGLE_FACT_CHECK_BASE_URL, + request.content, + source + ) + + if "claims" in result: + # Validate each claim through Pydantic + validated_claims = [ + Claim(**claim).dict() + for claim in result["claims"] + ] + all_results.extend(validated_claims) + + except HTTPException: + raise + except Exception as e: + # Log the error but continue with other sources + print(f"Error processing {source.domain}: {str(e)}") + continue + + if not all_results: + raise HTTPException( + status_code=404, + detail=ErrorResponse( + detail="No fact check results found", + error_code="NO_RESULTS_FOUND", + path="/check-facts" + ).dict() + ) + + # Create the response using Pydantic model + response = FactCheckResponse( + query=request.content, + total_claims_found=len(all_results), + results=all_results, + summary={ + "total_sources": len(set(claim.get("claimReview", [{}])[0].get("publisher", {}).get("site", "") + for claim in all_results if claim.get("claimReview"))), + "fact_checking_sites_queried": len(all_sources) + } + ) + + return response \ No newline at end of file diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..d9de9e9 --- /dev/null +++ b/app/config.py @@ -0,0 +1,10 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +GOOGLE_FACT_CHECK_API_KEY = os.environ["GOOGLE_FACT_CHECK_API_KEY"] +GOOGLE_FACT_CHECK_BASE_URL= os.environ["GOOGLE_FACT_CHECK_BASE_URL"] + +OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] +FRONTEND_URL = os.environ["FRONTEND_URL"] \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..6b79e28 --- /dev/null +++ b/main.py @@ -0,0 +1,49 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from app.api.fact_check import fact_check_router +from app.config import FRONTEND_URL + +# Initialize FastAPI app +app = FastAPI( + title="Your API Title", + description="Your API Description", + version="1.0.0" +) + +# CORS configuration +origins = [ + FRONTEND_URL, + "http://localhost", + "http://localhost:5173", + "http://0.0.0.0", + "http://0.0.0.0:5173", +] + + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Basic root endpoint +@app.get("/") +async def root(): + return {"message": "Welcome to your FastAPI application"} + +# Health check endpoint +@app.get("/health") +async def health_check(): + return {"status": "healthy"} + +app.include_router(fact_check_router, prefix="") + +# Include routers (uncomment and modify as needed) +# from routes import some_router +# app.include_router(some_router, prefix="/your-prefix", tags=["your-tag"]) + +if __name__ == "__main__": + import uvicorn + uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..418141f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +certifi==2024.8.30 +charset-normalizer==3.4.0 +idna==3.10 +python-dotenv==1.0.1 +requests==2.32.3 +urllib3==2.2.3