Aryaman25 commited on
Commit
fac7a64
·
1 Parent(s): d9e4ec5

Remove Gaze Samples stat and Attention Timeline section from report

Browse files

Trims the stat row to the 4 cards that matter for a glance (Session
Length, Elements Fixated, Most Attended, Mouse Events) and drops the
Attention Timeline chart along with its now-unused data plumbing
(compute_timeline(), TIMELINE/DURATION JS constants, legend blurb).

Files changed (1) hide show
  1. analysis.py +4 -107
analysis.py CHANGED
@@ -1,9 +1,9 @@
1
  """
2
  analysis.py
3
  Turns a raw InsightUX session (gaze_log.jsonl + dom_log.jsonl + screenshots/)
4
- into a self-contained HTML report: ranked attention (in plain language),
5
- a dwell timeline, and per-scroll-position heatmaps drawn directly on top
6
- of what the page actually looked like.
7
 
8
  Usage:
9
  from analysis import generate_report
@@ -201,25 +201,6 @@ def compute_dwell_ranking(attributed):
201
  return ranking
202
 
203
 
204
- def compute_timeline(attributed):
205
- segments = []
206
- if not attributed:
207
- return segments
208
- cur_raw = attributed[0][1]
209
- seg_start = attributed[0][0]
210
- last_t = attributed[0][0]
211
- for t, label in attributed[1:]:
212
- if label != cur_raw:
213
- segments.append({"start": round(seg_start, 2), "end": round(last_t, 2),
214
- "label": friendly_label(cur_raw) if cur_raw else None})
215
- cur_raw = label
216
- seg_start = t
217
- last_t = t
218
- segments.append({"start": round(seg_start, 2), "end": round(last_t, 2),
219
- "label": friendly_label(cur_raw) if cur_raw else None})
220
- return segments
221
-
222
-
223
  def session_summary(gaze, dom):
224
  if not gaze:
225
  return {"duration": 0.0, "url": None, "samples": 0}
@@ -402,7 +383,7 @@ __THEME_CSS__
402
  .topbar .actions { margin-left: auto; display: flex; gap: 8px; }
403
  .topbar .actions .iux-btn { padding: 8px 13px; font-size: 12px; }
404
 
405
- .stat-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; margin-bottom: 22px; }
406
  .stat {
407
  padding: 16px 18px; position: relative; overflow: hidden;
408
  }
@@ -550,7 +531,6 @@ __THEME_CSS__
550
  <div class="stat iux-card hoverable"><div class="icon-badge">__LAYERS_ICON__</div><div class="v">__NUM_ELEMENTS__</div><div class="l">Elements Fixated</div></div>
551
  <div class="stat iux-card hoverable"><div class="icon-badge">__TARGET_ICON__</div><div class="v" style="font-size:14px;">__TOP_LABEL__</div><div class="l">Most Attended</div></div>
552
  <div class="stat iux-card hoverable"><div class="icon-badge">__CURSOR_ICON__</div><div class="v">__CLICK_COUNT__</div><div class="l">Mouse Events</div></div>
553
- <div class="stat iux-card hoverable"><div class="icon-badge">__TYPE_ICON__</div><div class="v">__SAMPLES__</div><div class="l">Gaze Samples</div></div>
554
  </div>
555
 
556
  <div class="grid">
@@ -575,8 +555,6 @@ __THEME_CSS__
575
  the page automatically whenever you scrolled far enough that the view
576
  changed meaningfully. Use the full-session export buttons to download
577
  one stitched image covering the entire page.</p>
578
- <p><b>Timeline</b> — the same attention data laid out across time,
579
- so you can see the order things were looked at, not just the totals.</p>
580
  </div>
581
  </div>
582
  <div class="panel iux-card full iux-fade-in">
@@ -613,10 +591,6 @@ __THEME_CSS__
613
  </div>
614
  <div class="elem-chart-hint" id="elemChartHint" style="display:none;">Scroll to see every element &rarr;</div>
615
  </div>
616
- <div class="panel iux-card full iux-fade-in">
617
- <h2>__CLOCK_ICON__ Attention Timeline</h2>
618
- <canvas id="timeline" width="1240" height="320"></canvas>
619
- </div>
620
  <div class="panel iux-card iux-fade-in">
621
  <h2>__CURSOR_ICON__ Mouse — Most Interacted Elements</h2>
622
  <div id="mouseInterests"></div>
@@ -665,9 +639,7 @@ document.getElementById('themeToggle').addEventListener('click', function(){
665
  });
666
 
667
  const RANKING = __RANKING_JSON__;
668
- const TIMELINE = __TIMELINE_JSON__;
669
  const SEGMENTS = __SEGMENTS_JSON__;
670
- const DURATION = __DURATION_JSON__;
671
  const MOUSE_INTERESTS = __MOUSE_INTERESTS_JSON__;
672
  const MOUSE_CLICKS = __MOUSE_CLICKS_JSON__;
673
  const MOUSE_SEGMENTS = __MOUSE_SEGMENTS_JSON__;
@@ -1587,78 +1559,6 @@ function showToast(message, isError, onRetry){
1587
  window.addEventListener('resize', function(){ layout(); paint(); });
1588
  })();
1589
 
1590
- // ---------- Timeline ----------
1591
- (function(){
1592
- const cv = document.getElementById('timeline');
1593
- const ctx = cv.getContext('2d');
1594
-
1595
- function paintTimeline(){
1596
- const bg = getComputedStyle(document.documentElement).getPropertyValue('--iux-bg-alt').trim() || '#171923';
1597
- const dim = getComputedStyle(document.documentElement).getPropertyValue('--iux-text-dim').trim() || '#9a95b3';
1598
- ctx.clearRect(0, 0, cv.width, cv.height);
1599
- ctx.fillStyle = bg;
1600
- ctx.fillRect(0, 0, cv.width, cv.height);
1601
- if (!TIMELINE.length || DURATION <= 0) { return; }
1602
-
1603
- const labels = [...new Set(TIMELINE.filter(s => s.label).map(s => s.label))];
1604
- const rowH = Math.min(28, (cv.height - 40) / Math.max(labels.length, 1));
1605
- const leftPad = 220, topPad = 10, plotW = cv.width - leftPad - 20;
1606
-
1607
- ctx.font = '11px -apple-system, Arial';
1608
- ctx.fillStyle = dim;
1609
- labels.forEach((lab, i) => {
1610
- const y = topPad + i * rowH;
1611
- ctx.fillText(lab.length > 28 ? lab.slice(0,28)+'…' : lab, 4, y + rowH*0.65);
1612
- });
1613
-
1614
- const colors = ['#6366F1', '#8B5CF6', '#C084FC', '#67E8F9', '#FBBF24', '#34D399'];
1615
- TIMELINE.forEach(seg => {
1616
- if (!seg.label) return;
1617
- const rowIdx = labels.indexOf(seg.label);
1618
- const x = leftPad + (seg.start / DURATION) * plotW;
1619
- const w = Math.max(2, ((seg.end - seg.start) / DURATION) * plotW);
1620
- const y = topPad + rowIdx * rowH + 3;
1621
- ctx.fillStyle = colors[rowIdx % colors.length];
1622
- const rr = Math.min(4, (rowH - 6) / 2, w / 2);
1623
- ctx.beginPath();
1624
- ctx.moveTo(x + rr, y);
1625
- ctx.arcTo(x + w, y, x + w, y + (rowH - 6), rr);
1626
- ctx.arcTo(x + w, y + (rowH - 6), x, y + (rowH - 6), rr);
1627
- ctx.arcTo(x, y + (rowH - 6), x, y, rr);
1628
- ctx.arcTo(x, y, x + w, y, rr);
1629
- ctx.closePath();
1630
- ctx.fill();
1631
- });
1632
-
1633
- ctx.strokeStyle = '#302a3d55';
1634
- ctx.beginPath();
1635
- ctx.moveTo(leftPad, cv.height - 18);
1636
- ctx.lineTo(cv.width - 10, cv.height - 18);
1637
- ctx.stroke();
1638
- ctx.fillStyle = dim;
1639
- [0, 0.25, 0.5, 0.75, 1].forEach(f => {
1640
- const x = leftPad + f * plotW;
1641
- ctx.fillText((f * DURATION).toFixed(1) + 's', x, cv.height - 4);
1642
- });
1643
- }
1644
-
1645
- paintTimeline();
1646
- window.insightuxRepaintCanvases.push(paintTimeline);
1647
-
1648
- const leftPadForHit = 220, topPadForHit = 10;
1649
- cv.addEventListener('mousemove', function(e){
1650
- cv.title = '';
1651
- if (!TIMELINE.length || DURATION <= 0) return;
1652
- const rect = cv.getBoundingClientRect();
1653
- const scaleX = cv.width / rect.width;
1654
- const mx = (e.clientX - rect.left) * scaleX;
1655
- const plotW = cv.width - leftPadForHit - 20;
1656
- if (mx < leftPadForHit) return;
1657
- const t = ((mx - leftPadForHit) / plotW) * DURATION;
1658
- const hit = TIMELINE.find(s => s.label && t >= s.start && t <= s.end);
1659
- if (hit) cv.title = hit.label + ' — ' + hit.start.toFixed(1) + 's to ' + hit.end.toFixed(1) + 's';
1660
- });
1661
- })();
1662
  </script>
1663
  </body>
1664
  </html>
@@ -1670,7 +1570,6 @@ def generate_report(session_dir):
1670
  attributed = attribute_gaze(gaze, dom)
1671
 
1672
  ranking = compute_dwell_ranking(attributed)
1673
- timeline = compute_timeline(attributed)
1674
  segments = build_screenshot_segments(gaze, dom, session_dir)
1675
  summary = session_summary(gaze, dom)
1676
  mouse = summarize_mouse(session_dir)
@@ -1698,9 +1597,7 @@ def generate_report(session_dir):
1698
  html = html.replace("__TOP_LABEL__", top_label)
1699
  html = html.replace("__CLICK_COUNT__", str(mouse["click_count"]))
1700
  html = html.replace("__RANKING_JSON__", json.dumps(ranking))
1701
- html = html.replace("__TIMELINE_JSON__", json.dumps(timeline))
1702
  html = html.replace("__SEGMENTS_JSON__", json.dumps(segments))
1703
- html = html.replace("__DURATION_JSON__", json.dumps(summary["duration"]))
1704
  html = html.replace("__MOUSE_INTERESTS_JSON__", json.dumps(mouse["interests"]))
1705
  html = html.replace("__MOUSE_CLICKS_JSON__", json.dumps(mouse["clicks"]))
1706
  html = html.replace("__MOUSE_SEGMENTS_JSON__", json.dumps(mouse_segments))
 
1
  """
