| import subprocess
|
| import sys
|
| import os
|
| import json
|
| from datetime import datetime
|
|
|
|
|
|
|
|
|
| def install_if_missing(package):
|
| try:
|
| __import__(package)
|
| except ImportError:
|
| print(f"⚡ Installing missing package: {package}")
|
| subprocess.check_call([sys.executable, "-m", "pip", "install", package])
|
|
|
| install_if_missing("flask")
|
|
|
|
|
|
|
|
|
| from flask import Flask, render_template, request, jsonify, send_from_directory
|
|
|
|
|
|
|
|
|
| PORT = int(os.environ.get("PORT", 7860))
|
| BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
| CROSS_READING_DIR = os.path.join(BASE_DIR, "cross_reading")
|
| TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
|
| STATIC_DIR = os.path.join(BASE_DIR, "static")
|
| DATA_DIR = os.path.join(BASE_DIR, "data")
|
|
|
| SCENARIOS_FILE = os.path.join(CROSS_READING_DIR, "scenarios.json")
|
| SCENARIOS_LIST_FILE = os.path.join(CROSS_READING_DIR, "scenarios_list.json")
|
| STATS_FILE = os.path.join(CROSS_READING_DIR, "trust_stats.json")
|
| REVIEW_FILE = os.path.join(CROSS_READING_DIR, "review.json")
|
| CALIBRATION_FILE = os.path.join(CROSS_READING_DIR, "calibration-trust.json")
|
|
|
| CROSS_HTML = "cross_readers.html"
|
| CROSS_JS = "cross_readers.js"
|
|
|
| app = Flask(
|
| __name__,
|
| template_folder=TEMPLATES_DIR,
|
| static_folder=STATIC_DIR
|
| )
|
|
|
|
|
|
|
|
|
| @app.route("/")
|
| def index():
|
| return render_template("index.html")
|
|
|
| @app.route("/cross_readers.html")
|
| @app.route("/cross_reading/cross_readers.html")
|
| def cross_readers():
|
| if os.path.exists(os.path.join(CROSS_READING_DIR, CROSS_HTML)):
|
| return send_from_directory(CROSS_READING_DIR, CROSS_HTML)
|
| return "cross_readers.html introuvable", 404
|
|
|
| @app.route("/cross_reading/cross_readers.js")
|
| def cross_readers_js():
|
| return send_from_directory(CROSS_READING_DIR, CROSS_JS)
|
|
|
|
|
|
|
|
|
| @app.route("/api/scenarios")
|
| def get_scenarios():
|
| with open(SCENARIOS_FILE, "r", encoding="utf-8") as f:
|
| return jsonify(json.load(f))
|
|
|
| @app.route("/api/scenarios_list")
|
| def get_scenarios_list():
|
| with open(SCENARIOS_LIST_FILE, "r", encoding="utf-8") as f:
|
| return jsonify(json.load(f))
|
|
|
|
|
|
|
|
|
| @app.route("/api/trust/calibration")
|
| def get_trust_calibration():
|
| with open(CALIBRATION_FILE, "r", encoding="utf-8") as f:
|
| return jsonify(json.load(f))
|
|
|
| @app.route("/api/trust/calibration", methods=["POST"])
|
| def save_trust_calibration():
|
| data = request.json
|
| with open(CALIBRATION_FILE, "w", encoding="utf-8") as f:
|
| json.dump(data, f, indent=2, ensure_ascii=False)
|
| return jsonify({"status": "ok"})
|
|
|
|
|
|
|
|
|
| @app.route("/save_trust", methods=["POST"])
|
| def save_trust():
|
| data = request.json
|
|
|
| record = {
|
| "scenario_id": data.get("scenario_id"),
|
| "trust_metrics": data.get("trust_metrics", {}),
|
| "trust_score": data.get("trust_score"),
|
| "comment": data.get("comment"),
|
| "timestamp": datetime.now().isoformat()
|
| }
|
|
|
| with open(STATS_FILE, "r+", encoding="utf-8") as f:
|
| content = json.load(f)
|
| content["evaluations"].append(record)
|
| f.seek(0)
|
| json.dump(content, f, indent=2, ensure_ascii=False)
|
|
|
| return jsonify({"status": "ok"})
|
|
|
| @app.route("/api/evaluations")
|
| def get_evaluations():
|
| with open(STATS_FILE, "r", encoding="utf-8") as f:
|
| data = json.load(f)
|
|
|
|
|
| mapping = {
|
| "RA": "AR",
|
| "EA": "AE",
|
| "RE": "ESR",
|
| "MCS": "SDM",
|
| "MS": "SM"
|
| }
|
|
|
| for e in data.get("evaluations", []):
|
| if "trust_metrics" in e:
|
| e["trust_metrics"] = {
|
| mapping.get(k, k): v for k, v in e["trust_metrics"].items()
|
| }
|
|
|
| return jsonify(data)
|
|
|
|
|
|
|
|
|
| @app.route("/api/reviews")
|
| def get_reviews():
|
| with open(REVIEW_FILE, "r", encoding="utf-8") as f:
|
| return jsonify(json.load(f))
|
|
|
| @app.route("/save_review", methods=["POST"])
|
| def save_review():
|
| data = request.json
|
|
|
| review = {
|
| "scenario_id": data.get("scenario_id"),
|
| "reader": data.get("reader"),
|
| "review": data.get("review"),
|
| "timestamp": datetime.now().isoformat()
|
| }
|
|
|
| with open(REVIEW_FILE, "r+", encoding="utf-8") as f:
|
| content = json.load(f)
|
| content["reviews"].append(review)
|
| f.seek(0)
|
| json.dump(content, f, indent=2, ensure_ascii=False)
|
|
|
| return jsonify({"status": "ok"})
|
|
|
|
|
|
|
|
|
| @app.route("/cross_reading/<path:filename>")
|
| def serve_cross_reading(filename):
|
| return send_from_directory(CROSS_READING_DIR, filename)
|
|
|
|
|
|
|
|
|
| if __name__ == "__main__":
|
|
|
| print(f"🚀 Application : http://0.0.0.0:{PORT}")
|
| print(f"📈 Cross Reading : http://0.0.0.0:{PORT}/cross_readers.html")
|
|
|
|
|
| for folder in [CROSS_READING_DIR, TEMPLATES_DIR, STATIC_DIR, DATA_DIR]:
|
| os.makedirs(folder, exist_ok=True)
|
|
|
|
|
| defaults = {
|
| SCENARIOS_FILE: {"scenarios": []},
|
| SCENARIOS_LIST_FILE: {"scenarios": []},
|
| STATS_FILE: {"evaluations": []},
|
| REVIEW_FILE: {"reviews": []},
|
| CALIBRATION_FILE: {
|
| "metrics": {
|
| "AR": {"min": 0, "max": 5, "weight": 1},
|
| "AE": {"min": 0, "max": 5, "weight": 1},
|
| "ESR": {"min": 0, "max": 5, "weight": 1},
|
| "SDM": {"min": 0, "max": 5, "weight": 1},
|
| "SM": {"min": 0, "max": 5, "weight": 1}
|
| }
|
| }
|
| }
|
|
|
| for path, content in defaults.items():
|
| if not os.path.exists(path):
|
| with open(path, "w", encoding="utf-8") as f:
|
| json.dump(content, f, indent=2, ensure_ascii=False)
|
| print(f"📄 Créé : {path}")
|
|
|
| app.run(
|
| host="0.0.0.0",
|
| port=PORT,
|
| debug=True,
|
| use_reloader=False
|
| )
|
|
|