Spaces:
Sleeping
Sleeping
| """ | |
| π§ͺ ESG Intelligence Platform - Comprehensive Test Suite | |
| Tests all functionality: classification, visualization, edge cases | |
| """ | |
| import sys | |
| sys.path.insert(0, '/home/bechirdardouri/Downloads/esg_app') | |
| import numpy as np | |
| import pandas as pd | |
| from collections import Counter | |
| # Import the app components | |
| from app import ( | |
| ESGClassifier, CONFIG, PATTERNS, SAMPLES, | |
| create_radar, create_bars, create_batch_charts, | |
| analyze_text, analyze_batch | |
| ) | |
| class TestResults: | |
| def __init__(self): | |
| self.passed = 0 | |
| self.failed = 0 | |
| self.errors = [] | |
| def check(self, condition, test_name, details=""): | |
| if condition: | |
| self.passed += 1 | |
| print(f" β {test_name}") | |
| else: | |
| self.failed += 1 | |
| self.errors.append(f"{test_name}: {details}") | |
| print(f" β {test_name} - {details}") | |
| def summary(self): | |
| total = self.passed + self.failed | |
| print(f"\n{'='*60}") | |
| print(f"π TEST SUMMARY: {self.passed}/{total} passed ({100*self.passed/total:.1f}%)") | |
| if self.errors: | |
| print(f"\nβ FAILURES:") | |
| for e in self.errors: | |
| print(f" - {e}") | |
| print('='*60) | |
| return self.failed == 0 | |
| results = TestResults() | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 1: Configuration Validation") | |
| print("="*60) | |
| # Test CONFIG initialization | |
| results.check(CONFIG.labels == ['E', 'S', 'G', 'non_ESG'], | |
| "Labels defined correctly") | |
| results.check(all(l in CONFIG.thresholds for l in CONFIG.labels), | |
| "Thresholds defined for all labels") | |
| results.check(all(l in CONFIG.colors for l in CONFIG.labels), | |
| "Colors defined for all labels") | |
| results.check(all(l in CONFIG.icons for l in CONFIG.labels), | |
| "Icons defined for all labels") | |
| results.check(len(CONFIG.keywords['E']) > 10, | |
| "Environmental keywords list is populated") | |
| results.check(len(CONFIG.keywords['S']) > 10, | |
| "Social keywords list is populated") | |
| results.check(len(CONFIG.keywords['G']) > 10, | |
| "Governance keywords list is populated") | |
| # Test thresholds are reasonable | |
| for label, thresh in CONFIG.thresholds.items(): | |
| results.check(0.0 < thresh < 1.0, | |
| f"Threshold for {label} is valid ({thresh})") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 2: Classifier Basic Functionality") | |
| print("="*60) | |
| classifier = ESGClassifier() | |
| # Test empty input | |
| result = classifier.classify("") | |
| results.check(result['predictions'] == ['non_ESG'], | |
| "Empty text returns non_ESG") | |
| results.check(result['confidence'] > 0, | |
| "Empty text has valid confidence") | |
| # Test None-like input | |
| result = classifier.classify(" ") | |
| results.check(result['predictions'] == ['non_ESG'], | |
| "Whitespace-only text returns non_ESG") | |
| # Test score structure | |
| result = classifier.classify("test text") | |
| results.check(all(l in result['scores'] for l in CONFIG.labels), | |
| "All labels have scores") | |
| results.check(all(0 <= s <= 1 for s in result['scores'].values()), | |
| "All scores are in [0, 1] range") | |
| results.check('predictions' in result and 'confidence' in result, | |
| "Result has predictions and confidence") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 3: Environmental Classification") | |
| print("="*60) | |
| env_texts = [ | |
| "We are committed to reducing carbon emissions by 50% by 2030.", | |
| "Our solar and wind renewable energy investments totaled $100 million.", | |
| "The company achieved carbon neutrality through sustainable practices.", | |
| "Deforestation in our supply chain has been reduced through conservation efforts.", | |
| "Our waste management and recycling program diverted 90% from landfills.", | |
| ] | |
| for i, text in enumerate(env_texts): | |
| result = classifier.classify(text) | |
| has_E = 'E' in result['predictions'] | |
| results.check(has_E, f"Environmental text {i+1} detected as E", | |
| f"Got: {result['predictions']}, Score: {result['scores']['E']:.3f}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 4: Social Classification") | |
| print("="*60) | |
| social_texts = [ | |
| "Our diversity and inclusion initiatives increased female leadership to 40%.", | |
| "Employee health and safety remains our top priority.", | |
| "We invested in workforce training and community development programs.", | |
| "The company supports human rights throughout our supply chain.", | |
| "Worker welfare and fair labor practices are central to our operations.", | |
| ] | |
| for i, text in enumerate(social_texts): | |
| result = classifier.classify(text) | |
| has_S = 'S' in result['predictions'] | |
| results.check(has_S, f"Social text {i+1} detected as S", | |
| f"Got: {result['predictions']}, Score: {result['scores']['S']:.3f}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 5: Governance Classification") | |
| print("="*60) | |
| gov_texts = [ | |
| "The Board of Directors approved new governance policies.", | |
| "Our anti-corruption and ethics compliance program was enhanced.", | |
| "Executive compensation is now tied to transparency metrics.", | |
| "Independent audit committee oversight was strengthened.", | |
| "Shareholder accountability mechanisms were improved.", | |
| ] | |
| for i, text in enumerate(gov_texts): | |
| result = classifier.classify(text) | |
| has_G = 'G' in result['predictions'] | |
| results.check(has_G, f"Governance text {i+1} detected as G", | |
| f"Got: {result['predictions']}, Score: {result['scores']['G']:.3f}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 6: Non-ESG Classification") | |
| print("="*60) | |
| non_esg_texts = [ | |
| "Q3 revenue increased by 15% compared to last year.", | |
| "The company acquired TechCorp for $500 million.", | |
| "Our new product launch exceeded sales expectations.", | |
| "Operating margins improved due to cost optimization.", | |
| "The merger will create significant synergies.", | |
| ] | |
| for i, text in enumerate(non_esg_texts): | |
| result = classifier.classify(text) | |
| has_non_esg = 'non_ESG' in result['predictions'] | |
| esg_detected = any(l in result['predictions'] for l in ['E', 'S', 'G']) | |
| results.check(has_non_esg or not esg_detected, | |
| f"Non-ESG text {i+1} correctly classified", | |
| f"Got: {result['predictions']}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 7: Multi-Label Classification") | |
| print("="*60) | |
| multi_texts = [ | |
| ("Our sustainability report covers environmental emissions and board governance oversight.", | |
| ['E', 'G']), | |
| ("Employee diversity programs and carbon reduction targets were achieved.", | |
| ['E', 'S']), | |
| ("The board approved new worker safety and environmental policies.", | |
| ['E', 'S', 'G']), | |
| ] | |
| for text, expected in multi_texts: | |
| result = classifier.classify(text) | |
| detected = [l for l in ['E', 'S', 'G'] if l in result['predictions']] | |
| # Check if at least some expected labels are detected | |
| overlap = set(detected) & set(expected) | |
| results.check(len(overlap) > 0, | |
| f"Multi-label: detected {detected}", | |
| f"Expected some of {expected}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 8: Keyword Finding") | |
| print("="*60) | |
| text = "Our carbon emissions were reduced through renewable energy and solar power investments." | |
| keywords = classifier.find_keywords(text) | |
| results.check('E' in keywords, "Environmental keywords found") | |
| results.check(any(k in keywords.get('E', []) for k in ['carbon', 'emission', 'renewable', 'solar', 'energy']), | |
| "Correct E keywords identified") | |
| text2 = "Employee diversity and workforce training programs expanded." | |
| keywords2 = classifier.find_keywords(text2) | |
| results.check('S' in keywords2, "Social keywords found") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 9: Text Highlighting") | |
| print("="*60) | |
| text = "Carbon emissions and renewable energy" | |
| keywords = {'E': ['carbon', 'renewable', 'energy']} | |
| highlighted = classifier.highlight(text, keywords) | |
| results.check('<span' in highlighted, "Highlighting adds span tags") | |
| results.check('background:' in highlighted, "Highlighting includes background color") | |
| results.check('border-radius' in highlighted, "Highlighting includes styling") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 10: Visualization Functions") | |
| print("="*60) | |
| test_scores = {'E': 0.7, 'S': 0.5, 'G': 0.3, 'non_ESG': 0.2} | |
| test_preds = ['E', 'S'] | |
| # Test radar chart | |
| try: | |
| radar = create_radar(test_scores) | |
| results.check(radar is not None, "Radar chart created successfully") | |
| results.check(hasattr(radar, 'data'), "Radar chart has data attribute") | |
| except Exception as e: | |
| results.check(False, "Radar chart creation", str(e)) | |
| # Test bar chart | |
| try: | |
| bars = create_bars(test_scores, test_preds) | |
| results.check(bars is not None, "Bar chart created successfully") | |
| results.check(hasattr(bars, 'data'), "Bar chart has data attribute") | |
| except Exception as e: | |
| results.check(False, "Bar chart creation", str(e)) | |
| # Test batch charts | |
| try: | |
| test_results = [ | |
| {'scores': {'E': 0.8, 'S': 0.3, 'G': 0.2, 'non_ESG': 0.1}, 'predictions': ['E']}, | |
| {'scores': {'E': 0.2, 'S': 0.7, 'G': 0.4, 'non_ESG': 0.2}, 'predictions': ['S', 'G']}, | |
| {'scores': {'E': 0.1, 'S': 0.1, 'G': 0.1, 'non_ESG': 0.8}, 'predictions': ['non_ESG']}, | |
| ] | |
| fig1, fig2 = create_batch_charts(test_results) | |
| results.check(fig1 is not None and fig2 is not None, "Batch charts created successfully") | |
| except Exception as e: | |
| results.check(False, "Batch charts creation", str(e)) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 11: analyze_text Function") | |
| print("="*60) | |
| try: | |
| output = analyze_text("Carbon emissions reduction through renewable energy.") | |
| results.check(len(output) == 6, "analyze_text returns 6 outputs") | |
| results.check(isinstance(output[0], str), "Pills output is string (HTML)") | |
| results.check(isinstance(output[1], str), "Highlighted text is string (HTML)") | |
| results.check(isinstance(output[2], str), "Explanation is string") | |
| results.check(output[3] is not None, "Radar chart is not None") | |
| results.check(output[4] is not None, "Bar chart is not None") | |
| results.check(isinstance(output[5], str), "Score HTML is string") | |
| except Exception as e: | |
| results.check(False, "analyze_text execution", str(e)) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 12: Sample Texts") | |
| print("="*60) | |
| for name, text in SAMPLES.items(): | |
| result = classifier.classify(text) | |
| results.check(len(result['predictions']) > 0, | |
| f"Sample '{name}' produces predictions") | |
| results.check(result['confidence'] > 0, | |
| f"Sample '{name}' has valid confidence") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 13: Edge Cases") | |
| print("="*60) | |
| # Very short text | |
| result = classifier.classify("Hi") | |
| results.check('predictions' in result, "Very short text handled") | |
| # Very long text | |
| long_text = "Carbon emissions reduction. " * 100 | |
| result = classifier.classify(long_text) | |
| results.check('predictions' in result, "Very long text handled") | |
| results.check('E' in result['predictions'], "Long environmental text detected") | |
| # Special characters | |
| special_text = "Carbon emissions (CO2) - renewable energy! πΏ" | |
| result = classifier.classify(special_text) | |
| results.check('predictions' in result, "Special characters handled") | |
| # Numbers in text | |
| num_text = "We reduced carbon emissions by 50% in 2024." | |
| result = classifier.classify(num_text) | |
| results.check('predictions' in result, "Numbers in text handled") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 14: Score Consistency") | |
| print("="*60) | |
| # Same text should produce same scores (deterministic) | |
| text = "Carbon emissions and renewable energy investments." | |
| result1 = classifier.classify(text) | |
| result2 = classifier.classify(text) | |
| results.check(result1['scores'] == result2['scores'], | |
| "Same input produces consistent scores") | |
| results.check(result1['predictions'] == result2['predictions'], | |
| "Same input produces consistent predictions") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\n" + "="*60) | |
| print("π§ͺ TEST 15: Threshold Behavior") | |
| print("="*60) | |
| # Test that predictions respect thresholds | |
| for _ in range(10): | |
| text = np.random.choice(list(SAMPLES.values())) | |
| result = classifier.classify(text) | |
| for label in CONFIG.labels: | |
| if label in result['predictions']: | |
| # If predicted, score should be >= threshold | |
| results.check(result['scores'][label] >= CONFIG.thresholds[label] * 0.95, # small tolerance | |
| f"Threshold respected for {label}", | |
| f"Score {result['scores'][label]:.3f} < threshold {CONFIG.thresholds[label]}") | |
| break # Just test one per sample | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # FINAL SUMMARY | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| success = results.summary() | |
| sys.exit(0 if success else 1) | |