2
  analysis.py
3
  Turns a raw InsightUX session (gaze_log.jsonl + dom_log.jsonl + screenshots/)
4
+ into a self-contained HTML report: ranked attention (in plain language)
5
+ and per-scroll-position heatmaps drawn directly on top of what the page
6
+ actually looked like.
7
 
8
  Usage:
9
  from analysis import generate_report
 
201
  return ranking
202
 
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  def session_summary(gaze, dom):
205
  if not gaze:
206
  return {"duration": 0.0, "url": None, "samples": 0}
 
383
  .topbar .actions { margin-left: auto; display: flex; gap: 8px; }
384
  .topbar .actions .iux-btn { padding: 8px 13px; font-size: 12px; }
385
 
386
+ .stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 22px; }
387
  .stat {
388
  padding: 16px 18px; position: relative; overflow: hidden;
389
  }
 
531
  <div class="stat iux-card hoverable"><div class="icon-badge">__LAYERS_ICON__</div><div class="v">__NUM_ELEMENTS__</div><div class="l">Elements Fixated</div></div>
532
  <div class="stat iux-card hoverable"><div class="icon-badge">__TARGET_ICON__</div><div class="v" style="font-size:14px;">__TOP_LABEL__</div><div class="l">Most Attended</div></div>
533
  <div class="stat iux-card hoverable"><div class="icon-badge">__CURSOR_ICON__</div><div class="v">__CLICK_COUNT__</div><div class="l">Mouse Events</div></div>
 
