OUAREDAEK commited on
Commit
9ee7ae1
·
verified ·
1 Parent(s): 6baee54

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +165 -129
app.py CHANGED
@@ -1,160 +1,195 @@
 
 
1
  import os
2
  import json
3
  from datetime import datetime
4
- from flask import Flask, request, jsonify, render_template
5
-
6
- # ==================================================
7
- # 🔒 FILE LOCK (PORTALOCKER IF AVAILABLE, ELSE FALLBACK)
8
- # ==================================================
9
- try:
10
- import portalocker
11
- USE_PORTALOCKER = True
12
- except ImportError:
13
- USE_PORTALOCKER = False
14
- import threading
15
- _GLOBAL_LOCK = threading.Lock()
16
-
17
- def lock_file(f, exclusive=True):
18
- if USE_PORTALOCKER:
19
- portalocker.lock(
20
- f,
21
- portalocker.LOCK_EX if exclusive else portalocker.LOCK_SH
22
- )
23
- else:
24
- _GLOBAL_LOCK.acquire()
25
-
26
- def unlock_file(f):
27
- if USE_PORTALOCKER:
28
- portalocker.unlock(f)
29
- else:
30
- _GLOBAL_LOCK.release()
31
-
32
- # ==================================================
33
- # 🔒 SAFE JSON IO
34
- # ==================================================
35
- def read_json_safe(path, default):
36
- if not os.path.exists(path):
37
- return default
38
-
39
- with open(path, "r", encoding="utf-8") as f:
40
- lock_file(f, exclusive=False)
41
- try:
42
- return json.load(f)
43
- finally:
44
- unlock_file(f)
45
-
46
- def write_json_safe(path, data):
47
- with open(path, "w", encoding="utf-8") as f:
48
- lock_file(f, exclusive=True)
49
- try:
50
- json.dump(data, f, indent=2, ensure_ascii=False)
51
- finally:
52
- unlock_file(f)
53
-
54
- # ==================================================
55
- # ⚙️ CONFIGURATION
56
- # ==================================================
57
  PORT = int(os.environ.get("PORT", 7860))
58
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
59
 
60
- DATA_DIR = os.path.join(BASE_DIR, "data")
61
- TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
62
-
63
- os.makedirs(DATA_DIR, exist_ok=True)
64
- os.makedirs(TEMPLATES_DIR, exist_ok=True)
65
-
66
- SCENARIOS_FILE = os.path.join(DATA_DIR, "scenarios.json")
67
- STATS_FILE = os.path.join(DATA_DIR, "trust_stats.json")
68
- REVIEWS_FILE = os.path.join(DATA_DIR, "reviews.json")
69
- CALIBRATION_FILE = os.path.join(DATA_DIR, "calibration.json")
70
-
71
- # ==================================================
72
- # 🚀 FLASK APP
73
- # ==================================================
74
- app = Flask(__name__, template_folder=TEMPLATES_DIR)
75
-
76
- # ==================================================
77
- # 🌐 HOME (INDEX.HTML)
78
- # ==================================================
 
 
 
 
79
  @app.route("/")
80
  def index():
81
  return render_template("index.html")
82
 
83
- # ==================================================
84
- # 🌍 GLOBAL SCENARIOS
85
- # ==================================================
86
- @app.route("/api/scenarios", methods=["GET", "POST"])
87
- def scenarios():
88
- if request.method == "POST":
89
- scenario = request.json
90
- scenario["created_at"] = datetime.now().isoformat()
91
-
92
- data = read_json_safe(SCENARIOS_FILE, {"scenarios": []})
93
- data["scenarios"].append(scenario)
94
- write_json_safe(SCENARIOS_FILE, data)
95
-
96
- return jsonify({"status": "ok"})
97
-
98
- return jsonify(read_json_safe(SCENARIOS_FILE, {"scenarios": []}))
99
-
100
- # ==================================================
101
- # 🔧 TRUST CALIBRATION (GLOBAL)
102
- # ==================================================
103
- @app.route("/api/trust/calibration", methods=["GET", "POST"])
104
- def calibration():
105
- if request.method == "POST":
106
- write_json_safe(CALIBRATION_FILE, request.json)
107
- return jsonify({"status": "ok"})
108
-
109
- return jsonify(read_json_safe(CALIBRATION_FILE, {}))
110
-
111
- # ==================================================
112
- # 💾 TRUST EVALUATIONS
113
- # ==================================================
114
- @app.route("/api/trust", methods=["POST"])
 
 
 
 
 
 
 
 
 
 
 
115
  def save_trust():
116
  data = request.json
117
 
118
  record = {
119
  "scenario_id": data.get("scenario_id"),
120
- "metrics": data.get("metrics", {}),
121
- "score": data.get("score"),
122
  "comment": data.get("comment"),
123
  "timestamp": datetime.now().isoformat()
124
  }
125
 
126
- stats = read_json_safe(STATS_FILE, {"evaluations": []})
127
- stats["evaluations"].append(record)
128
- write_json_safe(STATS_FILE, stats)
 
 
129
 
130
  return jsonify({"status": "ok"})
131
 
