Spaces:
Sleeping
Sleeping
| """ | |
| 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 |