Spaces:
Sleeping
Sleeping
Download TruthScan AI_backend/truthscan_test.py from Bjornpool/truthscan-ai-backend: direct link, hf CLI and curl.
- Browser
- Download file 44.2 kB
-
https://huggingface.co/spaces/Bjornpool/truthscan-ai-backend/resolve/982bc35d1541cb4935b1807d664ed40e5ffc815b/TruthScan%20AI_backend/truthscan_test.py
- Command line
-
hf download 'hf://spaces/Bjornpool/truthscan-ai-backend@982bc35d1541cb4935b1807d664ed40e5ffc815b/TruthScan AI_backend/truthscan_test.py'
-
curl -L -o truthscan_test.py https://huggingface.co/spaces/Bjornpool/truthscan-ai-backend/resolve/982bc35d1541cb4935b1807d664ed40e5ffc815b/TruthScan%20AI_backend/truthscan_test.py
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() |