132
- # ==================================================
133
- # 📝 REVIEWS
134
- # ==================================================
135
- @app.route("/api/reviews", methods=["GET", "POST"])
136
- def reviews():
137
- if request.method == "POST":
138
- review = request.json
139
- review["timestamp"] = datetime.now().isoformat()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
- data = read_json_safe(REVIEWS_FILE, {"reviews": []})
142
- data["reviews"].append(review)
143
- write_json_safe(REVIEWS_FILE, data)
144
 
145
- return jsonify({"status": "ok"})
 
 
 
 
 
146
 
147
- return jsonify(read_json_safe(REVIEWS_FILE, {"reviews": []}))
 
 
 
 
148
 
149
- # ==================================================
150
- # 🏁 MAIN
151
- # ==================================================
 
 
 
 
 
 
 
 
 
152
  if __name__ == "__main__":
153
 
 
 
 
 
 
 
 
 
154
  defaults = {
155
  SCENARIOS_FILE: {"scenarios": []},
 
156
  STATS_FILE: {"evaluations": []},
157
- REVIEWS_FILE: {"reviews": []},
158
  CALIBRATION_FILE: {
159
  "metrics": {
160
  "AR": {"min": 0, "max": 5, "weight": 1},
@@ -168,12 +203,13 @@ if __name__ == "__main__":
168
 
169
  for path, content in defaults.items():
170
  if not os.path.exists(path):
171
- write_json_safe(path, content)
 
 
172
 
173
- print(f"🚀 Application globale active : http://127.0.0.1:{PORT}")
174
  app.run(
175
  host="0.0.0.0",
176
  port=PORT,
177
  debug=True,
178
- use_reloader=False # IMPORTANT pour Spyder
179
  )
 
1
+ import subprocess
2
+ import sys
3
  import os
4
  import json
5
  from datetime import datetime
6
+
7
+ # --------------------------------------------------
8
+ # 🔧 Auto-install Flask if missing
9
+ # --------------------------------------------------
10
+ def install_if_missing(package):
11
+ try:
12
+ __import__(package)
13
+ except ImportError:
14
+ print(f"⚡ Installing missing package: {package}")
15
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package])
16
+
17
+ install_if_missing("flask")
18
+
19
+ # --------------------------------------------------
20
+ # 📦 Imports
21
+ # --------------------------------------------------
22
+ from flask import Flask, render_template, request, jsonify, send_from_directory
23
+
24
+ # --------------------------------------------------
25
+ # ⚙️ CONFIGURATION GLOBALE
26
+ # --------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  PORT = int(os.environ.get("PORT", 7860))
28
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
29
 
30
+ CROSS_READING_DIR = os.path.join(BASE_DIR, "cross_reading")
31
+ TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
32
+ STATIC_DIR = os.path.join(BASE_DIR, "static")
33
+ DATA_DIR = os.path.join(BASE_DIR, "data")
34
+
35
+ SCENARIOS_FILE = os.path.join(CROSS_READING_DIR, "scenarios.json")
36
+ SCENARIOS_LIST_FILE = os.path.join(CROSS_READING_DIR, "scenarios_list.json")
37
+ STATS_FILE = os.path.join(CROSS_READING_DIR, "trust_stats.json")
38
+ REVIEW_FILE = os.path.join(CROSS_READING_DIR, "review.json")
39
+ CALIBRATION_FILE = os.path.join(CROSS_READING_DIR, "calibration-trust.json")
40
+
41
+ CROSS_HTML = "cross_readers.html"
42
+ CROSS_JS = "cross_readers.js"
43
+
44
+ app = Flask(
45
+ __name__,
46
+ template_folder=TEMPLATES_DIR,
47
+ static_folder=STATIC_DIR
48
+ )
49
+
50
+ # --------------------------------------------------
51
+ # 🌐 ROUTES UI
52
+ # --------------------------------------------------
53
  @app.route("/")
54
  def index():
55
  return render_template("index.html")
56
 
57
+ @app.route("/cross_readers.html")
58
+ @app.route("/cross_reading/cross_readers.html")
59
+ def cross_readers():
60
+ if os.path.exists(os.path.join(CROSS_READING_DIR, CROSS_HTML)):
61
+ return send_from_directory(CROSS_READING_DIR, CROSS_HTML)
62
+ return "cross_readers.html introuvable", 404
63
+
64
+ @app.route("/cross_reading/cross_readers.js")
65
+ def cross_readers_js():
66
+ return send_from_directory(CROSS_READING_DIR, CROSS_JS)
67
+
68
+ # --------------------------------------------------
69
+ # 📘 API – SCÉNARIOS
70
+ # --------------------------------------------------
71
+ @app.route("/api/scenarios")
72
+ def get_scenarios():
73
+ with open(SCENARIOS_FILE, "r", encoding="utf-8") as f:
74
+ return jsonify(json.load(f))
75
+
76
+ @app.route("/api/scenarios_list")
77
+ def get_scenarios_list():
78
+ with open(SCENARIOS_LIST_FILE, "r", encoding="utf-8") as f:
79
+ return jsonify(json.load(f))
80
+
81
+ # --------------------------------------------------
82
+ # 🔧 API – TRUST CALIBRATION
83
+ # --------------------------------------------------
84
+ @app.route("/api/trust/calibration")
85
+ def get_trust_calibration():
86
+ with open(CALIBRATION_FILE, "r", encoding="utf-8") as f:
87
+ return jsonify(json.load(f))
88
+
89
+ @app.route("/api/trust/calibration", methods=["POST"])
90
+ def save_trust_calibration():
91
+ data = request.json
92
+ with open(CALIBRATION_FILE, "w", encoding="utf-8") as f:
93
+ json.dump(data, f, indent=2, ensure_ascii=False)
94
+ return jsonify({"status": "ok"})
95
+
96
+ # --------------------------------------------------
97
+ # 💾 API – TRUST (quantitatif)
98
+ # --------------------------------------------------
99
+ @app.route("/save_trust", methods=["POST"])
100
  def save_trust():
101
  data = request.json
102
 
103
  record = {
104
  "scenario_id": data.get("scenario_id"),
105
+ "trust_metrics": data.get("trust_metrics", {}),
106
+ "trust_score": data.get("trust_score"),
107
  "comment": data.get("comment"),
108
  "timestamp": datetime.now().isoformat()
109
  }
110
 
111
+ with open(STATS_FILE, "r+", encoding="utf-8") as f:
112
+ content = json.load(f)
113
+ content["evaluations"].append(record)
114
+ f.seek(0)
115
+ json.dump(content, f, indent=2, ensure_ascii=False)
116
 
117
  return jsonify({"status": "ok"})
118
 
119
+ @app.route("/api/evaluations")
120
+ def get_evaluations():
121
+ with open(STATS_FILE, "r", encoding="utf-8") as f:
122
+ data = json.load(f)
123
+
124
+ # Normalisation métriques
125
+ mapping = {
126
+ "RA": "AR",
127
+ "EA": "AE",
128
+ "RE": "ESR",
129
+ "MCS": "SDM",
130
+ "MS": "SM"
131
+ }
132
+
133
+ for e in data.get("evaluations", []):
134
+ if "trust_metrics" in e:
135
+ e["trust_metrics"] = {
136
+ mapping.get(k, k): v for k, v in e["trust_metrics"].items()
137
+ }
138
+
139
+ return jsonify(data)
140
+
141
+ # --------------------------------------------------
142
+ # 📝 API – REVIEWS (qualitatif)
143
+ # --------------------------------------------------
144
+ @app.route("/api/reviews")
145
+ def get_reviews():
146
+ with open(REVIEW_FILE, "r", encoding="utf-8") as f:
147
+ return jsonify(json.load(f))
148
 
149
+ @app.route("/save_review", methods=["POST"])
150
+ def save_review():
151
+ data = request.json
152
 
153
+ review = {
154
+ "scenario_id": data.get("scenario_id"),
155
+ "reader": data.get("reader"),
156
+ "review": data.get("review"),
157
+ "timestamp": datetime.now().isoformat()
158
+ }
159
 
160
+ with open(REVIEW_FILE, "r+", encoding="utf-8") as f:
161
+ content = json.load(f)
162
+ content["reviews"].append(review)
163
+ f.seek(0)
164
+ json.dump(content, f, indent=2, ensure_ascii=False)
165
 
166
+ return jsonify({"status": "ok"})
167
+
168
+ # --------------------------------------------------
169
+ # 📂 ROUTES FICHIERS STATIQUES
170
+ # --------------------------------------------------
171
+ @app.route("/cross_reading/<path:filename>")
172
+ def serve_cross_reading(filename):
173
+ return send_from_directory(CROSS_READING_DIR, filename)
174
+
175
+ # --------------------------------------------------
176
+ # 🚀 MAIN
177
+ # --------------------------------------------------
178
  if __name__ == "__main__":
179
 
180
+ print(f"🚀 Application : http://0.0.0.0:{PORT}")
181
+ print(f"📈 Cross Reading : http://0.0.0.0:{PORT}/cross_readers.html")
182
+
183
+ # Création dossiers
184
+ for folder in [CROSS_READING_DIR, TEMPLATES_DIR, STATIC_DIR, DATA_DIR]:
185
+ os.makedirs(folder, exist_ok=True)
186
+
187
+ # Initialisation fichiers
188
  defaults = {
189
  SCENARIOS_FILE: {"scenarios": []},
190
+ SCENARIOS_LIST_FILE: {"scenarios": []},
191
  STATS_FILE: {"evaluations": []},
192
+ REVIEW_FILE: {"reviews": []},
193
  CALIBRATION_FILE: {
194
  "metrics": {
195
  "AR": {"min": 0, "max": 5, "weight": 1},
 
203
 
204
  for path, content in defaults.items():
205
  if not os.path.exists(path):
206
+ with open(path, "w", encoding="utf-8") as f:
207
+ json.dump(content, f, indent=2, ensure_ascii=False)
208
+ print(f"📄 Créé : {path}")
209
 
 
210
  app.run(
211
  host="0.0.0.0",
212
  port=PORT,
213
  debug=True,
214
+ use_reloader=False
215
  )