"""Configuration management blueprint.""" from flask import Blueprint, render_template, redirect, url_for, request, flash, jsonify from ..db import db from ..models import VolumeConfig, ScheduleConfig, ActivityLog from ..defaults import MAX_VOLUME bp = Blueprint("config", __name__, url_prefix="/config") # Helper functions for configuration updates def _update_volume(new_volume): """ Helper function to update volume configuration. Args: new_volume (float): The new volume value Returns: tuple: (success, message, volume_config) """ try: new_volume = float(new_volume) if new_volume <= 0 or new_volume > MAX_VOLUME: return False, f"Volume must be between 1 and {MAX_VOLUME}", None volume_config = VolumeConfig.query.first() if not volume_config: volume_config = VolumeConfig(volume=new_volume) db.session.add(volume_config) else: old_value = volume_config.volume volume_config.volume = new_volume ActivityLog.log_config_change( config_key="scraper_volume", old_value=old_value, new_value=new_volume, description="Updated scraper volume" ) db.session.commit() return True, "Volume updated successfully!", volume_config except (ValueError, TypeError) as e: db.session.rollback() return False, f"Error updating volume: {str(e)}", None def _update_schedule(schedule_data): """ Helper function to update schedule configuration. Args: schedule_data (dict): Dictionary with hour:weight pairs Returns: tuple: (success, message) """ try: # Validate all entries first for hour_str, weight in schedule_data.items(): try: hour = int(hour_str) weight = float(weight) if hour < 0 or hour > 23: return False, f"Hour value must be between 0 and 23, got {hour}" if weight < 0.1 or weight > 5: return False, f"Weight for hour {hour} must be between 0.1 and 5, got {weight}" except ValueError: return False, f"Invalid data format for hour {hour_str}" # Update schedule after validation for hour_str, weight in schedule_data.items(): hour = int(hour_str) weight = float(weight) config = ScheduleConfig.query.get(hour) if not config: config = ScheduleConfig(hour=hour, weight=weight) db.session.add(config) else: old_value = config.weight config.weight = weight ActivityLog.log_config_change( config_key=f"schedule_hour_{hour}", old_value=old_value, new_value=weight, description=f"Updated schedule weight for hour {hour}" ) db.session.commit() return True, "Schedule updated successfully!" except Exception as e: db.session.rollback() return False, f"Error updating schedule: {str(e)}" @bp.route("/") @bp.route("/general") def general(): """Show general configuration page.""" volume_config = VolumeConfig.query.first() if not volume_config: volume_config = VolumeConfig(volume=100) # Default value db.session.add(volume_config) db.session.commit() return render_template( "config/index.html.jinja", active_tab="general", volume_config=volume_config, max_volume=MAX_VOLUME, app_title="Configuration" ) @bp.route("/schedule") def schedule(): """Show schedule configuration page.""" # Ensure we have schedule config for all hours existing_hours = {record.hour: record for record in ScheduleConfig.query.all()} schedule_config = {} for hour in range(24): if hour in existing_hours: schedule_config[hour] = existing_hours[hour].weight else: # Create default schedule entry (weight 1.0) new_config = ScheduleConfig(hour=hour, weight=1.0) db.session.add(new_config) schedule_config[hour] = 1.0 if len(existing_hours) < 24: db.session.commit() volume_config = VolumeConfig.query.first() if not volume_config: volume_config = VolumeConfig(volume=100) # Default value db.session.add(volume_config) db.session.commit() return render_template( "config/index.html.jinja", active_tab="schedule", schedule=schedule_config, volume=volume_config.volume, max_volume=MAX_VOLUME, app_title="Configuration" ) @bp.route("/update/volume", methods=["POST"]) def update_volume(): """Update volume configuration.""" new_volume = request.form.get("total_volume", 0) success, message, _ = _update_volume(new_volume) if success: flash(message, "success") else: flash(message, "error") return redirect(url_for("config.general")) @bp.route("/update/schedule", methods=["POST"]) def update_schedule(): """Update schedule configuration.""" schedule_data = {} for hour in range(24): key = f"hour_{hour}" if key not in request.form: flash(f"Missing data for hour {hour}", "error") return redirect(url_for("config.schedule")) schedule_data[str(hour)] = request.form.get(key, 0) success, message = _update_schedule(schedule_data) if success: flash(message, "success") else: flash(message, "error") return redirect(url_for("config.schedule")) @bp.route("/api/schedule/stats") def schedule_stats(): """Get statistics about the current schedule configuration.""" volume_config = VolumeConfig.query.first() if not volume_config: return jsonify({"error": "No volume configuration found"}) total_volume = volume_config.volume schedule_configs = ScheduleConfig.query.all() if not schedule_configs: return jsonify({"error": "No schedule configuration found"}) # Calculate total weight total_weight = sum(config.weight for config in schedule_configs) # Calculate papers per hour papers_per_hour = {} hourly_weights = {} for config in schedule_configs: weight_ratio = config.weight / total_weight if total_weight > 0 else 0 papers = weight_ratio * total_volume papers_per_hour[config.hour] = papers hourly_weights[config.hour] = config.weight return jsonify({ "total_volume": total_volume, "total_weight": total_weight, "papers_per_hour": papers_per_hour, "hourly_weights": hourly_weights }) @bp.route("/api/update_config", methods=["POST"]) def api_update_config(): """API endpoint to update configuration.""" data = request.json response = {"success": True, "updates": []} try: # Update volume if provided if "volume" in data: success, message, volume_config = _update_volume(data["volume"]) response["updates"].append({ "type": "volume", "success": success, "message": message }) if not success: response["success"] = False # Update schedule if provided if "schedule" in data: success, message = _update_schedule(data["schedule"]) response["updates"].append({ "type": "schedule", "success": success, "message": message }) if not success: response["success"] = False return jsonify(response) except Exception as e: db.session.rollback() return jsonify({ "success": False, "message": f"Unexpected error: {str(e)}" })