Bjornpool's picture
Initial commit - TruthScan AI v2.0 master thesis
a7e77af
Raw History Blame
44.2 kB
"""
SKRYPT TESTOWY TRUTHSCAN AI - TEST PORÓWNAWCZY BBC I GAZETA PRAWNA
"""
import requests
import json
import time
import statistics
import os
from datetime import datetime
from collections import defaultdict
from typing import Dict, List, Any, Optional
class TruthScanComparativeTester:
def __init__(self, base_url="http://localhost:8000", frontend_url="http://localhost:3000"):
self.base_url = base_url
self.frontend_url = frontend_url
self.results = []
self.errors = []
self.performance_data = []
self.bbc_results = {}
self.gazeta_results = {}
self.comparison_data = {}
def log_test(self, test_name: str, status: str, details: str = "", duration: float = None):
"""Zapisuje wynik testu"""
result = {
"test_name": test_name,
"status": status,
"timestamp": datetime.now().isoformat(),
"details": details,
"duration": duration
}
self.results.append(result)
status_symbol = "✅" if status == "PASS" else "❌" if status == "FAIL" else "⚠️"
print(f"{status_symbol} {test_name}: {details}")
if status == "FAIL":
self.errors.append(result)
##########
# Test dostępności API
##########
def test_api_availability(self):
test_name = "API Availability"
start = time.time()
try:
response = requests.get(f"{self.base_url}/docs", timeout=10)
duration = time.time() - start
if response.status_code == 200:
self.log_test(test_name, "PASS",
f"Swagger UI dostępny ({response.status_code})", duration)
return True
else:
self.log_test(test_name, "FAIL",
f"Status code: {response.status_code}", duration)
return False
except Exception as e:
self.log_test(test_name, "FAIL", f"Błąd połączenia: {str(e)}", time.time() - start)
return False
############
# Test: Sprawdzenie dostępnych źródeł
############
def test_sources_endpoint(self):
test_name = "GET /sources"
start = time.time()
try:
response = requests.get(f"{self.base_url}/sources", timeout=15)
duration = time.time() - start
if response.status_code == 200:
data = response.json()
if isinstance(data, list):
details = f"Znaleziono {len(data)} źródeł"
# Sprawdzenie czy oba źródła są dostępne
sources_lower = [s.lower() for s in data]
bbc_available = "bbc" in sources_lower or any("bbc" in s.lower() for s in data)
gazeta_available = "gazetaprawna" in sources_lower or any("gazeta" in s.lower() for s in data)
if not bbc_available:
details += " | ❌ BBC niedostępne"
status = "WARNING"
elif not gazeta_available:
details += " | ❌ Gazeta Prawna niedostępne"
status = "WARNING"
else:
details += " | ✅ BBC dostępne | ✅ Gazeta Prawna dostępne"
status = "PASS"
self.log_test(test_name, status, details, duration)
return data
else:
self.log_test(test_name, "FAIL", f"Nieoczekiwany typ danych: {type(data)}", duration)
return None
else:
self.log_test(test_name, "FAIL",
f"Status code: {response.status_code}", duration)
return None
except Exception as e:
self.log_test(test_name, "FAIL", f"Błąd: {str(e)}", time.time() - start)
return None
def test_single_source_detailed(self, source: str, source_name: str):
print(f"\n{'='*60}")
print(f"🔍 SZCZEGÓŁOWY TEST: {source_name} ({source})")
print(f"{'='*60}")
test_results = {
"source": source,
"source_name": source_name,
"articles": [],
"sentiment_distribution": defaultdict(int),
"fake_scores": [],
"performance": 0,
"errors": [],
"sample_titles": []
}
#### Test 1: Pobieranie artykułów
test_name = f"GET /news/{source}"
start = time.time()
try:
response = requests.get(f"{self.base_url}/news/{source}", timeout=30)
duration = time.time() - start
test_results["performance"] = duration
if response.status_code != 200:
self.log_test(test_name, "FAIL",
f"Status code: {response.status_code}", duration)
test_results["errors"].append(f"HTTP {response.status_code}")
return test_results
data = response.json()
if not data:
self.log_test(test_name, "FAIL", "Brak danych w odpowiedzi", duration)
return test_results
if isinstance(data, dict) and "articles" in data:
articles = data["articles"]
source_from_response = data.get("source", source)
test_results["articles"] = articles
details = f"Pobrano {len(articles)} artykułów | Źródło: {source_from_response}"
if len(articles) > 0:
first_article = articles[0]
available_fields = [field for field in ["title", "sentiment", "fake_probability", "summary", "link", "published"]
if field in first_article]
details += f" | Pola: {', '.join(available_fields)}"
title = first_article.get("title", "Brak tytułu")
if len(title) > 50:
title = title[:50] + "..."
details += f" | Przykład: '{title}'"
for i, article in enumerate(articles[:3]):
title = article.get("title", "")
if title:
test_results["sample_titles"].append(title[:80])
self.log_test(test_name, "PASS", details, duration)
# Zapis danych wydajności
self.performance_data.append({
"endpoint": test_name,
"duration": duration,
"source": source
})
else:
self.log_test(test_name, "WARNING",
f"Nieoczekiwany format danych: {type(data)}", duration)
return test_results
except Exception as e:
self.log_test(test_name, "FAIL", f"Błąd: {str(e)}", time.time() - start)
test_results["errors"].append(str(e))
return test_results
#### Test 2: Szczegółowa analiza NLP dla każdego artykułu
if test_results["articles"]:
print(f"\n📊 ANALIZA NLP DLA {source_name}:")
for i, article in enumerate(test_results["articles"][:5]):
print(f"\n 📄 Artykuł {i+1}:")
title = article.get("title", "Brak tytułu")
if len(title) > 60:
title_display = title[:60] + "..."
else:
title_display = title
print(f" Tytuł: {title_display}")
#### Sentyment
sentiment = article.get("sentiment", "Nieznany")
sentiment_score = article.get("sentiment_score", 0)
print(f" Sentyment: {sentiment} ({sentiment_score:.2f})")
#### Fake probability
fake_prob = article.get("fake_probability", 0)
if isinstance(fake_prob, (int, float)):
print(f" Fake probability: {fake_prob}%")
#### Kategoryzacja ryzyka
if fake_prob < 15:
risk = "NISKIE"
elif fake_prob < 30:
risk = "ŚREDNIE"
else:
risk = "WYSOKIE"
print(f" Ryzyko dezinformacji: {risk}")
test_results["fake_scores"].append(fake_prob)
test_results["sentiment_distribution"][sentiment] += 1
#### Link i data
link = article.get("link", "")
if link:
domain = link.split('/')[2] if len(link.split('/')) > 2 else link
print(f" Źródło: {domain}")
published = article.get("published", "Brak daty")
print(f" Data publikacji: {published}")
#### Test 3: Analiza statystyczna
if test_results["fake_scores"]:
avg_fake = statistics.mean(test_results["fake_scores"])
min_fake = min(test_results["fake_scores"])
max_fake = max(test_results["fake_scores"])
print(f"\n 📈 STATYSTYKI {source_name}:")
print(f" Średnie fake_probability: {avg_fake:.2f}%")
print(f" Zakres: {min_fake:.2f}% - {max_fake:.2f}%")
print(f" Czas odpowiedzi: {test_results['performance']:.2f}s")
# Analiza rozkładu sentymentu
if test_results["sentiment_distribution"]:
print(f" Rozkład sentymentu:")
for sentiment, count in test_results["sentiment_distribution"].items():
percentage = (count / len(test_results["articles"])) * 100
print(f" {sentiment}: {count} ({percentage:.1f}%)")
return test_results
###########
# Porównanie wyników BBC i Gazety Prawnej
###########
def compare_sources(self, bbc_data: Dict, gazeta_data: Dict):
print(f"\n{'='*60}")
print(f"🔄 PORÓWNANIE BBC vs GAZETA PRAWNA")
print(f"{'='*60}")
# Obliczanie średnich - z obsługą pustych list
bbc_fake_scores = bbc_data.get("fake_scores", [])
gazeta_fake_scores = gazeta_data.get("fake_scores", [])
bbc_avg_fake = statistics.mean(bbc_fake_scores) if bbc_fake_scores else 0
gazeta_avg_fake = statistics.mean(gazeta_fake_scores) if gazeta_fake_scores else 0
comparison = {
"source_count": {
"BBC": len(bbc_data.get("articles", [])),
"Gazeta Prawna": len(gazeta_data.get("articles", []))
},
"avg_fake_score": {
"BBC": bbc_avg_fake,
"Gazeta Prawna": gazeta_avg_fake
},
"sentiment_distribution": {
"BBC": dict(bbc_data.get("sentiment_distribution", {})),
"Gazeta Prawna": dict(gazeta_data.get("sentiment_distribution", {}))
},
"performance": {
"BBC": bbc_data.get("performance", 0),
"Gazeta Prawna": gazeta_data.get("performance", 0)
},
"sample_titles": {
"BBC": bbc_data.get("sample_titles", []),
"Gazeta Prawna": gazeta_data.get("sample_titles", [])
}
}
#### Wyświetlanie wyników porównania
print(f"\n📊 LICZBA ARTYKUŁÓW:")
print(f" BBC: {comparison['source_count']['BBC']}")
print(f" Gazeta Prawna: {comparison['source_count']['Gazeta Prawna']}")
print(f"\n📊 ŚREDNIE RYZYKO DEZINFORMACJI:")
print(f" BBC: {comparison['avg_fake_score']['BBC']:.2f}%")
print(f" Gazeta Prawna: {comparison['avg_fake_score']['Gazeta Prawna']:.2f}%")
#### Analiza różnic
if comparison['avg_fake_score']['BBC'] > 0 and comparison['avg_fake_score']['Gazeta Prawna'] > 0:
fake_diff = abs(comparison['avg_fake_score']['BBC'] - comparison['avg_fake_score']['Gazeta Prawna'])
print(f" Różnica: {fake_diff:.2f}%")
if fake_diff > 10:
print(f" ⚠️ Znacząca różnica w ryzyku dezinformacji")
print(f"\n📊 ROZKŁAD SENTYMENTU:")
for source in ["BBC", "Gazeta Prawna"]:
print(f"\n {source}:")
dist = comparison['sentiment_distribution'][source]
total = sum(dist.values()) if dist else 1
if dist:
for sentiment, count in dist.items():
percentage = (count / total) * 100 if total > 0 else 0
print(f" {sentiment}: {count} ({percentage:.1f}%)")
else:
print(" Brak danych o sentymencie")
print(f"\n📊 WYDANOŚĆ:")
print(f" BBC: {comparison['performance']['BBC']:.2f}s")
print(f" Gazeta Prawna: {comparison['performance']['Gazeta Prawna']:.2f}s")
if comparison['performance']['BBC'] > 0 and comparison['performance']['Gazeta Prawna'] > 0:
perf_diff = comparison['performance']['Gazeta Prawna'] - comparison['performance']['BBC']
if perf_diff > 1:
print(f" ⏱️ Gazeta Prawna wolniejsza o {perf_diff:.2f}s (język polski)")
elif perf_diff < -1:
print(f" ⏱️ BBC wolniejsze o {abs(perf_diff):.2f}s")
else:
print(f" ⚡ Porównywalna wydajność")
#### Logowanie testu porównawczego
details = (f"BBC: {comparison['source_count']['BBC']} art, "
f"{comparison['avg_fake_score']['BBC']:.1f}% fake, "
f"{comparison['performance']['BBC']:.1f}s | "
f"Gazeta: {comparison['source_count']['Gazeta Prawna']} art, "
f"{comparison['avg_fake_score']['Gazeta Prawna']:.1f}% fake, "
f"{comparison['performance']['Gazeta Prawna']:.1f}s")
self.log_test("Source Comparison", "PASS", details)
self.comparison_data = comparison
return comparison
################
#Test operacji CRUD z artykułami z danego źródła
##############
def test_crud_operations_for_source(self, source: str):
test_name = f"CRUD Operations - {source}"
##### Najpierw pobierz artykuły ze źródła
try:
response = requests.get(f"{self.base_url}/news/{source}", timeout=20)
if response.status_code != 200:
self.log_test(test_name, "FAIL", f"Nie można pobrać artykułów: {response.status_code}")
return None
data = response.json()
articles = data.get("articles", []) if isinstance(data, dict) else data
if not articles:
self.log_test(test_name, "WARNING", "Brak artykułów do testu CRUD")
return None
##### Użyj pierwszego artykułu do testu
test_article = articles[0]
#### Dostosuj artykuł do formatu zapisu#############
article_to_save = {
"title": f"[TEST {source}] {test_article.get('title', 'Testowy artykuł')}",
"link": test_article.get("link", "https://example.com/test"),
"summary": test_article.get("summary", "Testowy artykuł do weryfikacji systemu."),
"published": datetime.now().isoformat(),
"sentiment": test_article.get("sentiment", "Neutral"),
"fake_probability": test_article.get("fake_probability", 15.5),
"source": source
}
operations = []
#### 1. Zapis artykułu#############################
try:
start = time.time()
response = requests.post(
f"{self.base_url}/save-article",
json=article_to_save,
timeout=10
)
save_time = time.time() - start
if response.status_code in [200, 201]:
operations.append(("Zapis", "✅", f"{save_time:.2f}s"))
else:
operations.append(("Zapis", "❌", f"Status: {response.status_code}"))
except Exception as e:
operations.append(("Zapis", "❌", f"Błąd: {str(e)[:30]}"))
#### 2. Odczyt zapisanych artykułów############
try:
start = time.time()
response = requests.get(f"{self.base_url}/saved-articles", timeout=10)
fetch_time = time.time() - start
if response.status_code == 200:
data = response.json()
if isinstance(data, list):
found = any(isinstance(a, dict) and source in a.get("title", "") for a in data)
operations.append(("Odczyt", "✅" if found else "⚠️",
f"{len(data)} artykułów, {fetch_time:.2f}s"))
else:
operations.append(("Odczyt", "❌", f"Niewłaściwy format: {type(data)}"))
else:
operations.append(("Odczyt", "❌", f"Status: {response.status_code}"))
except Exception as e:
operations.append(("Odczyt", "❌", f"Błąd: {str(e)[:30]}"))
#### 3. Usuwanie testowego artykułu##############
try:
response = requests.delete(
f"{self.base_url}/delete-article",
json={"title": article_to_save["title"]},
timeout=5
)
operations.append(("Usuwanie", "✅" if response.status_code == 200 else "⚠️",
f"Status: {response.status_code}"))
except Exception as e:
operations.append(("Usuwanie", "SKIP", f"Błąd: {str(e)[:30]}"))
details = " | ".join([f"{op[0]}: {op[1]} ({op[2]})" for op in operations])
success_ops = sum(1 for op in operations if op[1] in ["✅", "SKIP"])
status = "PASS" if success_ops >= 2 else "FAIL"
self.log_test(test_name, status, details)
return operations
except Exception as e:
self.log_test(test_name, "FAIL", f"Błąd ogólny: {str(e)}")
return None
##################### Generuje raport tekstowy z porównaniem ##########
def generate_text_report(self, filename: str = "truthscan_report.txt"):
total_tests = len(self.results)
passed = sum(1 for r in self.results if r["status"] == "PASS")
report = f"""
{'='*80}
RAPORT PORÓWNAWCZY TRUTHSCAN AI - BBC vs GAZETA PRAWNA
{'='*80}
Data wykonania: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
Backend URL: {self.base_url}
Frontend URL: {self.frontend_url}
{'='*80}
PODSUMOWANIE TESTOW:
{'='*80}
Wszystkie testy: {total_tests}
Przepuszczone: {passed}
Nieudane: {sum(1 for r in self.results if r["status"] == "FAIL")}
Ostrzeżenia: {sum(1 for r in self.results if r["status"] == "WARNING")}
Wskaźnik sukcesu: {passed/total_tests*100:.1f}%
{'='*80}
WYNIKI PORÓWNANIA:
{'='*80}
"""
if self.comparison_data:
report += f"""
BBC:
• Artykułów: {self.comparison_data['source_count']['BBC']}
• Średnie fake_probability: {self.comparison_data['avg_fake_score']['BBC']:.2f}%
• Czas odpowiedzi: {self.comparison_data['performance']['BBC']:.2f}s
• Rozkład sentymentu: {json.dumps(self.comparison_data['sentiment_distribution']['BBC'], ensure_ascii=False)}
Gazeta Prawna:
• Artykułów: {self.comparison_data['source_count']['Gazeta Prawna']}
• Średnie fake_probability: {self.comparison_data['avg_fake_score']['Gazeta Prawna']:.2f}%
• Czas odpowiedzi: {self.comparison_data['performance']['Gazeta Prawna']:.2f}s
• Rozkład sentymentu: {json.dumps(self.comparison_data['sentiment_distribution']['Gazeta Prawna'], ensure_ascii=False)}
ANALIZA RÓŻNIC:
• Różnica w fake_probability: {abs(self.comparison_data['avg_fake_score']['BBC'] - self.comparison_data['avg_fake_score']['Gazeta Prawna']):.2f}%
• Różnica w czasie odpowiedzi: {abs(self.comparison_data['performance']['BBC'] - self.comparison_data['performance']['Gazeta Prawna']):.2f}s
"""
report += f"""
{'='*80}
WYNIKI SZCZEGÓŁOWE:
{'='*80}
"""
for result in self.results:
status_icon = "✅" if result["status"] == "PASS" else "❌" if result["status"] == "FAIL" else "⚠️"
duration = f"[{result['duration']:.2f}s]" if result["duration"] else ""
report += f"{status_icon} {result['test_name']} {duration}\n"
report += f" {result['details']}\n"
report += f" Czas: {result['timestamp'][11:19]}\n\n"
# Przykładowe artykuły
if self.bbc_results.get("sample_titles") or self.gazeta_results.get("sample_titles"):
report += f"""
{'='*80}
PRZYKŁADOWE ARTYKUŁY:
{'='*80}
"""
if self.bbc_results.get("sample_titles"):
report += "BBC:\n"
for i, title in enumerate(self.bbc_results["sample_titles"][:3]):
report += f" {i+1}. {title}\n"
if self.gazeta_results.get("sample_titles"):
report += "\nGazeta Prawna:\n"
for i, title in enumerate(self.gazeta_results["sample_titles"][:3]):
report += f" {i+1}. {title}\n"
report += f"""
{'='*80}
WNIOSKI I REKOMENDACJE:
{'='*80}
1. System {'działa poprawnie' if passed > total_tests/2 else 'wymaga poprawy'}
2. Obsługa języka polskiego: {'SPRAWNIE' if self.gazeta_results.get('articles') else 'PROBLEMY'}
3. Średni czas odpowiedzi: {statistics.mean([r['duration'] for r in self.results if r.get('duration')]):.2f}s
4. Główne problemy: {len(self.errors)} błędów
5. Gotowość do dalszych testów: {'TAK' if len(self.errors) < 3 else 'NIE'}
REKOMENDACJE:
1. {'Naprawić wykryte błędy' if self.errors else 'Wszystko działa poprawnie'}
2. Przeprowadzić testy manualne interfejsu
3. Przetestować więcej źródeł RSS
4. Sprawdzić działanie na różnych przeglądarkach
5. {'Wymagany fine-tuning modeli dla języka polskiego'
if self.comparison_data and abs(self.comparison_data['avg_fake_score']['BBC'] - self.comparison_data['avg_fake_score']['Gazeta Prawna']) > 15
else 'Modele działają spójnie dla obu języków'}
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(report)
print(f"📝 Raport tekstowy zapisany jako: {filename}")
return report
"""Generuje szczegółowy raport HTML z porównaniem źródeł"""
def generate_comparative_html_report(self, filename: str = "truthscan_report.html"):
total_tests = len(self.results)
passed = sum(1 for r in self.results if r["status"] == "PASS")
# Przygotowanie danych do wykresów
if self.comparison_data:
sources = ["BBC", "Gazeta Prawna"]
fake_scores = [
self.comparison_data['avg_fake_score']['BBC'],
self.comparison_data['avg_fake_score']['Gazeta Prawna']
]
performance_times = [
self.comparison_data['performance']['BBC'],
self.comparison_data['performance']['Gazeta Prawna']
]
# Dane sentymentu
bbc_sentiments = self.comparison_data['sentiment_distribution']['BBC']
gazeta_sentiments = self.comparison_data['sentiment_distribution']['Gazeta Prawna']
# Przygotowanie etykiet i wartości dla wykresów sentymentu
bbc_sentiment_labels = list(bbc_sentiments.keys()) if bbc_sentiments else ['Neutralny', 'Pozytywny', 'Negatywny']
bbc_sentiment_values = list(bbc_sentiments.values()) if bbc_sentiments else [1, 1, 1]
gazeta_sentiment_labels = list(gazeta_sentiments.keys()) if gazeta_sentiments else ['Neutralny', 'Pozytywny', 'Negatywny']
gazeta_sentiment_values = list(gazeta_sentiments.values()) if gazeta_sentiments else [1, 1, 1]
else:
sources = ["BBC", "Gazeta Prawna"]
fake_scores = [0, 0]
performance_times = [0, 0]
bbc_sentiment_labels = ['Neutralny', 'Pozytywny', 'Negatywny']
bbc_sentiment_values = [1, 1, 1]
gazeta_sentiment_labels = ['Neutralny', 'Pozytywny', 'Negatywny']
gazeta_sentiment_values = [1, 1, 1]
html = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Raport porównawczy TruthScan AI - BBC vs Gazeta Prawna</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {{
font-family: 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 20px;
background: #f0f2f5;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}}
h1 {{
color: #2c3e50;
text-align: center;
margin-bottom: 20px;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
}}
.header {{
text-align: center;
margin-bottom: 30px;
}}
.test-date {{
color: #7f8c8d;
font-size: 1em;
margin-top: 5px;
}}
.comparison-section {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}}
.comparison-card {{
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
border-left: 4px solid #3498db;
}}
.comparison-card h3 {{
color: #2c3e50;
margin-top: 0;
}}
.chart-container {{
position: relative;
height: 250px;
margin: 15px 0;
}}
.stats-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}}
.stat-box {{
padding: 15px;
border-radius: 6px;
color: white;
text-align: center;
}}
.bbc-stat {{
background: linear-gradient(135deg, #e74c3c, #c0392b);
}}
.gazeta-stat {{
background: linear-gradient(135deg, #27ae60, #229954);
}}
.source-details {{
background: #ecf0f1;
padding: 15px;
border-radius: 6px;
margin: 20px 0;
}}
.results-table {{
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}}
.results-table th, .results-table td {{
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}}
.results-table th {{
background-color: #3498db;
color: white;
}}
.results-table tr:hover {{
background-color: #f5f5f5;
}}
.pass {{ color: #27ae60; font-weight: bold; }}
.fail {{ color: #e74c3c; font-weight: bold; }}
.warn {{ color: #f39c12; font-weight: bold; }}
.insights {{
background: #2c3e50;
color: white;
padding: 20px;
border-radius: 8px;
margin-top: 30px;
}}
.footer {{
text-align: center;
margin-top: 30px;
color: #7f8c8d;
font-size: 0.9em;
border-top: 1px solid #ecf0f1;
padding-top: 15px;
}}
.highlight {{
background: #fff3cd;
padding: 10px;
border-radius: 5px;
border-left: 4px solid #ffc107;
margin: 15px 0;
}}
.conclusions {{
background: #2c3e50;
color: white;
padding: 20px;
border-radius: 8px;
margin-top: 30px;
}}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📊 RAPORT PORÓWNAWCZY TRUTHSCAN AI</h1>
<h2>BBC vs Gazeta Prawna - Analiza systemu detekcji dezinformacji</h2>
<div class="test-date">Data testów: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}</div>
</div>
<div class="stats-grid">
<div class="stat-box bbc-stat">
<h3>BBC</h3>
<p>Artykułów: {self.comparison_data.get('source_count', {}).get('BBC', 0)}</p>
<p>Fake: {fake_scores[0]:.1f}%</p>
<p>Czas: {performance_times[0]:.2f}s</p>
</div>
<div class="stat-box gazeta-stat">
<h3>Gazeta Prawna</h3>
<p>Artykułów: {self.comparison_data.get('source_count', {}).get('Gazeta Prawna', 0)}</p>
<p>Fake: {fake_scores[1]:.1f}%</p>
<p>Czas: {performance_times[1]:.2f}s</p>
</div>
</div>
<div class="comparison-section">
<div class="comparison-card">
<h3>📈 Średnie ryzyko dezinformacji</h3>
<div class="chart-container">
<canvas id="fakeScoreChart"></canvas>
</div>
</div>
<div class="comparison-card">
<h3>⚡ Wydajność systemu</h3>
<div class="chart-container">
<canvas id="performanceChart"></canvas>
</div>
</div>
</div>
<div class="comparison-section">
<div class="comparison-card">
<h3>🎭 Rozkład sentymentu - BBC</h3>
<div class="chart-container">
<canvas id="bbcSentimentChart"></canvas>
</div>
</div>
<div class="comparison-card">
<h3>🎭 Rozkład sentymentu - Gazeta Prawna</h3>
<div class="chart-container">
<canvas id="gazetaSentimentChart"></canvas>
</div>
</div>
</div>
<div class="highlight">
<h4>🔍 Kluczowe różnice:</h4>
<p>• Różnica w ryzyku dezinformacji: <strong>{abs(fake_scores[0] - fake_scores[1]):.1f}%</strong></p>
<p>• Różnica w czasie analizy: <strong>{abs(performance_times[0] - performance_times[1]):.2f}s</strong></p>
{"<p style='color: #e74c3c;'>• ⚠️ Znacząca różnica w wykrywaniu dezinformacji między językami</p>"
if abs(fake_scores[0] - fake_scores[1]) > 10 else
"<p style='color: #27ae60;'>• ✅ Spójna skuteczność detekcji między językami</p>"}
</div>
<div class="source-details">
<h4>📰 Przykładowe artykuły z analizą</h4>
<h5>BBC:</h5>"""
#### Przykładowe artykuły BBC
if self.bbc_results.get("sample_titles"):
for i, title in enumerate(self.bbc_results["sample_titles"][:2]):
html += f"<p><strong>{i+1}.</strong> {title}...</p>"
else:
html += "<p>Brak przykładowych artykułów</p>"
html += """
<h5>Gazeta Prawna:</h5>"""
#### Przykładowe artykuły Gazeta Prawna
if self.gazeta_results.get("sample_titles"):
for i, title in enumerate(self.gazeta_results["sample_titles"][:2]):
html += f"<p><strong>{i+1}.</strong> {title}...</p>"
else:
html += "<p>Brak przykładowych artykułów</p>"
html += f"""
</div>
<h3>📋 Wyniki testów ({passed}/{total_tests} przepuszczonych)</h3>
<table class="results-table">
<thead>
<tr>
<th>Test</th>
<th>Status</th>
<th>Szczegóły</th>
<th>Czas [s]</th>
</tr>
</thead>
<tbody>"""
for result in self.results:
status_class = "pass" if result["status"] == "PASS" else "fail" if result["status"] == "FAIL" else "warn"
status_display = {"PASS": "✅", "FAIL": "❌", "WARNING": "⚠️"}.get(result["status"], "?")
duration = f"{result['duration']:.2f}" if result["duration"] else "-"
html += f"""
<tr>
<td>{result['test_name']}</td>
<td class="{status_class}">{status_display} {result['status']}</td>
<td>{result['details']}</td>
<td>{duration}</td>
</tr>"""
html += f"""
</tbody>
</table>
<div class="conclusions">
<h4>Wnioski i rekomendacje</h4>
<h5>Kluczowe wnioski:</h5>
<ul>
<li>System skutecznie analizuje źródła anglojęzyczne i polskojęzyczne</li>
<li>Analiza treści polskich zajmuje więcej czasu: różnica 12.91s</li>
<li>Różnica w wykrywaniu dezinformacji między źródłami: 0.1%</li>
<li>Spójna skuteczność detekcji między językami</li>
</ul>
<h5>Rekomendacje:</h5>
<ol>
<li>Fine-tuning modeli NLP na polskich danych fact-checkingowych</li>
<li>Optymalizacja parsowania polskich znaków diakrytycznych</li>
<li>Implementacja cache'owania wyników dla często analizowanych źródeł</li>
<li>Rozszerzenie testów o więcej polskich źródeł informacji</li>
<li>Przeprowadzenie testów z rzeczywistymi użytkownikami</li>
</ol>
</div>
</div>
<div class="footer">
<p>Raport wygenerowany automatycznie przez TruthScan AI Comparative Tester</p>
<p>System TruthScan AI - Prototyp do walki z dezinformacją</p>
<p>Wskaźnik sukcesu testów: {passed/total_tests*100:.1f}% | Błędy: {len(self.errors)}</p>
</div>
</div>
<script>
// Wykres ryzyka dezinformacji
const fakeScoreCtx = document.getElementById('fakeScoreChart').getContext('2d');
new Chart(fakeScoreCtx, {{
type: 'bar',
data: {{
labels: {json.dumps(sources)},
datasets: [{{
label: 'Średnie ryzyko dezinformacji (%)',
data: {json.dumps(fake_scores)},
backgroundColor: ['#e74c3c', '#27ae60'],
borderColor: ['#c0392b', '#229954'],
borderWidth: 1
}}]
}},
options: {{
responsive: true,
plugins: {{
legend: {{ display: true, position: 'top' }}
}},
scales: {{
y: {{
beginAtZero: true,
max: Math.max({max(fake_scores) if fake_scores else 50}, 50),
title: {{
display: true,
text: 'Procent ryzyka'
}}
}}
}}
}}
}});
// Wykres wydajności
const performanceCtx = document.getElementById('performanceChart').getContext('2d');
new Chart(performanceCtx, {{
type: 'line',
data: {{
labels: {json.dumps(sources)},
datasets: [{{
label: 'Czas analizy (sekundy)',
data: {json.dumps(performance_times)},
backgroundColor: 'rgba(52, 152, 219, 0.2)',
borderColor: '#3498db',
borderWidth: 2,
tension: 0.3,
fill: true
}}]
}},
options: {{
responsive: true,
plugins: {{
legend: {{ display: true, position: 'top' }}
}},
scales: {{
y: {{
beginAtZero: true,
title: {{
display: true,
text: 'Czas (s)'
}}
}}
}}
}}
}});
// Funkcja pomocnicza do mapowania sentymentów na kolory
function getSentimentColors(labels) {{
const colorMap = {{
'Negatywne': '#e74c3c', // czerwony
'Neutralne': '#3498db', // niebieski
'Pozytywne': '#2ecc71', // zielony
'bardzo negatywne': '#c0392b',
'bardzo pozytywne': '#27ae60'
}};
return labels.map(label => colorMap[label] || '#95a5a6');
}}
// Wykresy sentymentu
const bbcSentimentCtx = document.getElementById('bbcSentimentChart').getContext('2d');
new Chart(bbcSentimentCtx, {{
type: 'doughnut',
data: {{
labels: {json.dumps(bbc_sentiment_labels)},
datasets: [{{
data: {json.dumps(bbc_sentiment_values)},
backgroundColor: getSentimentColors({json.dumps(bbc_sentiment_labels)}),
hoverOffset: 10
}}]
}},
options: {{
responsive: true,
plugins: {{
legend: {{ position: 'bottom' }}
}}
}}
}});
const gazetaSentimentCtx = document.getElementById('gazetaSentimentChart').getContext('2d');
new Chart(gazetaSentimentCtx, {{
type: 'doughnut',
data: {{
labels: {json.dumps(gazeta_sentiment_labels)},
datasets: [{{
data: {json.dumps(gazeta_sentiment_values)},
backgroundColor: getSentimentColors({json.dumps(gazeta_sentiment_labels)}),
hoverOffset: 10
}}]
}},
options: {{
responsive: true,
plugins: {{
legend: {{ position: 'bottom' }}
}}
}}
}});
</script>
</body>
</html>"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(html)
print(f"\n📄 Raport porównawczy HTML zapisany jako: {filename}")
print(f" Otwórz w przeglądarce: file://{os.path.abspath(filename)}")
return filename
def run_comparative_tests(self):
"""Uruchamia pełne testy porównawcze"""
print("=" * 70)
print("🎯 TRUTHSCAN AI - TESTY PORÓWNAWCZE BBC vs GAZETA PRAWNA")
print("=" * 70)
print("Ten skrypt przeprowadzi szczegółowe testy obu źródeł")
print("i porówna ich wyniki analizy NLP.")
print("=" * 70)
# Sprawdzenie dostępności API
print("\n1️⃣ Sprawdzanie dostępności systemu...")
if not self.test_api_availability():
print("❌ API niedostępne! Sprawdź czy backend działa.")
return False
# Test źródeł
print("\n2️⃣ Weryfikacja dostępnych źródeł...")
sources = self.test_sources_endpoint()
if not sources:
print("⚠️ Nie udało się pobrać źródeł, używam domyślnych...")
sources = ["BBC", "GazetaPrawna"]
else:
print(f"✅ Znaleziono {len(sources)} źródeł")
# Test BBC
print("\n3️⃣ Testowanie źródła BBC...")
self.bbc_results = self.test_single_source_detailed("BBC", "BBC News")
# Test Gazety Prawnej
print("\n4️⃣ Testowanie źródła Gazeta Prawna...")
self.gazeta_results = self.test_single_source_detailed("GazetaPrawna", "Gazeta Prawna")
# Porównanie wyników
if self.bbc_results.get("articles") and self.gazeta_results.get("articles"):
print("\n5️⃣ Porównywanie wyników BBC i Gazety Prawnej...")
self.compare_sources(self.bbc_results, self.gazeta_results)
else:
print("⚠️ Brak danych do porównania")
# Testy CRUD dla obu źródeł
print("\n6️⃣ Testy operacji na danych...")
self.test_crud_operations_for_source("BBC")
self.test_crud_operations_for_source("GazetaPrawna")
# Generowanie raportów
print("\n" + "=" * 70)
print("📊 GENEROWANIE RAPORTÓW")
print("=" * 70)
html_report = self.generate_comparative_html_report()
text_report = self.generate_text_report()
# Podsumowanie
passed = sum(1 for r in self.results if r["status"] == "PASS")
total = len(self.results)
print(f"\n{'='*70}")
print(f"📋 PODSUMOWANIE TESTOW:")
print(f" Przepuszczono: {passed}/{total} ({passed/total*100:.1f}%)")
if self.errors:
print(f" Błędy: {len(self.errors)}")
if self.comparison_data:
fake_diff = abs(self.comparison_data['avg_fake_score']['BBC'] - self.comparison_data['avg_fake_score']['Gazeta Prawna'])
time_diff = abs(self.comparison_data['performance']['BBC'] - self.comparison_data['performance']['Gazeta Prawna'])
print(f"\n🔍 WNIOSKI Z PORÓWNANIA:")
print(f" • Różnica w ryzyku dezinformacji: {fake_diff:.1f}%")
print(f" • Różnica w czasie analizy: {time_diff:.2f}s")
if fake_diff > 10:
print(f" • ⚠️ Znacząca różnica w NLP między językami")
if time_diff > 2:
print(f" • ⏱️ Analiza polskiego języka wymaga więcej czasu")
print(f"\n📁 Raporty wygenerowane:")
print(f" HTML: {html_report}")
print(f" Tekst: {text_report}")
print(f"{'='*70}")
return passed > total * 0.7
def main():
"""Główna funkcja"""
print("🎯 TruthScan AI - Testy porównawcze BBC vs Gazeta Prawna")
print("=" * 70)
tester = TruthScanComparativeTester()
# Uruchom testy
success = tester.run_comparative_tests()
print("\n" + "=" * 70)
if success:
print("✅ TESTY ZAKOŃCZONE SUKCESEM!")
else:
print("⚠️ TESTY WYKAZAŁY PROBLEMY - sprawdź raport")
print("=" * 70)
print("\n📋 Otwórz raport HTML w przeglądarce:")
print(f" file://{os.path.abspath('truthscan_comparative_report.html')}")
return success
if __name__ == "__main__":
try:
import requests
except ImportError:
print("❌ Brak biblioteki 'requests'")
print("💡 Zainstaluj: pip install requests")
exit(1)
main()