vendor_report/api_server.py
2025-11-06 20:50:19 +04:00

227 lines
8.4 KiB
Python

#!/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)
# 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)
)
logger.info(f"Downloaded {len(downloaded)} file(s) from SharePoint")
except Exception as e:
logger.error(f"Failed to download from SharePoint: {e}")
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', []))
})
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)