227 lines
8.4 KiB
Python
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)
|
|
|