File size: 3,509 Bytes
4ae4ae8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""Cross-card pattern analysis — distortion frequency, timing, themes."""

from __future__ import annotations

from session import SessionState


def analyze_patterns(state: SessionState) -> dict:
    """Analyze card deck for patterns.

    Returns dict with:
        - top_distortions: list of (name, count) sorted by frequency
        - total_cards: int
        - card_dates: list of dates
        - insight: str (generated summary)
    """
    if not state.cards:
        return {
            "top_distortions": [],
            "total_cards": 0,
            "card_dates": [],
            "insight": "Complete your first thought record to start seeing patterns.",
        }

    top = sorted(state.distortion_counts.items(), key=lambda x: x[1], reverse=True)
    dates = [c.get("date", "") for c in state.cards]

    # Generate insight
    insight = _generate_insight(top, len(state.cards))

    return {
        "top_distortions": top[:5],
        "total_cards": len(state.cards),
        "card_dates": dates,
        "insight": insight,
    }


def _generate_insight(top_distortions: list[tuple[str, int]], total_cards: int) -> str:
    """Generate a human-readable insight from patterns."""
    if not top_distortions:
        return "Keep going — patterns emerge after a few cards."

    top_name, top_count = top_distortions[0]

    if total_cards < 3:
        return f"Early pattern: {top_name} has come up {top_count} time(s). Let's see if it continues."

    pct = int((top_count / max(total_cards, 1)) * 100)
    return (
        f"Your most common pattern is {top_name} "
        f"({top_count} times across {total_cards} cards — {pct}% of sessions). "
        f"Noticing it is the first step to changing it."
    )


def get_patterns_html(state: SessionState) -> str:
    """Render patterns as HTML for the progress panel."""
    data = analyze_patterns(state)

    if not data["top_distortions"]:
        return """
        <div style="color:#5a6a7a; font-size:0.85rem; text-align:center; padding:20px;">
            <p>Complete thought records to reveal your thinking patterns.</p>
        </div>
        """

    bars = ""
    max_count = data["top_distortions"][0][1] if data["top_distortions"] else 1
    for name, count in data["top_distortions"]:
        width = int((count / max_count) * 100)
        bars += f"""
        <div style="display:flex; align-items:center; gap:8px; margin-bottom:8px;">
            <span style="font-size:0.75rem; color:#8899aa; width:120px; flex-shrink:0;">{name}</span>
            <div style="flex:1; height:6px; background:rgba(255,255,255,0.08); border-radius:3px; overflow:hidden;">
                <div style="height:100%; width:{width}%; background:linear-gradient(90deg,#8b5cf6,#a78bfa); border-radius:3px;"></div>
            </div>
            <span style="font-size:0.7rem; color:#5a6a7a; width:30px; text-align:right;">{count}x</span>
        </div>
        """

    return f"""
    <div style="background:#1e2a3a; border-radius:12px; padding:16px; border:1px solid rgba(255,255,255,0.08);">
        <h3 style="font-size:0.9rem; color:#e8edf3; margin-bottom:12px;">🔍 Your Thinking Patterns</h3>
        <div>{bars}</div>
        <div style="margin-top:12px; font-size:0.8rem; color:#e8edf3; line-height:1.4; padding:10px; background:rgba(139,92,246,0.05); border-radius:8px;">💡 {data['insight']}</div>
        <div style="margin-top:8px; font-size:0.7rem; color:#5a6a7a;">{data['total_cards']} cards completed</div>
    </div>
    """