from flask import Flask, render_template, request, redirect, url_for, send_file, flash, jsonify import os import traceback from pathlib import Path import json import pandas as pd app = Flask(__name__) app.secret_key = os.urandom(24) try: import main as project_main except Exception as e: project_main = None import_error = traceback.format_exc() else: import_error = None RESULTS_CSV = Path("results_flask.csv") def safe_update_dict_from_json(orig_dict, json_text): if not json_text or not json_text.strip(): return orig_dict try: new = json.loads(json_text) if not isinstance(new, dict): return orig_dict d = orig_dict.copy() d.update(new) return d except Exception: return orig_dict @app.route("/", methods=["GET"]) def index(): if project_main is None: return render_template("index.html", import_error=import_error, project=None) techs = list(project_main.TECHNOLOGY_DATA.keys()) defaults = { "inflation": project_main.INFLATION_RATE, "tax": project_main.TAX_RATE, "years": project_main.PROJECT_YEARS, "cap_min": project_main.OPTIMIZATION_SPACE['capacity_kta'][0], "cap_max": project_main.OPTIMIZATION_SPACE['capacity_kta'][1], "export_mix": project_main.OPTIMIZATION_SPACE['export_market_mix'][0], "technology": "Engro_Pakistan", # FIX: default برای match IRR main.py } return render_template("index.html", import_error=None, project=project_main, techs=techs, defaults=defaults) @app.route("/run", methods=["POST"]) def run(): if project_main is None: flash("خطا: فایل main.py بارگزاری نشد. لطفاً لاگ را بررسی کنید.", "danger") return redirect(url_for("index")) try: inflation_rate = float(request.form.get("inflation", project_main.INFLATION_RATE)) tax_rate = float(request.form.get("tax", project_main.TAX_RATE)) project_years = int(request.form.get("years", project_main.PROJECT_YEARS)) capacity_min = float(request.form.get("cap_min", project_main.OPTIMIZATION_SPACE['capacity_kta'][0])) capacity_max = float(request.form.get("cap_max", project_main.OPTIMIZATION_SPACE['capacity_kta'][1])) technology = request.form.get("technology", "Engro_Pakistan") # FIX: default Engro export_mix = float(request.form.get("export_mix", project_main.OPTIMIZATION_SPACE['export_market_mix'][0])) sell_byproducts = request.form.get("sell_byproducts") == "on" tech_json = request.form.get("tech_json", "") prices_json = request.form.get("prices_json", "") except Exception as e: flash("خطا در خواندن ورودی‌ها: " + str(e), "danger") return redirect(url_for("index")) try: project_main.INFLATION_RATE = inflation_rate project_main.TAX_RATE = tax_rate project_main.PROJECT_YEARS = project_years project_main.OPTIMIZATION_SPACE['capacity_kta'] = (capacity_min, capacity_max) project_main.OPTIMIZATION_SPACE['technology'] = ["Engro_Pakistan", "Shin_Etsu_2004"] if technology not in ["Engro_Pakistan", "Shin_Etsu_2004"] else [technology] project_main.OPTIMIZATION_SPACE['sourcing_strategy'] = ['Integrated_Production'] # FIX: صریح project_main.OPTIMIZATION_SPACE['export_market_mix'] = (0.6, 0.8) project_main.OPTIMIZATION_SPACE['sell_byproducts'] = [bool(sell_byproducts)] project_main.TECHNOLOGY_DATA = safe_update_dict_from_json(project_main.TECHNOLOGY_DATA, tech_json) project_main.PRODUCT_PRICES_USD_PER_TON = safe_update_dict_from_json(project_main.PRODUCT_PRICES_USD_PER_TON, prices_json) except Exception as e: flash("خطا در اعمال پارامترها: " + str(e), "danger") return redirect(url_for("index")) try: flash("محاسبات شروع شد — صبر کنید تا عملیات به پایان برسد...", "info") results = project_main.run_optimizations_without_ml() df_results = pd.DataFrame(results).sort_values(by="irr", ascending=False).reset_index(drop=True) df_results = df_results.round(2) RESULTS_CSV = os.path.join("/tmp", "results_flask.csv") # RESULTS_CSV = Path.cwd() / "results_flask.csv" df_results.to_csv(RESULTS_CSV, index=False, encoding='utf-8-sig') try: project_main.display_and_save_results(df_results) project_main.create_kpi_comparison_dashboard(df_results) except Exception: pass if not df_results.empty: best = df_results.iloc[0] top_kpis = { "irr": round(float(best['irr']), 2), "annual_profit_M": round(float(best['annual_profit'])/1_000_000, 2), "capex_M": round(float(best['total_capex'])/1_000_000, 2), "payback": round(float(best['payback_period']), 2) } else: top_kpis = None charts_data = df_results.to_dict(orient="records") flash("محاسبات با موفقیت تکمیل شد.", "success") kpi_img = Path("static/images/kpi_dashboard.png") if Path("static/images/kpi_dashboard.png").exists() else None tornado_img = Path("static/images/sensitivity_analysis_tornado.png") if Path("static/images/sensitivity_analysis_tornado.png").exists() else None return render_template( "index.html", project=project_main, techs=list(project_main.TECHNOLOGY_DATA.keys()), defaults={"inflation": project_main.INFLATION_RATE, "tax": project_main.TAX_RATE}, table_html = df_results.to_html( classes='table table-striped table-dark', index=False, justify='center', float_format=lambda x: f"{x:.2f}" ), top_kpis=top_kpis, charts_json=json.dumps(charts_data, default=str), kpi_img="images/kpi_dashboard.png" if kpi_img else None, tornado_img="images/sensitivity_analysis_tornado.png" if tornado_img else None ) except Exception as e: tb = traceback.format_exc() flash("خطا در اجرای الگوریتم‌ها. لاگ: " + str(e), "danger") return render_template("index.html", import_error=tb, project=project_main) @app.route("/download") def download_results(): if RESULTS_CSV.exists(): return send_file(str(RESULTS_CSV), as_attachment=True) flash("فایل نتایج آماده نیست.", "warning") return redirect(url_for("index")) if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))