336 lines
9.5 KiB
JavaScript
336 lines
9.5 KiB
JavaScript
/**
|
|
* Scraper control functionality
|
|
*/
|
|
|
|
class ScraperController {
|
|
constructor(options = {}) {
|
|
this.maxVolume = options.maxVolume || 1000;
|
|
this.volumeConfig = options.volumeConfig || 100;
|
|
|
|
// DOM elements
|
|
this.statusIndicator = document.getElementById("statusIndicator");
|
|
this.statusText = document.getElementById("statusText");
|
|
this.startButton = document.getElementById("startButton");
|
|
this.pauseButton = document.getElementById("pauseButton");
|
|
this.stopButton = document.getElementById("stopButton");
|
|
this.resetButton = document.getElementById("resetButton");
|
|
|
|
this.initEventListeners();
|
|
this.initStatusPolling();
|
|
}
|
|
|
|
/**
|
|
* Initialize event listeners for scraper controls
|
|
*/
|
|
initEventListeners() {
|
|
if (this.startButton) {
|
|
this.startButton.addEventListener("click", () => this.startScraper());
|
|
}
|
|
if (this.pauseButton) {
|
|
this.pauseButton.addEventListener("click", () =>
|
|
this.togglePauseScraper()
|
|
);
|
|
}
|
|
if (this.stopButton) {
|
|
this.stopButton.addEventListener("click", () => this.stopScraper());
|
|
}
|
|
if (this.resetButton) {
|
|
this.resetButton.addEventListener("click", () => this.resetScraper());
|
|
}
|
|
|
|
// Configuration form (handles both volume and scraper module)
|
|
const configForm = document.getElementById("volumeForm");
|
|
if (configForm) {
|
|
configForm.addEventListener("submit", (e) => {
|
|
e.preventDefault();
|
|
this.updateConfiguration();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize status polling
|
|
*/
|
|
initStatusPolling() {
|
|
this.updateStatus();
|
|
setInterval(() => this.updateStatus(), 5000); // Poll every 5 seconds
|
|
}
|
|
|
|
/**
|
|
* Update scraper status display
|
|
*/
|
|
async updateStatus() {
|
|
try {
|
|
const data = await apiRequest("/scraper/status");
|
|
console.log("Status data received:", data);
|
|
|
|
// Remove all status classes first
|
|
if (this.statusIndicator) {
|
|
this.statusIndicator.classList.remove(
|
|
"status-active",
|
|
"status-paused",
|
|
"status-inactive"
|
|
);
|
|
}
|
|
|
|
// Handle the new JSON structure with scraper_state
|
|
const scraperState = data.scraper_state || data; // Fallback for old structure
|
|
|
|
if (scraperState.active) {
|
|
if (scraperState.paused) {
|
|
this.statusIndicator?.classList.add("status-paused");
|
|
if (this.statusText) this.statusText.textContent = "Paused";
|
|
if (this.pauseButton) this.pauseButton.textContent = "Resume";
|
|
} else {
|
|
this.statusIndicator?.classList.add("status-active");
|
|
if (this.statusText) this.statusText.textContent = "Active";
|
|
if (this.pauseButton) this.pauseButton.textContent = "Pause";
|
|
}
|
|
if (this.startButton) this.startButton.disabled = true;
|
|
if (this.pauseButton) this.pauseButton.disabled = false;
|
|
if (this.stopButton) this.stopButton.disabled = false;
|
|
if (this.resetButton) this.resetButton.disabled = false;
|
|
} else {
|
|
this.statusIndicator?.classList.add("status-inactive");
|
|
if (this.statusText) this.statusText.textContent = "Inactive";
|
|
if (this.startButton) this.startButton.disabled = false;
|
|
if (this.pauseButton) this.pauseButton.disabled = true;
|
|
if (this.stopButton) this.stopButton.disabled = true;
|
|
if (this.resetButton) this.resetButton.disabled = false;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching status:", error);
|
|
// On error, show inactive state
|
|
if (this.statusIndicator) {
|
|
this.statusIndicator.classList.remove(
|
|
"status-active",
|
|
"status-paused",
|
|
"status-inactive"
|
|
);
|
|
this.statusIndicator.classList.add("status-inactive");
|
|
}
|
|
if (this.statusText) this.statusText.textContent = "Error";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start the scraper
|
|
*/
|
|
async startScraper() {
|
|
console.log("Start button clicked - sending request to /scraper/start");
|
|
|
|
try {
|
|
const data = await apiRequest("/scraper/start", {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
console.log("Data received:", data);
|
|
|
|
if (data.success) {
|
|
showFlashMessage("Scraper started successfully", "success");
|
|
this.updateStatus();
|
|
// Trigger activity refresh if callback is provided
|
|
if (this.onActivityRefresh) {
|
|
setTimeout(() => this.onActivityRefresh(), 1000);
|
|
}
|
|
} else {
|
|
showFlashMessage(data.message, "error");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error starting scraper:", error);
|
|
showFlashMessage("Error starting scraper: " + error.message, "error");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle pause/resume scraper
|
|
*/
|
|
async togglePauseScraper() {
|
|
try {
|
|
const data = await apiRequest("/scraper/pause", {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
|
|
if (data.success) {
|
|
showFlashMessage(data.message, "info");
|
|
this.updateStatus();
|
|
if (this.onActivityRefresh) {
|
|
setTimeout(() => this.onActivityRefresh(), 1000);
|
|
}
|
|
} else {
|
|
showFlashMessage(data.message, "error");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error toggling pause:", error);
|
|
showFlashMessage("Error controlling scraper: " + error.message, "error");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop the scraper
|
|
*/
|
|
async stopScraper() {
|
|
try {
|
|
const data = await apiRequest("/scraper/stop", {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
|
|
if (data.success) {
|
|
showFlashMessage("Scraper stopped successfully", "warning");
|
|
this.updateStatus();
|
|
if (this.onActivityRefresh) {
|
|
setTimeout(() => this.onActivityRefresh(), 1000);
|
|
}
|
|
} else {
|
|
showFlashMessage(data.message, "error");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error stopping scraper:", error);
|
|
showFlashMessage("Error stopping scraper: " + error.message, "error");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the scraper
|
|
*/
|
|
async resetScraper() {
|
|
if (
|
|
!confirm(
|
|
"Are you sure you want to reset the scraper? This will stop all current tasks, optionally clear non-pending papers, and restart the scraper."
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Disable button to prevent multiple clicks
|
|
if (this.resetButton) this.resetButton.disabled = true;
|
|
|
|
// Show a loading message
|
|
showFlashMessage("Resetting scraper, please wait...", "info");
|
|
|
|
try {
|
|
const data = await apiRequest("/scraper/reset", {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
clear_papers: true, // You could make this configurable with a checkbox
|
|
}),
|
|
});
|
|
|
|
if (data.success) {
|
|
showFlashMessage(
|
|
"Scraper has been completely reset and restarted",
|
|
"success"
|
|
);
|
|
// Update everything
|
|
this.updateStatus();
|
|
if (this.onActivityRefresh) {
|
|
this.onActivityRefresh();
|
|
setTimeout(() => this.onActivityRefresh(), 1000);
|
|
}
|
|
if (this.onChartRefresh) {
|
|
this.onChartRefresh();
|
|
}
|
|
} else {
|
|
showFlashMessage(data.message || "Error resetting scraper", "error");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error resetting scraper:", error);
|
|
showFlashMessage("Error resetting scraper: " + error.message, "error");
|
|
} finally {
|
|
// Re-enable button
|
|
if (this.resetButton) this.resetButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update configuration (volume and/or scraper module)
|
|
*/
|
|
async updateConfiguration() {
|
|
const volumeInput = document.getElementById("volumeInput");
|
|
const scraperSelect = document.getElementById("mainScraperSelect");
|
|
const submitButton = document.querySelector(
|
|
'#volumeForm button[type="submit"]'
|
|
);
|
|
|
|
if (!submitButton) return;
|
|
|
|
const updates = {};
|
|
let hasChanges = false;
|
|
|
|
// Check volume changes
|
|
if (volumeInput) {
|
|
const volume = volumeInput.value;
|
|
|
|
// Basic validation
|
|
if (!volume || volume < 1 || volume > this.maxVolume) {
|
|
showFlashMessage(
|
|
`Please enter a valid volume between 1 and ${this.maxVolume}`,
|
|
"warning"
|
|
);
|
|
volumeInput.focus();
|
|
return;
|
|
}
|
|
|
|
updates.volume = volume;
|
|
hasChanges = true;
|
|
}
|
|
|
|
// Check scraper module changes
|
|
if (scraperSelect && scraperSelect.value) {
|
|
updates.scraper_module = scraperSelect.value;
|
|
hasChanges = true;
|
|
}
|
|
|
|
if (!hasChanges) {
|
|
showFlashMessage("No changes to save", "info");
|
|
return;
|
|
}
|
|
|
|
// Toggle loading state
|
|
toggleButtonLoading(submitButton, true, "Updating...");
|
|
|
|
try {
|
|
const data = await apiRequest("/scraper/update_config", {
|
|
method: "POST",
|
|
body: JSON.stringify(updates),
|
|
});
|
|
|
|
if (data.success) {
|
|
showFlashMessage(
|
|
data.message || "Configuration updated successfully",
|
|
"success"
|
|
);
|
|
} else {
|
|
showFlashMessage(
|
|
data.message || "Failed to update configuration",
|
|
"error"
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error updating configuration:", error);
|
|
showFlashMessage(
|
|
"Network error while updating configuration. Please try again.",
|
|
"error"
|
|
);
|
|
} finally {
|
|
toggleButtonLoading(submitButton, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set callback for activity refresh
|
|
*/
|
|
setActivityRefreshCallback(callback) {
|
|
this.onActivityRefresh = callback;
|
|
}
|
|
|
|
/**
|
|
* Set callback for chart refresh
|
|
*/
|
|
setChartRefreshCallback(callback) {
|
|
this.onChartRefresh = callback;
|
|
}
|
|
}
|