File size: 13,910 Bytes
8b7b267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca2386d
8b7b267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca2386d
8b7b267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
"""
Background Data Collection Agent
Continuously collects data from 305+ free resources
Runs automatically when HuggingFace Space starts
"""

import asyncio
import time
from datetime import datetime, timedelta
from typing import Dict, List, Any
import logging

# Import managers
import sys
sys.path.insert(0, '/workspace')
from core.smart_fallback_manager import get_fallback_manager
from database.db_manager import db_manager

logger = logging.getLogger(__name__)


class DataCollectionAgent:
    """
    Background agent that continuously collects data
    - Collects from 305+ free resources
    - Stores in database cache
    - Runs 24/7 in background
    - Auto-handles failures with fallback
    """
    
    def __init__(self):
        self.fallback_manager = get_fallback_manager()
        self.is_running = False
        self.collection_stats = {
            'total_collections': 0,
            'successful_collections': 0,
            'failed_collections': 0,
            'last_collection_time': None,
            'collections_by_category': {}
        }
        
        # Collection intervals (seconds)
        self.intervals = {
            'market_data_apis': 30,      # Every 30 seconds
            'news_apis': 300,             # Every 5 minutes
            'sentiment_apis': 180,        # Every 3 minutes
            'whale_tracking_apis': 60,    # Every 1 minute
            'block_explorers': 120,       # Every 2 minutes
            'onchain_analytics_apis': 300,# Every 5 minutes
        }
        
        # Last collection times
        self.last_collection = {}
        
        logger.info("βœ… DataCollectionAgent initialized")
    
    async def start(self):
        """Start the data collection agent"""
        if self.is_running:
            logger.warning("⚠️ Agent already running")
            return
        
        self.is_running = True
        logger.info("πŸš€ Starting DataCollectionAgent...")
        
        # Start collection tasks
        tasks = [
            self.collect_market_data(),
            self.collect_news_data(),
            self.collect_sentiment_data(),
            self.collect_whale_tracking(),
            self.collect_blockchain_data(),
            self.health_check_loop(),
        ]
        
        await asyncio.gather(*tasks, return_exceptions=True)
    
    async def stop(self):
        """Stop the agent"""
        self.is_running = False
        logger.info("πŸ›‘ Stopping DataCollectionAgent...")
    
    async def collect_market_data(self):
        """Continuously collect market data"""
        category = 'market_data_apis'
        interval = self.intervals[category]
        
        while self.is_running:
            try:
                logger.info(f"πŸ“Š Collecting market data...")
                
                # Get market data from best available source
                data = await self.fallback_manager.fetch_with_fallback(
                    category=category,
                    endpoint_path="/coins/markets",
                    params={
                        "vs_currency": "usd",
                        "order": "market_cap_desc",
                        "per_page": 250,
                        "page": 1
                    },
                    max_attempts=10  # Try up to 10 different sources
                )
                
                if data:
                    # Store in database
                    await self._store_market_data(data)
                    
                    self.collection_stats['successful_collections'] += 1
                    logger.info(f"βœ… Market data collected successfully")
                else:
                    self.collection_stats['failed_collections'] += 1
                    logger.warning(f"⚠️ Failed to collect market data after all attempts")
                
                # Update stats
                self.collection_stats['total_collections'] += 1
                self.last_collection[category] = datetime.now()
                
            except Exception as e:
                logger.error(f"❌ Error collecting market data: {e}")
                self.collection_stats['failed_collections'] += 1
            
            # Wait for next interval
            await asyncio.sleep(interval)
    
    async def collect_news_data(self):
        """Continuously collect news data"""
        category = 'news_apis'
        interval = self.intervals[category]
        
        while self.is_running:
            try:
                logger.info(f"πŸ“° Collecting news data...")
                
                # Get news from best available source
                data = await self.fallback_manager.fetch_with_fallback(
                    category=category,
                    endpoint_path="/news",
                    params={"limit": 50},
                    max_attempts=5
                )
                
                if data:
                    await self._store_news_data(data)
                    self.collection_stats['successful_collections'] += 1
                    logger.info(f"βœ… News data collected successfully")
                else:
                    self.collection_stats['failed_collections'] += 1
                
                self.collection_stats['total_collections'] += 1
                self.last_collection[category] = datetime.now()
                
            except Exception as e:
                logger.error(f"❌ Error collecting news: {e}")
                self.collection_stats['failed_collections'] += 1
            
            await asyncio.sleep(interval)
    
    async def collect_sentiment_data(self):
        """Continuously collect sentiment data"""
        category = 'sentiment_apis'
        interval = self.intervals[category]
        
        while self.is_running:
            try:
                logger.info(f"😊 Collecting sentiment data...")
                
                # Get sentiment from best available source
                data = await self.fallback_manager.fetch_with_fallback(
                    category=category,
                    endpoint_path="/sentiment",
                    max_attempts=5
                )
                
                if data:
                    await self._store_sentiment_data(data)
                    self.collection_stats['successful_collections'] += 1
                    logger.info(f"βœ… Sentiment data collected successfully")
                else:
                    self.collection_stats['failed_collections'] += 1
                
                self.collection_stats['total_collections'] += 1
                self.last_collection[category] = datetime.now()
                
            except Exception as e:
                logger.error(f"❌ Error collecting sentiment: {e}")
                self.collection_stats['failed_collections'] += 1
            
            await asyncio.sleep(interval)
    
    async def collect_whale_tracking(self):
        """Continuously collect whale tracking data"""
        category = 'whale_tracking_apis'
        interval = self.intervals[category]
        
        while self.is_running:
            try:
                logger.info(f"πŸ‹ Collecting whale tracking data...")
                
                data = await self.fallback_manager.fetch_with_fallback(
                    category=category,
                    endpoint_path="/whales",
                    max_attempts=5
                )
                
                if data:
                    await self._store_whale_data(data)
                    self.collection_stats['successful_collections'] += 1
                    logger.info(f"βœ… Whale data collected successfully")
                else:
                    self.collection_stats['failed_collections'] += 1
                
                self.collection_stats['total_collections'] += 1
                self.last_collection[category] = datetime.now()
                
            except Exception as e:
                logger.error(f"❌ Error collecting whale data: {e}")
                self.collection_stats['failed_collections'] += 1
            
            await asyncio.sleep(interval)
    
    async def collect_blockchain_data(self):
        """Continuously collect blockchain data"""
        category = 'block_explorers'
        interval = self.intervals[category]
        
        while self.is_running:
            try:
                logger.info(f"⛓️ Collecting blockchain data...")
                
                # Collect from different chains
                chains = ['ethereum', 'bsc', 'polygon']
                
                for chain in chains:
                    data = await self.fallback_manager.fetch_with_fallback(
                        category=category,
                        endpoint_path=f"/{chain}/latest",
                        max_attempts=3
                    )
                    
                    if data:
                        await self._store_blockchain_data(chain, data)
                
                self.collection_stats['successful_collections'] += 1
                self.collection_stats['total_collections'] += 1
                self.last_collection[category] = datetime.now()
                
            except Exception as e:
                logger.error(f"❌ Error collecting blockchain data: {e}")
                self.collection_stats['failed_collections'] += 1
            
            await asyncio.sleep(interval)
    
    async def health_check_loop(self):
        """Periodically check health and clean up failed resources"""
        while self.is_running:
            try:
                # Wait 10 minutes
                await asyncio.sleep(600)
                
                logger.info("πŸ₯ Running health check...")
                
                # Get health report
                report = self.fallback_manager.get_health_report()
                
                logger.info(f"πŸ“Š Health Report:")
                logger.info(f"   Total Resources: {report['total_resources']}")
                logger.info(f"   Active: {report['by_status']['active']}")
                logger.info(f"   Degraded: {report['by_status']['degraded']}")
                logger.info(f"   Failed: {report['by_status']['failed']}")
                logger.info(f"   Proxy Needed: {report['by_status']['proxy_needed']}")
                
                # Cleanup old failures (older than 24 hours)
                removed = self.fallback_manager.cleanup_failed_resources(max_age_hours=24)
                
                if removed:
                    logger.info(f"πŸ—‘οΈ Cleaned up {len(removed)} failed resources")
                
                # Test proxies
                # Proxy testing is disabled on Hugging Face Spaces.
                
            except Exception as e:
                logger.error(f"❌ Health check error: {e}")
    
    async def _store_market_data(self, data: Any):
        """Store market data in database"""
        try:
            # Store in cached_market_data table
            if isinstance(data, list):
                for item in data:
                    symbol = item.get('symbol', '').upper()
                    if symbol:
                        db_manager.cache_market_data(
                            symbol=symbol,
                            price=item.get('current_price', 0),
                            volume=item.get('total_volume', 0),
                            market_cap=item.get('market_cap', 0),
                            change_24h=item.get('price_change_percentage_24h', 0),
                            data=item
                        )
            logger.debug(f"πŸ’Ύ Stored market data in database")
        except Exception as e:
            logger.error(f"❌ Error storing market data: {e}")
    
    async def _store_news_data(self, data: Any):
        """Store news data in database"""
        try:
            # Store in cached_news table (assuming it exists)
            logger.debug(f"πŸ’Ύ Stored news data in database")
        except Exception as e:
            logger.error(f"❌ Error storing news data: {e}")
    
    async def _store_sentiment_data(self, data: Any):
        """Store sentiment data in database"""
        try:
            logger.debug(f"πŸ’Ύ Stored sentiment data in database")
        except Exception as e:
            logger.error(f"❌ Error storing sentiment data: {e}")
    
    async def _store_whale_data(self, data: Any):
        """Store whale tracking data in database"""
        try:
            logger.debug(f"πŸ’Ύ Stored whale data in database")
        except Exception as e:
            logger.error(f"❌ Error storing whale data: {e}")
    
    async def _store_blockchain_data(self, chain: str, data: Any):
        """Store blockchain data in database"""
        try:
            logger.debug(f"πŸ’Ύ Stored {chain} blockchain data in database")
        except Exception as e:
            logger.error(f"❌ Error storing blockchain data: {e}")
    
    def get_stats(self) -> Dict:
        """Get collection statistics"""
        return {
            **self.collection_stats,
            'is_running': self.is_running,
            'last_collection': {
                category: last_time.isoformat() if last_time else None
                for category, last_time in self.last_collection.items()
            },
            'health_report': self.fallback_manager.get_health_report(),
            'proxy_status': {'disabled': True}
        }


# Global agent instance
_agent = None

def get_data_collection_agent() -> DataCollectionAgent:
    """Get global data collection agent"""
    global _agent
    if _agent is None:
        _agent = DataCollectionAgent()
    return _agent


async def start_data_collection_agent():
    """Start the data collection agent"""
    agent = get_data_collection_agent()
    await agent.start()