#!/usr/bin/env python3 """ Web API Server for On-Demand Report Generation Provides REST API endpoints to trigger report generation on demand. """ import logging from pathlib import Path from typing import Optional import json try: from flask import Flask, jsonify, request from flask_cors import CORS FLASK_AVAILABLE = True except ImportError: FLASK_AVAILABLE = False logging.warning("Flask not installed. API server features disabled.") from config import load_config from report_generator import generate_report from sharepoint_downloader import download_from_sharepoint logger = logging.getLogger(__name__) app = None config = None def create_app(config_path: Optional[str] = None): """Create and configure Flask app.""" global app, config if not FLASK_AVAILABLE: raise ImportError( "Flask is required for API server. " "Install it with: pip install flask flask-cors" ) app = Flask(__name__) CORS(app) # Enable CORS for all routes config = load_config(config_path) api_config = config.get('api', {}) sharepoint_config = config.get('sharepoint', {}) report_config = config.get('report', {}) # Store config in app context app.config['API_KEY'] = api_config.get('api_key') app.config['SHAREPOINT_CONFIG'] = sharepoint_config app.config['REPORT_CONFIG'] = report_config @app.route('/health', methods=['GET']) def health(): """Health check endpoint.""" return jsonify({ 'status': 'healthy', 'service': 'vendor-report-generator' }) @app.route('/api/generate', methods=['POST']) def generate_report_endpoint(): """ Generate report on demand. Request body (optional): { "download_from_sharepoint": true, "reports_dir": "reports", "output_file": "output/report.json" } """ # Check API key if configured api_key = app.config.get('API_KEY') if api_key: provided_key = request.headers.get('X-API-Key') or request.json.get('api_key') if request.json else None if provided_key != api_key: return jsonify({'error': 'Invalid API key'}), 401 try: request_data = request.json or {} download_from_sp = request_data.get('download_from_sharepoint', False) downloaded_files = [] # Initialize here for scope # Download from SharePoint if requested if download_from_sp: sp_config = app.config['SHAREPOINT_CONFIG'] if not sp_config.get('enabled'): return jsonify({ 'error': 'SharePoint is not enabled in configuration' }), 400 logger.info("Downloading files from SharePoint...") try: downloaded = download_from_sharepoint( site_url=sp_config['site_url'], folder_path=sp_config.get('folder_path'), file_path=sp_config.get('file_path'), local_dir=sp_config.get('local_dir', 'reports'), username=sp_config.get('username'), password=sp_config.get('password'), client_id=sp_config.get('client_id'), client_secret=sp_config.get('client_secret'), use_app_authentication=sp_config.get('use_app_authentication', False), file_pattern=sp_config.get('file_pattern'), overwrite=sp_config.get('overwrite', True) ) downloaded_files = downloaded if downloaded else [] logger.info(f"Downloaded {len(downloaded_files)} file(s) from SharePoint: {downloaded_files}") except Exception as e: logger.error(f"Failed to download from SharePoint: {e}", exc_info=True) return jsonify({ 'error': f'SharePoint download failed: {str(e)}' }), 500 # Generate report report_config = app.config['REPORT_CONFIG'] reports_dir = request_data.get('reports_dir', report_config.get('reports_dir', 'reports')) output_file = request_data.get('output_file', str(Path(report_config.get('output_dir', 'output')) / 'report.json')) logger.info(f"Generating report from {reports_dir}...") report_data = generate_report( reports_dir=reports_dir, output_file=output_file, verbose=False # Don't print to console in API mode ) if report_data: return jsonify({ 'status': 'success', 'message': 'Report generated successfully', 'output_file': output_file, 'summary': report_data.get('summary', {}), 'vendors_count': len(report_data.get('vendors', [])), 'downloaded_files': len(downloaded_files) if download_from_sp else 0, 'downloaded_file_names': [Path(f).name for f in downloaded_files] if download_from_sp else [] }) else: return jsonify({ 'error': 'Report generation failed' }), 500 except Exception as e: logger.error(f"Error generating report: {e}", exc_info=True) return jsonify({ 'error': f'Report generation failed: {str(e)}' }), 500 @app.route('/api/status', methods=['GET']) def status(): """Get service status and configuration.""" return jsonify({ 'status': 'running', 'sharepoint_enabled': app.config['SHAREPOINT_CONFIG'].get('enabled', False), 'reports_dir': app.config['REPORT_CONFIG'].get('reports_dir', 'reports'), 'output_dir': app.config['REPORT_CONFIG'].get('output_dir', 'output') }) @app.route('/api/report/json', methods=['GET']) def get_report_json(): """Get latest report JSON file.""" try: report_config = app.config['REPORT_CONFIG'] output_dir = Path(report_config.get('output_dir', 'output')) report_file = output_dir / 'report.json' if not report_file.exists(): return jsonify({'error': 'Report not found. Generate a report first.'}), 404 with open(report_file, 'r', encoding='utf-8') as f: report_data = json.load(f) return jsonify(report_data) except Exception as e: logger.error(f"Error reading report JSON: {e}", exc_info=True) return jsonify({'error': f'Failed to read report: {str(e)}'}), 500 @app.route('/api/report/html', methods=['GET']) def get_report_html(): """Get latest report HTML file.""" try: from flask import send_from_directory report_config = app.config['REPORT_CONFIG'] output_dir = Path(report_config.get('output_dir', 'output')) html_file = output_dir / 'report.html' if not html_file.exists(): return jsonify({'error': 'Report HTML not found. Generate a report first.'}), 404 return send_from_directory(str(output_dir), 'report.html', mimetype='text/html') except Exception as e: logger.error(f"Error reading report HTML: {e}", exc_info=True) return jsonify({'error': f'Failed to read report HTML: {str(e)}'}), 500 return app def run_server(config_path: Optional[str] = None, host: Optional[str] = None, port: Optional[int] = None): """Run the API server.""" app = create_app(config_path) api_config = config.get('api', {}) server_host = host or api_config.get('host', '0.0.0.0') server_port = port or api_config.get('port', 8080) logger.info(f"Starting API server on {server_host}:{server_port}") app.run(host=server_host, port=server_port, debug=False) if __name__ == "__main__": import sys logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) config_path = sys.argv[1] if len(sys.argv) > 1 else None # Check if API is enabled config = load_config(config_path) if not config.get('api', {}).get('enabled', False): logger.warning("API is disabled in configuration. Set api.enabled=true to enable.") logger.info("Starting API server anyway (for testing)...") run_server(config_path=config_path)