diff options
| author | Agando <roduccomm@gmail.com> | 2026-10-18 14:32:57 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-10-18 14:32:57 +0200 |
| commit | 4d3fa20151e101d6486d5b57935370d93e0fcffe (patch) | |
| tree | 1b7f6b77bde45b1c5f6172a53308d81f49b14866 | |
| parent | ee8a03a63adfb8db1edf38de72645d685cffff2e (diff) | |
course: exclude auto-open from lesson_open telemetry, add session counts, filter synthetic sids
| -rw-r--r-- | course.html | 6 | ||||
| -rw-r--r-- | scripts/course-insights.py | 10 |
2 files changed, 9 insertions, 7 deletions
diff --git a/course.html b/course.html index c61a901..c14979c 100644 --- a/course.html +++ b/course.html @@ -132,9 +132,9 @@ async function renderChapters(){ } const p2 = prog(); const all = (MANIFEST.chapters||[]).flatMap(c=>(c.lessons||[]).map(l=>({c,l}))); const first = all.find(x=>!p2.completed[x.l.id]) || all[0]; - if(first) openLesson(first.c.id, first.l.id); + if(first) openLesson(first.c.id, first.l.id, false); } -async function openLesson(chId, lessonId){ +async function openLesson(chId, lessonId, doTrack = true){ $('reader').innerHTML = '<p class="small">Loading lesson…</p>'; try{ const ch = await fetch(BASE+'chapters/'+chId+'.json?v='+V).then(r=>r.json()); @@ -171,7 +171,7 @@ async function openLesson(chId, lessonId){ $('quiz-msg').style.color = ok ? 'var(--green)' : 'var(--rose)'; if(ok) completeLesson(l.id, 5); }); - track('lesson_open', {lesson:l.id}); + if (doTrack) track('lesson_open', {lesson:l.id}); }catch(e){ $('reader').innerHTML = '<p style="color:var(--rose)">Failed: '+esc(e.message)+'</p>'; } } function completeLesson(id, xp){ diff --git a/scripts/course-insights.py b/scripts/course-insights.py index 0ee891e..299b264 100644 --- a/scripts/course-insights.py +++ b/scripts/course-insights.py @@ -14,21 +14,23 @@ def norm_q(q): def main(): opens, completes, fails, cmds, questions = Counter(), Counter(), Counter(), Counter(), Counter() - projects = Counter() + projects = Counter(); sids = set() files = sorted(glob.glob(os.path.join(DATA, 'events-*.jsonl')))[-2:] for path in files: with open(path, encoding='utf-8', errors='ignore') as f: for line in f: try: e = json.loads(line) except Exception: continue + if (e.get('sid') or '').startswith('test'): continue t, d = e.get('type'), e.get('detail') or {} lesson = (d.get('lesson') or '')[:40] - if t == 'lesson_open' and lesson: opens[lesson] += 1 - elif t == 'lesson_complete' and lesson: completes[lesson] += 1 + if t == 'lesson_open' and lesson: opens[lesson] += 1; sids.add(e.get('sid') or '') + elif t == 'lesson_complete' and lesson: completes[lesson] += 1; sids.add(e.get('sid') or '') elif t == 'exercise_fail': if lesson: fails[lesson] += 1 c = (d.get('command') or '').strip().split(' ')[0][:40] if c: cmds[c] += 1 + sids.add(e.get('sid') or '') elif t == 'question': q = norm_q(d.get('q')) if len(q) >= 8: questions[q] += 1 @@ -46,7 +48,7 @@ def main(): 'top_failed_commands': [{'cmd': c, 'n': n} for c, n in cmds.most_common(20)], 'top_questions': [{'q': q, 'n': n} for q, n in questions.most_common(20) if n >= 2], 'projects': dict(projects), - 'totals': {'opens': sum(opens.values()), 'completes': sum(completes.values()), 'failures': sum(fails.values())}} + 'totals': {'sessions': len(sids), 'opens': sum(opens.values()), 'completes': sum(completes.values()), 'failures': sum(fails.values())}} os.makedirs(os.path.dirname(OUT), exist_ok=True) tmp = OUT + '.tmp' with open(tmp, 'w', encoding='utf-8') as f: json.dump(payload, f, ensure_ascii=False, indent=1) |