/** * Activity monitoring and display functionality */ class ActivityMonitor { constructor() { this.activityLog = document.getElementById("activityLog"); this.notificationsToggle = document.getElementById("notificationsToggle"); this.notificationsEnabled = true; this.lastPaperTimestamp = new Date().toISOString(); this.initEventListeners(); this.setupWebSocket(); } /** * Initialize event listeners */ initEventListeners() { if (this.notificationsToggle) { this.notificationsToggle.addEventListener("click", () => { this.notificationsEnabled = this.notificationsToggle.checked; }); } // Time range buttons document.querySelectorAll(".time-range-btn").forEach((btn) => { btn.addEventListener("click", () => { document .querySelectorAll(".time-range-btn") .forEach((b) => b.classList.remove("active")); btn.classList.add("active"); const currentTimeRange = parseInt(btn.dataset.hours); // Trigger chart refresh if callback is provided if (this.onChartRefresh) { this.onChartRefresh(currentTimeRange); } }); }); } /** * Load and render recent activity */ async loadRecentActivity() { if (!this.activityLog) return; try { const data = await apiRequest( "/api/activity_logs?category=scraper_activity&category=scraper_command&limit=50" ); this.renderActivityLog(data); console.log("Activity log refreshed with latest data"); } catch (error) { console.error("Failed to load activity logs:", error); // If the API endpoint doesn't exist, just show a message this.activityLog.innerHTML = 'Activity log API not available'; } } /** * Render activity log data * @param {Array} logs - Array of log entries */ renderActivityLog(logs) { if (!this.activityLog) return; this.activityLog.innerHTML = ""; if (!logs || logs.length === 0) { this.activityLog.innerHTML = 'No recent activity'; return; } logs.forEach((log) => { const row = document.createElement("tr"); // Format timestamp const timeStr = formatTimestamp(log.timestamp); // Create status badge const statusBadge = createStatusBadge(log.status); row.innerHTML = ` ${timeStr} ${log.action} ${statusBadge} ${log.description || ""} `; this.activityLog.appendChild(row); }); } /** * Setup WebSocket for real-time notifications */ setupWebSocket() { // If WebSocket is available, implement it here // For now we'll poll the server periodically for new papers setInterval(() => this.checkForNewPapers(), 10000); // Check every 10 seconds } /** * Check for new papers and show notifications */ async checkForNewPapers() { if (!this.notificationsEnabled) return; try { const data = await apiRequest( `/api/activity_logs?category=scraper_activity&category=scraper_command&action=scrape_paper&after=${this.lastPaperTimestamp}&limit=5` ); if (data && data.length > 0) { // Update the timestamp this.lastPaperTimestamp = new Date().toISOString(); // Show notifications for new papers data.forEach((log) => { const extraData = log.extra_data ? JSON.parse(log.extra_data) : {}; if (log.status === "success") { showFlashMessage( `New paper scraped: ${extraData.title || "Unknown title"}`, "success" ); } else if (log.status === "error") { showFlashMessage( `Failed to scrape paper: ${log.description}`, "error" ); } }); // Refresh the activity chart and log if (this.onChartRefresh) { this.onChartRefresh(); } this.loadRecentActivity(); } } catch (error) { // If the API endpoint doesn't exist, do nothing console.debug("Activity polling failed (this may be expected):", error); } } /** * Set callback for chart refresh */ setChartRefreshCallback(callback) { this.onChartRefresh = callback; } }