534
  </div>
535
 
536
  <div class="grid">
 
555
  the page automatically whenever you scrolled far enough that the view
556
  changed meaningfully. Use the full-session export buttons to download
557
  one stitched image covering the entire page.</p>
 
 
558
  </div>
559
  </div>
560
  <div class="panel iux-card full iux-fade-in">
 
591
  </div>
592
  <div class="elem-chart-hint" id="elemChartHint" style="display:none;">Scroll to see every element &rarr;</div>
593
  </div>
 
 
 
 
594
  <div class="panel iux-card iux-fade-in">
595
  <h2>__CURSOR_ICON__ Mouse — Most Interacted Elements</h2>
596
  <div id="mouseInterests"></div>
 
639
  });
640
 
641
  const RANKING = __RANKING_JSON__;
 
642
  const SEGMENTS = __SEGMENTS_JSON__;
 
643
  const MOUSE_INTERESTS = __MOUSE_INTERESTS_JSON__;
644
  const MOUSE_CLICKS = __MOUSE_CLICKS_JSON__;
645
  const MOUSE_SEGMENTS = __MOUSE_SEGMENTS_JSON__;
 
1559
  window.addEventListener('resize', function(){ layout(); paint(); });
1560
  })();
1561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1562
  </script>
1563
  </body>
1564
  </html>
 
1570
  attributed = attribute_gaze(gaze, dom)
1571
 
1572
  ranking = compute_dwell_ranking(attributed)
 
1573
  segments = build_screenshot_segments(gaze, dom, session_dir)
1574
  summary = session_summary(gaze, dom)
1575
  mouse = summarize_mouse(session_dir)
 
1597
  html = html.replace("__TOP_LABEL__", top_label)
1598
  html = html.replace("__CLICK_COUNT__", str(mouse["click_count"]))
1599
  html = html.replace("__RANKING_JSON__", json.dumps(ranking))
 
1600
  html = html.replace("__SEGMENTS_JSON__", json.dumps(segments))
 
1601
  html = html.replace("__MOUSE_INTERESTS_JSON__", json.dumps(mouse["interests"]))
1602
  html = html.replace("__MOUSE_CLICKS_JSON__", json.dumps(mouse["clicks"]))
1603
  html = html.replace("__MOUSE_SEGMENTS_JSON__", json.dumps(mouse_segments))