Spaces:
Running
Running
Commit ·
3edb836
1
Parent(s): 0151d8f
Harden demo feedback pathing and runtime diagnostics
Browse files- .gitignore +17 -0
- Dockerfile +2 -1
- app.py +42 -3
- static/app.js +3 -2
- storage_paths.py +8 -8
- 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 =
|
| 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 |
-
|
|
|
|
| 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)
|
| 16 |
-
3)
|
|
|
|
| 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 |
-
|
| 27 |
-
|
| 28 |
-
preferred_rel = Path("data/demo_guests") if demo_mode else Path("data")
|
| 29 |
else:
|
| 30 |
-
|
|
|
|
| 31 |
|
| 32 |
preferred = project_root / preferred_rel
|
| 33 |
try:
|
| 34 |
preferred.mkdir(parents=True, exist_ok=True)
|
| 35 |
return preferred
|
| 36 |
except OSError:
|
| 37 |
-
|
| 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=
|
| 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>
|