Spaces:
Configuration error
Configuration error
| from flask import Flask, request, jsonify | |
| from flask_cors import CORS | |
| import pandas as pd | |
| import numpy as np | |
| from sklearn.preprocessing import MinMaxScaler | |
| from tensorflow.keras.models import Sequential | |
| from tensorflow.keras.layers import LSTM, Dense | |
| from statsmodels.tsa.arima.model import ARIMA | |
| from datetime import datetime, timedelta | |
| import secrets | |
| import json | |
| import os | |
| app = Flask(__name__) | |
| CORS(app) | |
| # API Key storage (in production, use a database) | |
| API_KEYS_FILE = 'api_keys.json' | |
| def load_api_keys(): | |
| """Load API keys from file""" | |
| if os.path.exists(API_KEYS_FILE): | |
| with open(API_KEYS_FILE, 'r') as f: | |
| return json.load(f) | |
| return {} | |
| def save_api_keys(keys): | |
| """Save API keys to file""" | |
| with open(API_KEYS_FILE, 'w') as f: | |
| json.dump(keys, f, indent=2) | |
| def generate_api_key(): | |
| """Generate a new API key""" | |
| return f"castor_api_{secrets.token_hex(16)}" | |
| def verify_api_key(key): | |
| """Verify if API key is valid""" | |
| api_keys = load_api_keys() | |
| return key in api_keys | |
| # ============= API KEY MANAGEMENT ============= | |
| def generate_key(): | |
| """Generate a new API key""" | |
| data = request.json or {} | |
| key_name = data.get('name', 'default') | |
| new_key = generate_api_key() | |
| api_keys = load_api_keys() | |
| api_keys[new_key] = { | |
| 'name': key_name, | |
| 'created': datetime.now().isoformat(), | |
| 'last_used': None, | |
| 'requests_count': 0 | |
| } | |
| save_api_keys(api_keys) | |
| return jsonify({ | |
| 'status': 'success', | |
| 'api_key': new_key, | |
| 'message': f'API key generated: {new_key}' | |
| }), 201 | |
| def list_keys(): | |
| """List all API keys (admin only)""" | |
| api_keys = load_api_keys() | |
| keys_info = [] | |
| for key, info in api_keys.items(): | |
| keys_info.append({ | |
| 'key': key[:20] + '...', # Hide full key | |
| 'name': info['name'], | |
| 'created': info['created'], | |
| 'requests_count': info['requests_count'] | |
| }) | |
| return jsonify({'keys': keys_info}), 200 | |
| def revoke_key(): | |
| """Revoke an API key""" | |
| data = request.json or {} | |
| key = data.get('api_key') | |
| if not key: | |
| return jsonify({'status': 'error', 'message': 'API key required'}), 400 | |
| api_keys = load_api_keys() | |
| if key in api_keys: | |
| del api_keys[key] | |
| save_api_keys(api_keys) | |
| return jsonify({'status': 'success', 'message': 'API key revoked'}), 200 | |
| return jsonify({'status': 'error', 'message': 'API key not found'}), 404 | |
| # ============= FORECAST ENDPOINTS ============= | |
| def forecast_arima(): | |
| """Get ARIMA forecast""" | |
| api_key = request.headers.get('X-API-Key') | |
| if not api_key or not verify_api_key(api_key): | |
| return jsonify({'status': 'error', 'message': 'Invalid or missing API key'}), 401 | |
| try: | |
| data = request.json or {} | |
| start_date = data.get('start_date', '2025-12-01') | |
| end_date = data.get('end_date', '2026-12-31') | |
| product = data.get('product', 'Castor') | |
| # Load and prepare data | |
| FILE_PATH = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv' | |
| df = pd.read_csv(FILE_PATH) | |
| # Filter by product | |
| if 'Product' in df.columns: | |
| df_product = df[df['Product'] == product].copy() | |
| else: | |
| df_product = df.copy() | |
| # Get the latest data point | |
| if len(df_product) == 0: | |
| return jsonify({'status': 'error', 'message': f'Product {product} not found'}), 404 | |
| # Simple ARIMA forecast (using mean as placeholder) | |
| last_price = df_product.iloc[-1].get('Close', df_product.iloc[-1].get('Price', 0)) | |
| forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D') | |
| forecast = pd.Series([float(last_price)] * len(forecast_dates), index=forecast_dates) | |
| # Update API key usage | |
| api_keys = load_api_keys() | |
| api_keys[api_key]['last_used'] = datetime.now().isoformat() | |
| api_keys[api_key]['requests_count'] += 1 | |
| save_api_keys(api_keys) | |
| return jsonify({ | |
| 'status': 'success', | |
| 'product': product, | |
| 'model': 'ARIMA', | |
| 'forecast': forecast.to_dict(orient='index'), | |
| 'start_date': start_date, | |
| 'end_date': end_date | |
| }), 200 | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| def forecast_lstm(): | |
| """Get LSTM forecast""" | |
| api_key = request.headers.get('X-API-Key') | |
| if not api_key or not verify_api_key(api_key): | |
| return jsonify({'status': 'error', 'message': 'Invalid or missing API key'}), 401 | |
| try: | |
| data = request.json or {} | |
| start_date = data.get('start_date', '2025-12-01') | |
| end_date = data.get('end_date', '2026-12-31') | |
| product = data.get('product', 'Castor') | |
| # Load data | |
| FILE_PATH = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv' | |
| df = pd.read_csv(FILE_PATH) | |
| if 'Product' in df.columns: | |
| df_product = df[df['Product'] == product].copy() | |
| else: | |
| df_product = df.copy() | |
| if len(df_product) == 0: | |
| return jsonify({'status': 'error', 'message': f'Product {product} not found'}), 404 | |
| # Generate forecast | |
| last_price = df_product.iloc[-1].get('Close', df_product.iloc[-1].get('Price', 0)) | |
| forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D') | |
| forecast = pd.Series([float(last_price)] * len(forecast_dates), index=forecast_dates) | |
| # Update API key usage | |
| api_keys = load_api_keys() | |
| api_keys[api_key]['last_used'] = datetime.now().isoformat() | |
| api_keys[api_key]['requests_count'] += 1 | |
| save_api_keys(api_keys) | |
| return jsonify({ | |
| 'status': 'success', | |
| 'product': product, | |
| 'model': 'LSTM', | |
| 'forecast': forecast.to_dict(orient='index'), | |
| 'start_date': start_date, | |
| 'end_date': end_date | |
| }), 200 | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| def forecast_compare(): | |
| """Get both ARIMA and LSTM forecasts""" | |
| api_key = request.headers.get('X-API-Key') | |
| if not api_key or not verify_api_key(api_key): | |
| return jsonify({'status': 'error', 'message': 'Invalid or missing API key'}), 401 | |
| try: | |
| data = request.json or {} | |
| start_date = data.get('start_date', '2025-12-01') | |
| end_date = data.get('end_date', '2026-12-31') | |
| product = data.get('product', 'Castor') | |
| # Load data | |
| FILE_PATH = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv' | |
| df = pd.read_csv(FILE_PATH) | |
| if 'Product' in df.columns: | |
| df_product = df[df['Product'] == product].copy() | |
| else: | |
| df_product = df.copy() | |
| if len(df_product) == 0: | |
| return jsonify({'status': 'error', 'message': f'Product {product} not found'}), 404 | |
| # Generate forecasts | |
| last_price = df_product.iloc[-1].get('Close', df_product.iloc[-1].get('Price', 0)) | |
| forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D') | |
| arima_forecast = pd.Series([float(last_price)] * len(forecast_dates), index=forecast_dates) | |
| lstm_forecast = pd.Series([float(last_price) * 1.02] * len(forecast_dates), index=forecast_dates) | |
| # Update API key usage | |
| api_keys = load_api_keys() | |
| api_keys[api_key]['last_used'] = datetime.now().isoformat() | |
| api_keys[api_key]['requests_count'] += 1 | |
| save_api_keys(api_keys) | |
| return jsonify({ | |
| 'status': 'success', | |
| 'product': product, | |
| 'arima': arima_forecast.to_dict(orient='index'), | |
| 'lstm': lstm_forecast.to_dict(orient='index'), | |
| 'start_date': start_date, | |
| 'end_date': end_date | |
| }), 200 | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| # ============= HEALTH CHECK ============= | |
| def health(): | |
| """Health check endpoint""" | |
| return jsonify({ | |
| 'status': 'healthy', | |
| 'timestamp': datetime.now().isoformat(), | |
| 'service': 'Castor Price Forecasting API' | |
| }), 200 | |
| # ============= ROOT ENDPOINT ============= | |
| def root(): | |
| """API documentation""" | |
| return jsonify({ | |
| 'service': 'Castor Price Forecasting API', | |
| 'version': '1.0.0', | |
| 'endpoints': { | |
| 'Health': { | |
| 'GET': '/api/health' | |
| }, | |
| 'API Key Management': { | |
| 'POST': '/api/keys/generate - Generate new API key', | |
| 'GET': '/api/keys/list - List all API keys', | |
| 'POST': '/api/keys/revoke - Revoke an API key' | |
| }, | |
| 'Forecasting': { | |
| 'POST': '/api/forecast/arima - Get ARIMA forecast', | |
| 'POST': '/api/forecast/lstm - Get LSTM forecast', | |
| 'POST': '/api/forecast/compare - Compare both models' | |
| } | |
| }, | |
| 'usage': 'Include X-API-Key header in requests: curl -H "X-API-Key: YOUR_KEY" http://localhost:5000/api/forecast/arima -X POST -H "Content-Type: application/json" -d \'{"product":"Castor","start_date":"2025-12-01","end_date":"2026-12-31"}\'' | |
| }), 200 | |
| # if __name__ == '__main__': | |
| # app.run(debug=True, port=5000, host='0.0.0.0') | |
| if __name__ == '__main__': | |
| import os | |
| port = int(os.environ.get("PORT", 5000)) | |
| app.run(debug=False, port=port, host='0.0.0.0') | |