feat(i18n + export + guide): locale-aware scenarios/debrief; CSV export; how-to-use page
Backend:
- SCENARIOS localized (th/en): scenario preamble + persona tone adaptation follow the
trainee's locale; start accepts {locale} and stores it on the session; send/auto-finish
use it so debrief text (why/coaching/turning points) is in the active language.
- /api/analytics/export returns per-trainee finished-session CSV (admin).
Frontend:
- Scenario picker labels localize by i18n.locale.
- New /guide page (non-IT how-to), linked from Training.
- Analytics: date-filter bar uses line icon + 'Download CSV' button (fetch w/ auth).
Rebuilt dist.
This commit is contained in:
@@ -111,3 +111,63 @@ def analytics():
|
||||
"trainee_count": len(users),
|
||||
"hardest_personas": hardest,
|
||||
})
|
||||
|
||||
|
||||
@analytics_bp.get("/export")
|
||||
@require_auth
|
||||
@require_roles("admin")
|
||||
def export_csv():
|
||||
"""Export per-trainee finished session results as CSV (for HR/offline review)."""
|
||||
import csv
|
||||
import io
|
||||
|
||||
from flask import Response, current_app
|
||||
|
||||
s = _stores()
|
||||
users = {}
|
||||
try:
|
||||
user_store = current_app.extensions.get("user_store")
|
||||
if user_store and hasattr(user_store, "list_users"):
|
||||
for rec in user_store.list_users():
|
||||
users[rec.get("id") or rec.get("username")] = rec.get("username") or rec.get("id")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sessions = []
|
||||
try:
|
||||
grp_store = s["groups"]
|
||||
sess_store = s["sessions"]
|
||||
if hasattr(grp_store, "groups"):
|
||||
group_ids = [g.get("id") for g in grp_store.groups.all()]
|
||||
elif hasattr(grp_store, "all"):
|
||||
group_ids = [g.get("id") for g in grp_store.all()]
|
||||
else:
|
||||
group_ids = []
|
||||
for gid in group_ids:
|
||||
try:
|
||||
ss = sess_store.sessions.where(
|
||||
lambda r, _gid=gid: r.get("group_id") == _gid and r.get("outcome")
|
||||
)
|
||||
sessions.extend(ss)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
buf = io.StringIO()
|
||||
w = csv.writer(buf)
|
||||
w.writerow(["username", "persona", "scenario", "outcome", "score", "created_at"])
|
||||
for sess in sessions:
|
||||
w.writerow([
|
||||
users.get(sess.get("user_id"), sess.get("user_id", "")),
|
||||
sess.get("persona_name", ""),
|
||||
sess.get("scenario", ""),
|
||||
sess.get("outcome", ""),
|
||||
(sess.get("debrief") or {}).get("score", ""),
|
||||
sess.get("created_at", ""),
|
||||
])
|
||||
return Response(
|
||||
buf.getvalue(),
|
||||
mimetype="text/csv",
|
||||
headers={"Content-Disposition": "attachment; filename=sales-trainer-results.csv"},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user