File size: 4,738 Bytes
f9766bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
utils.py
Helper utilities: progress persistence, notes, topic lists, learning paths, etc.
"""
import json
import os
from datetime import date

PROGRESS_FILE = "learning_progress.json"
NOTES_FILE = "notes.json"

TOPICS = [
    "Photosynthesis",
    "Machine Learning",
    "World War II",
    "Python Functions",
    "Calculus",
    "Climate Change",
    "The French Revolution",
    "DNA Replication",
    "Object-Oriented Programming",
    "The Solar System",
    "Economics Supply & Demand",
    "Quantum Mechanics",
]

LEARNING_PATHS = {
    "machine learning": ["Python Functions", "Calculus", "Economics Supply & Demand", "Machine Learning"],
    "quantum mechanics": ["Calculus", "The Solar System", "Quantum Mechanics"],
    "dna replication": ["Photosynthesis", "DNA Replication"],
    "calculus": ["Python Functions", "Calculus"],
    "object-oriented programming": ["Python Functions", "Object-Oriented Programming"],
    "climate change": ["Photosynthesis", "Economics Supply & Demand", "Climate Change"],
}


def get_topics() -> list:
    return sorted(TOPICS)


def load_progress() -> dict:
    """Load progress from JSON file, or return empty structure."""
    if os.path.exists(PROGRESS_FILE):
        try:
            with open(PROGRESS_FILE, "r") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError):
            pass
    return {
        "topics_studied": [],
        "scores": [],
        "best_score": 0,
        "topic_scores": {},   # {topic: best_score_int}
        "sessions": [],       # [{topic, score, date}]
    }


def save_progress(progress: dict, topic: str = None, score: int = None) -> None:
    """Update and persist progress data."""
    if "topic_scores" not in progress:
        progress["topic_scores"] = {}
    if "sessions" not in progress:
        progress["sessions"] = []

    if topic:
        studied = progress.get("topics_studied", [])
        if topic not in studied:
            studied.append(topic)
        progress["topics_studied"] = studied

    if score is not None:
        scores = progress.get("scores", [])
        scores.append(score)
        progress["scores"] = scores
        if score > progress.get("best_score", 0):
            progress["best_score"] = score

        # Per-topic best score (store single int, not list)
        if topic:
            ts = progress["topic_scores"]
            existing = ts.get(topic, 0)
            # Handle legacy list format
            if isinstance(existing, list):
                existing = max(existing) if existing else 0
            ts[topic] = max(existing, score)
            progress["topic_scores"] = ts

        # Session log
        sessions = progress.get("sessions", [])
        sessions.append({
            "topic": topic or "Unknown",
            "score": score,
            "date": str(date.today())
        })
        progress["sessions"] = sessions

    try:
        with open(PROGRESS_FILE, "w") as f:
            json.dump(progress, f, indent=2)
    except IOError:
        pass


def get_weak_topics(progress: dict) -> list:
    """Return topics where the best score is below 60%."""
    result = []
    for topic, score in progress.get("topic_scores", {}).items():
        # Handle legacy list format
        if isinstance(score, list):
            score = max(score) if score else 0
        if score < 60:
            result.append(topic)
    return result


def get_learning_path(topic: str) -> list:
    """Return recommended learning path for a topic, or empty list."""
    return LEARNING_PATHS.get(topic.lower().strip(), [])


# ── Notes ────────────────────────────────────────────────────────────────────

def load_notes() -> list:
    """Load saved notes from JSON file."""
    if os.path.exists(NOTES_FILE):
        try:
            with open(NOTES_FILE, "r") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError):
            pass
    return []


def save_note(note_text: str, topic: str) -> None:
    """Append a note and persist to file."""
    notes = load_notes()
    notes.append({
        "topic": topic,
        "note": note_text,
        "date": str(date.today())
    })
    try:
        with open(NOTES_FILE, "w") as f:
            json.dump(notes, f, indent=2)
    except IOError:
        pass


def delete_note(index: int) -> None:
    """Delete a note by its index."""
    notes = load_notes()
    if 0 <= index < len(notes):
        notes.pop(index)
    try:
        with open(NOTES_FILE, "w") as f:
            json.dump(notes, f, indent=2)
    except IOError:
        pass