/** * Scraping Control Panel for Legal Dashboard * Manages web scraping operations, real-time monitoring, and results display */ class ScrapingControlPanel { constructor() { this.baseEndpoint = '/api/scraping'; this.currentJob = null; this.isRunning = false; this.statusInterval = null; this.logs = []; this.initializeEventListeners(); this.loadScrapingStatus(); } initializeEventListeners() { // Start scraping button const startBtn = document.getElementById('startScrapingBtn'); if (startBtn) { startBtn.addEventListener('click', () => this.startScraping()); } // Stop scraping button const stopBtn = document.getElementById('stopScrapingBtn'); if (stopBtn) { stopBtn.addEventListener('click', () => this.stopScraping()); } // Refresh results button const refreshBtn = document.getElementById('refreshResultsBtn'); if (refreshBtn) { refreshBtn.addEventListener('click', () => this.loadScrapingResults()); } // Clear logs button const clearLogsBtn = document.getElementById('clearLogsBtn'); if (clearLogsBtn) { clearLogsBtn.addEventListener('click', () => this.clearLogs()); } } async startScraping() { if (this.isRunning) { this.showToast('اسکرپینگ در حال انجام است', 'warning'); return; } const scrapingConfig = this.getScrapingConfig(); if (!scrapingConfig.url) { this.showToast('لطفاً URL را وارد کنید', 'error'); return; } try { this.isRunning = true; this.updateStartButton(true); this.addLog('شروع اسکرپینگ...', 'info'); const response = await fetchWithErrorHandling(`${this.baseEndpoint}/start`, { method: 'POST', body: JSON.stringify(scrapingConfig) }); this.currentJob = response.job_id; this.showToast('اسکرپینگ شروع شد', 'success'); this.addLog(`شغل اسکرپینگ ایجاد شد: ${this.currentJob}`, 'success'); // Start monitoring this.startStatusMonitoring(); } catch (error) { this.showToast(`خطا در شروع اسکرپینگ: ${error.message}`, 'error'); this.addLog(`خطا در شروع اسکرپینگ: ${error.message}`, 'error'); this.isRunning = false; this.updateStartButton(false); } } async stopScraping() { if (!this.isRunning) { this.showToast('هیچ اسکرپینگی در حال انجام نیست', 'warning'); return; } try { const response = await fetchWithErrorHandling(`${this.baseEndpoint}/stop`, { method: 'POST', body: JSON.stringify({ job_id: this.currentJob }) }); this.showToast('اسکرپینگ متوقف شد', 'success'); this.addLog('اسکرپینگ متوقف شد', 'info'); this.isRunning = false; this.currentJob = null; this.updateStartButton(false); this.stopStatusMonitoring(); } catch (error) { this.showToast(`خطا در توقف اسکرپینگ: ${error.message}`, 'error'); this.addLog(`خطا در توقف اسکرپینگ: ${error.message}`, 'error'); } } async loadScrapingStatus() { try { const response = await fetchWithErrorHandling(`${this.baseEndpoint}/status`); this.updateStatusDisplay(response); } catch (error) { console.error('Failed to load scraping status:', error); } } async loadScrapingResults() { try { const response = await fetchWithErrorHandling(`${this.baseEndpoint}/results`); this.renderResults(response); } catch (error) { console.error('Failed to load scraping results:', error); this.showToast('خطا در بارگذاری نتایج', 'error'); } } async loadScrapingStatistics() { try { const response = await fetchWithErrorHandling(`${this.baseEndpoint}/statistics`); this.renderStatistics(response); } catch (error) { console.error('Failed to load scraping statistics:', error); } } startStatusMonitoring() { this.statusInterval = setInterval(async () => { if (this.isRunning) { await this.updateScrapingStatus(); } }, 5000); // Update every 5 seconds } stopStatusMonitoring() { if (this.statusInterval) { clearInterval(this.statusInterval); this.statusInterval = null; } } async updateScrapingStatus() { try { const response = await fetchWithErrorHandling(`${this.baseEndpoint}/status`); this.updateStatusDisplay(response); // Check if scraping is complete if (response.status === 'completed' || response.status === 'failed') { this.isRunning = false; this.currentJob = null; this.updateStartButton(false); this.stopStatusMonitoring(); if (response.status === 'completed') { this.addLog('اسکرپینگ تکمیل شد', 'success'); this.loadScrapingResults(); } else { this.addLog('اسکرپینگ با خطا مواجه شد', 'error'); } } } catch (error) { console.error('Failed to update scraping status:', error); } } updateStatusDisplay(status) { const statusElement = document.getElementById('scrapingStatus'); const progressElement = document.getElementById('scrapingProgress'); const statsElement = document.getElementById('scrapingStats'); if (statusElement) { statusElement.textContent = this.getStatusText(status.status); statusElement.className = `status-indicator ${this.getStatusClass(status.status)}`; } if (progressElement && status.progress) { progressElement.style.width = `${status.progress}%`; progressElement.textContent = `${status.progress}%`; } if (statsElement) { statsElement.innerHTML = `
صفحات پردازش شده: ${status.pages_processed || 0}
نتایج یافت شده: ${status.items_found || 0}
زمان سپری شده: ${status.elapsed_time || '0s'}
`; } } renderResults(results) { const container = document.getElementById('scrapingResults'); if (!container) return; if (!results.items || results.items.length === 0) { container.innerHTML = '

هیچ نتیجه‌ای یافت نشد

'; return; } const resultsHTML = results.items.map(item => `

${item.title || 'بدون عنوان'}

امتیاز: ${item.rating || 0}

${item.url}

${item.description || 'توضیحات موجود نیست'}

${this.formatDate(item.date)} ${item.category || 'نامشخص'}
`).join(''); container.innerHTML = resultsHTML; } renderStatistics(stats) { const container = document.getElementById('scrapingStatistics'); if (!container) return; const statsHTML = `
${stats.total_scraped || 0}
کل نتایج
${stats.success_rate || 0}%
نرخ موفقیت
${stats.average_speed || 0}
سرعت متوسط (صفحه/دقیقه)
${stats.average_rating || 0}
امتیاز متوسط
`; container.innerHTML = statsHTML; } getScrapingConfig() { const urlInput = document.getElementById('scrapingUrl'); const depthInput = document.getElementById('scrapingDepth'); const maxPagesInput = document.getElementById('maxPages'); const filtersInput = document.getElementById('scrapingFilters'); return { url: urlInput ? urlInput.value : '', depth: depthInput ? parseInt(depthInput.value) : 1, max_pages: maxPagesInput ? parseInt(maxPagesInput.value) : 100, filters: filtersInput ? filtersInput.value : '' }; } updateStartButton(isRunning) { const startBtn = document.getElementById('startScrapingBtn'); const stopBtn = document.getElementById('stopScrapingBtn'); if (startBtn) { startBtn.disabled = isRunning; startBtn.textContent = isRunning ? 'در حال اجرا...' : 'شروع اسکرپینگ'; } if (stopBtn) { stopBtn.disabled = !isRunning; stopBtn.style.display = isRunning ? 'inline-block' : 'none'; } } addLog(message, type = 'info') { const timestamp = new Date().toLocaleTimeString('fa-IR'); const logEntry = { timestamp, message, type }; this.logs.unshift(logEntry); this.renderLogs(); // Keep only last 100 logs if (this.logs.length > 100) { this.logs = this.logs.slice(0, 100); } } renderLogs() { const container = document.getElementById('scrapingLogs'); if (!container) return; const logsHTML = this.logs.map(log => `
${log.timestamp} ${log.message}
`).join(''); container.innerHTML = logsHTML; } clearLogs() { this.logs = []; this.renderLogs(); this.showToast('لاگ‌ها پاک شدند', 'info'); } getStatusClass(status) { const statusMap = { 'idle': 'status-idle', 'running': 'status-running', 'completed': 'status-completed', 'failed': 'status-failed', 'stopped': 'status-stopped' }; return statusMap[status] || 'status-unknown'; } getStatusText(status) { const statusMap = { 'idle': 'آماده', 'running': 'در حال اجرا', 'completed': 'تکمیل شده', 'failed': 'ناموفق', 'stopped': 'متوقف شده' }; return statusMap[status] || 'نامشخص'; } formatDate(dateString) { if (!dateString) return 'نامشخص'; const date = new Date(dateString); return date.toLocaleDateString('fa-IR'); } showToast(message, type = 'info') { if (typeof showToast === 'function') { showToast(message, type); } else { console.log(`${type.toUpperCase()}: ${message}`); } } } // Initialize scraping control panel const scrapingControlPanel = new ScrapingControlPanel();