Brajmovech commited on
Commit
3edb836
·
1 Parent(s): 0151d8f

Harden demo feedback pathing and runtime diagnostics

Browse files
Files changed (6) hide show
  1. .gitignore +17 -0
  2. Dockerfile +2 -1
  3. app.py +42 -3
  4. static/app.js +3 -2
  5. storage_paths.py +8 -8
  6. templates/index.html +1 -1
.gitignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+
4
+ .env
5
+
6
+ # Runtime output folders
7
+ runtime_data/
8
+ demo_guests_data/
9
+ tmp_feedback_test_demo/
10
+
11
+ # Repo data runtime artifacts
12
+ data/demo_guests/
13
+ data/charts/
14
+ data/sessions/
15
+ data/yfinance_tz_cache/
16
+ data/*_report.json
17
+ data/feedback_logs.json
Dockerfile CHANGED
@@ -4,7 +4,8 @@ FROM python:3.10-slim
4
  RUN useradd -m -u 1000 user
5
  USER user
6
  ENV HOME=/home/user \
7
- PATH=/home/user/.local/bin:$PATH
 
8
 
9
  WORKDIR $HOME/app
10
 
 
4
  RUN useradd -m -u 1000 user
5
  USER user
6
  ENV HOME=/home/user \
7
+ PATH=/home/user/.local/bin:$PATH \
8
+ DEMO_MODE=true
9
 
10
  WORKDIR $HOME/app
11
 
app.py CHANGED
@@ -30,6 +30,16 @@ app = Flask(__name__)
30
  CORS(app) # Enable CORS for all routes
31
  DATA_DIR.mkdir(parents=True, exist_ok=True)
32
  YF_CACHE_DIR.mkdir(parents=True, exist_ok=True)
 
 
 
 
 
 
 
 
 
 
33
  try:
34
  cache_mod = getattr(yf, "cache", None)
35
  cache_setter = getattr(cache_mod, "set_cache_location", None)
@@ -274,9 +284,7 @@ def submit_feedback():
274
 
275
  feedback_item = dict(payload)
276
  feedback_item["timestamp"] = datetime.now(timezone.utc).isoformat()
277
- log_path = DATA_DIR / "feedback_logs.json"
278
- if DEMO_MODE:
279
- Path("data/demo_guests").mkdir(parents=True, exist_ok=True)
280
  log_path.parent.mkdir(parents=True, exist_ok=True)
281
  try:
282
  with open(log_path, "r", encoding="utf-8") as f:
@@ -303,6 +311,37 @@ def submit_feedback():
303
  "status": "success",
304
  "message": "Feedback logged",
305
  "saved_to": saved_to,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  })
307
 
308
  @app.route('/api/session-summary/latest')
 
30
  CORS(app) # Enable CORS for all routes
31
  DATA_DIR.mkdir(parents=True, exist_ok=True)
32
  YF_CACHE_DIR.mkdir(parents=True, exist_ok=True)
33
+
34
+
35
+ def _feedback_log_path() -> Path:
36
+ """Return the canonical feedback log path for the current runtime mode."""
37
+ if DEMO_MODE:
38
+ demo_dir = PROJECT_ROOT / "data" / "demo_guests"
39
+ demo_dir.mkdir(parents=True, exist_ok=True)
40
+ return demo_dir / "feedback_logs.json"
41
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
42
+ return DATA_DIR / "feedback_logs.json"
43
  try:
44
  cache_mod = getattr(yf, "cache", None)
45
  cache_setter = getattr(cache_mod, "set_cache_location", None)
 
284
 
285
  feedback_item = dict(payload)
286
  feedback_item["timestamp"] = datetime.now(timezone.utc).isoformat()
287
+ log_path = _feedback_log_path()
 
 
288
  log_path.parent.mkdir(parents=True, exist_ok=True)
289
  try:
290
  with open(log_path, "r", encoding="utf-8") as f:
 
311
  "status": "success",
312
  "message": "Feedback logged",
313
  "saved_to": saved_to,
314
+ "demo_mode": DEMO_MODE,
315
+ })
316
+
317
+
318
+ @app.route('/api/feedback/status', methods=['GET'])
319
+ def feedback_status():
320
+ """Expose current feedback storage wiring for runtime debugging."""
321
+ log_path = _feedback_log_path()
322
+ exists = log_path.exists()
323
+ count = 0
324
+ last_timestamp = None
325
+ if exists:
326
+ try:
327
+ with open(log_path, "r", encoding="utf-8") as f:
328
+ loaded = json.load(f)
329
+ if isinstance(loaded, list):
330
+ count = len(loaded)
331
+ if loaded and isinstance(loaded[-1], dict):
332
+ last_timestamp = loaded[-1].get("timestamp")
333
+ except (OSError, json.JSONDecodeError):
334
+ pass
335
+
336
+ return jsonify({
337
+ "demo_mode": DEMO_MODE,
338
+ "cwd": os.getcwd(),
339
+ "project_root": str(PROJECT_ROOT),
340
+ "data_dir": str(DATA_DIR),
341
+ "feedback_log_path": str(log_path),
342
+ "feedback_log_exists": exists,
343
+ "feedback_log_entries": count,
344
+ "last_timestamp": last_timestamp,
345
  })
346
 
347
  @app.route('/api/session-summary/latest')
static/app.js CHANGED
@@ -412,11 +412,12 @@ document.addEventListener('DOMContentLoaded', () => {
412
  },
413
  body: JSON.stringify(payload),
414
  });
 
415
  if (!response.ok) {
416
- const body = await response.json().catch(() => ({}));
417
  throw new Error(body.error || 'Failed to send feedback.');
418
  }
419
- alert('Feedback sent!');
 
420
  if (feedbackMessageEl) {
421
  feedbackMessageEl.value = '';
422
  }
 
412
  },
413
  body: JSON.stringify(payload),
414
  });
415
+ const body = await response.json().catch(() => ({}));
416
  if (!response.ok) {
 
417
  throw new Error(body.error || 'Failed to send feedback.');
418
  }
419
+ const destination = String(body.saved_to || '').trim();
420
+ alert(destination ? `Feedback sent! Saved to ${destination}` : 'Feedback sent!');
421
  if (feedbackMessageEl) {
422
  feedbackMessageEl.value = '';
423
  }
storage_paths.py CHANGED
@@ -12,8 +12,9 @@ def resolve_data_dir(project_root: Path, demo_mode: bool) -> Path:
12
 
13
  Priority:
14
  1) IRIS_DATA_DIR (absolute or project-relative)
15
- 2) IRIS_USE_REPO_DATA=true -> data/ (or data/demo_guests in demo mode)
16
- 3) default isolated runtime dir -> runtime_data/ (or demo_guests_data/)
 
17
  """
18
  explicit_dir = str(os.environ.get("IRIS_DATA_DIR", "")).strip()
19
  if explicit_dir:
@@ -23,18 +24,17 @@ def resolve_data_dir(project_root: Path, demo_mode: bool) -> Path:
23
  candidate.mkdir(parents=True, exist_ok=True)
24
  return candidate
25
 
26
- use_repo_data = _as_bool(os.environ.get("IRIS_USE_REPO_DATA", "false"))
27
- if use_repo_data:
28
- preferred_rel = Path("data/demo_guests") if demo_mode else Path("data")
29
  else:
30
- preferred_rel = Path("demo_guests_data") if demo_mode else Path("runtime_data")
 
31
 
32
  preferred = project_root / preferred_rel
33
  try:
34
  preferred.mkdir(parents=True, exist_ok=True)
35
  return preferred
36
  except OSError:
37
- fallback_rel = Path("demo_guests_data") if demo_mode else Path("runtime_data")
38
- fallback = project_root / fallback_rel
39
  fallback.mkdir(parents=True, exist_ok=True)
40
  return fallback
 
12
 
13
  Priority:
14
  1) IRIS_DATA_DIR (absolute or project-relative)
15
+ 2) Demo mode -> data/demo_guests (single canonical feedback/report path)
16
+ 3) IRIS_USE_REPO_DATA=true -> data/
17
+ 4) default isolated runtime dir -> runtime_data/
18
  """
19
  explicit_dir = str(os.environ.get("IRIS_DATA_DIR", "")).strip()
20
  if explicit_dir:
 
24
  candidate.mkdir(parents=True, exist_ok=True)
25
  return candidate
26
 
27
+ if demo_mode:
28
+ preferred_rel = Path("data/demo_guests")
 
29
  else:
30
+ use_repo_data = _as_bool(os.environ.get("IRIS_USE_REPO_DATA", "false"))
31
+ preferred_rel = Path("data") if use_repo_data else Path("runtime_data")
32
 
33
  preferred = project_root / preferred_rel
34
  try:
35
  preferred.mkdir(parents=True, exist_ok=True)
36
  return preferred
37
  except OSError:
38
+ fallback = project_root / Path("runtime_data")
 
39
  fallback.mkdir(parents=True, exist_ok=True)
40
  return fallback
templates/index.html CHANGED
@@ -166,7 +166,7 @@
166
  <!-- TradingView Lightweight Charts -->
167
  <script
168
  src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js?v=4"></script>
169
- <script src="/static/app.js?v=12"></script>
170
  </body>
171
 
172
  </html>
 
166
  <!-- TradingView Lightweight Charts -->
167
  <script
168
  src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js?v=4"></script>
169
+ <script src="/static/app.js?v=13"></script>
170
  </body>
171
 
172
  </html>