from flask import Flask, render_template, jsonify from datetime import datetime, timedelta from engine import DataEngine import concurrent.futures from flask_caching import Cache import csv, os, logging from concurrent.futures import ThreadPoolExecutor app = Flask(__name__) cache = Cache(app, config={'CACHE_TYPE': 'SimpleCache'}) import os import csv @cache.memoize(timeout=3600) def fetch_and_calculate(config): engine = DataEngine(config['symbol'], config['url'], config['provider']) df = engine.fetch_data() if df is not None: metrics = engine.calculate_table_metrics(df) if metrics: metrics['symbol'] = config['symbol'] return metrics return None @app.route('/') def index(): return render_template('index.html') @app.route('/api/summary') def get_summary(): engine_base = DataEngine() instruments = engine_base.load_instruments_from_csv('instruments.csv') results = [] for item in instruments: engine = DataEngine( symbol=item['symbol'], url=item['url'], provider=item['provider'] ) metrics = engine.get_local_metrics() if metrics and isinstance(metrics, dict): metrics['symbol'] = item['symbol'] results.append(metrics) else: # 🔥 FIX: Include the 'error' key so JavaScript hits the Gatekeeper results.append({ "symbol": item['symbol'], "last_close": None, # Ensure this is null, not the string "No Data" "error": True }) return jsonify(results) @app.route('/api/sync', methods=['POST']) def run_sync(): # ✅ THIS RUNS THE FULL fetch_data() WITH NETWORK ACCESS engine = DataEngine() report = engine.global_sync() return jsonify(report) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)