Created
October 14, 2025 01:15
-
-
Save rahimnathwani/ff42e0e71b4936e090fa1aae67276526 to your computer and use it in GitHub Desktop.
Math Academy 'up next'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Parse SVG file to find unfamiliar topics that have no unfamiliar or just-started-learning prerequisites. | |
| """ | |
| import re | |
| import xml.etree.ElementTree as ET | |
| from collections import defaultdict | |
| from typing import Dict, List, Set, Tuple | |
| def parse_svg_file(svg_file: str) -> Tuple[Dict[str, str], Dict[str, List[str]], Dict[str, str]]: | |
| """ | |
| Parse SVG file to extract: | |
| - nodes: topic_id -> topic_name | |
| - edges: topic_id -> list of prerequisite topic_ids | |
| - colors: topic_id -> rgb color | |
| """ | |
| with open(svg_file, 'r') as f: | |
| content = f.read() | |
| # Extract nodes and their text content | |
| nodes = {} | |
| colors = {} | |
| node_titles = {} # Map node_id to node_title | |
| # Find all node groups with better pattern | |
| node_pattern = r'<g id="node(\d+)" class="node">(.*?)</g>' | |
| node_matches = re.findall(node_pattern, content, re.DOTALL) | |
| for node_id, node_content in node_matches: | |
| # Extract ellipse with color | |
| ellipse_pattern = r'<ellipse fill="rgb\(([^)]+)\)"' | |
| ellipse_match = re.search(ellipse_pattern, node_content) | |
| if ellipse_match: | |
| color = ellipse_match.group(1) | |
| colors[node_id] = color | |
| # Extract node title (the number in the title tag) | |
| title_pattern = r'<title[^>]*>(\d+)</title>' | |
| title_match = re.search(title_pattern, node_content) | |
| if title_match: | |
| node_title = title_match.group(1) | |
| node_titles[node_id] = node_title | |
| # Extract all text content within this node | |
| text_pattern = r'<text[^>]*>([^<]+)</text>' | |
| text_matches = re.findall(text_pattern, node_content) | |
| if text_matches: | |
| # Join multiple text lines and clean up | |
| topic_name = ' '.join(text_matches).strip() | |
| # Remove extra whitespace | |
| topic_name = re.sub(r'\s+', ' ', topic_name) | |
| nodes[node_id] = topic_name | |
| # Extract edges (dependencies) - now using node titles | |
| edges = defaultdict(list) | |
| # Find all edge groups | |
| edge_pattern = r'<g id="edge\d+" class="edge">(.*?)</g>' | |
| edge_matches = re.findall(edge_pattern, content, re.DOTALL) | |
| for edge_content in edge_matches: | |
| # Extract title to get source->target (using node titles) | |
| title_pattern = r'<title[^>]*>(\d+)->(\d+)</title>' | |
| title_match = re.search(title_pattern, edge_content) | |
| if title_match: | |
| source_title = title_match.group(1) | |
| target_title = title_match.group(2) | |
| # Find the node IDs that correspond to these titles | |
| source_id = None | |
| target_id = None | |
| for node_id, title in node_titles.items(): | |
| if title == source_title: | |
| source_id = node_id | |
| if title == target_title: | |
| target_id = node_id | |
| if source_id and target_id: | |
| edges[target_id].append(source_id) # target depends on source | |
| return nodes, dict(edges), colors | |
| def get_learning_status(color: str) -> str: | |
| """Map RGB color to learning status.""" | |
| # Parse RGB values | |
| rgb_match = re.match(r'(\d+),\s*(\d+),\s*(\d+)', color) | |
| if not rgb_match: | |
| return "unknown" | |
| r, g, b = map(int, rgb_match.groups()) | |
| # Dark blue (mastered) | |
| if (r == 29 and g == 134 and b == 226) or (r == 23 and g == 107 and b == 181): | |
| return "mastered" | |
| # Medium blue (still learning) | |
| elif r == 120 and g == 182 and b == 237: | |
| return "still_learning" | |
| # Light blue (just started) | |
| elif (r == 165 and g == 207 and b == 243) or (r == 210 and g == 231 and b == 249): | |
| return "just_started" | |
| # White/gray (unfamiliar) | |
| elif r >= 240 and g >= 240 and b >= 240: | |
| return "unfamiliar" | |
| else: | |
| return "unknown" | |
| def debug_topic(nodes: Dict[str, str], edges: Dict[str, List[str]], colors: Dict[str, str], target_topic: str): | |
| """Debug a specific topic to see its prerequisites and their status.""" | |
| print(f"\nDebugging topic: {target_topic}") | |
| # Find the topic ID | |
| topic_id = None | |
| for tid, name in nodes.items(): | |
| if name == target_topic: | |
| topic_id = tid | |
| break | |
| if not topic_id: | |
| print(f"Topic '{target_topic}' not found!") | |
| return | |
| print(f"Topic ID: {topic_id}") | |
| print(f"Topic status: {get_learning_status(colors[topic_id])}") | |
| # Check prerequisites | |
| prerequisites = edges.get(topic_id, []) | |
| print(f"Number of prerequisites: {len(prerequisites)}") | |
| for prereq_id in prerequisites: | |
| prereq_name = nodes.get(prereq_id, "Unknown") | |
| prereq_color = colors.get(prereq_id, "Unknown") | |
| prereq_status = get_learning_status(prereq_color) | |
| print(f" Prerequisite: {prereq_name}") | |
| print(f" ID: {prereq_id}") | |
| print(f" Color: {prereq_color}") | |
| print(f" Status: {prereq_status}") | |
| print() | |
| def categorize_ready_topics(nodes: Dict[str, str], edges: Dict[str, List[str]], colors: Dict[str, str]) -> Tuple[List[Tuple[str, List[str]]], List[Tuple[str, List[str]]], List[Tuple[str, List[str]]]]: | |
| """ | |
| Categorize unfamiliar topics that are ready to learn into three groups: | |
| 1. All prerequisites mastered | |
| 2. At least one prerequisite mastered, rest still learning | |
| 3. All prerequisites still learning | |
| """ | |
| all_mastered = [] | |
| mixed_mastered_still_learning = [] | |
| all_still_learning = [] | |
| for topic_id, topic_name in nodes.items(): | |
| if topic_id not in colors: | |
| continue | |
| status = get_learning_status(colors[topic_id]) | |
| # Only consider unfamiliar topics | |
| if status != "unfamiliar": | |
| continue | |
| # Check prerequisites | |
| prerequisites = edges.get(topic_id, []) | |
| prerequisite_names = [] | |
| prerequisite_statuses = [] | |
| # If no prerequisites, skip (shouldn't happen for ready topics) | |
| if not prerequisites: | |
| continue | |
| # Check prerequisite statuses | |
| all_prereqs_ready = True | |
| mastered_count = 0 | |
| still_learning_count = 0 | |
| for prereq_id in prerequisites: | |
| if prereq_id not in colors: | |
| all_prereqs_ready = False | |
| break | |
| prereq_status = get_learning_status(colors[prereq_id]) | |
| if prereq_status in ["unfamiliar", "just_started"]: | |
| all_prereqs_ready = False | |
| break | |
| # Add prerequisite name and status for display | |
| if prereq_id in nodes: | |
| prereq_name = nodes[prereq_id] | |
| prerequisite_names.append(prereq_name) | |
| prerequisite_statuses.append(prereq_status) | |
| if prereq_status == "mastered": | |
| mastered_count += 1 | |
| elif prereq_status == "still_learning": | |
| still_learning_count += 1 | |
| if all_prereqs_ready: | |
| # Categorize based on prerequisite statuses | |
| if mastered_count > 0 and still_learning_count == 0: | |
| # All prerequisites mastered | |
| all_mastered.append((topic_name, prerequisite_names)) | |
| elif mastered_count > 0 and still_learning_count > 0: | |
| # Mixed: at least one mastered, rest still learning | |
| mixed_mastered_still_learning.append((topic_name, prerequisite_names)) | |
| elif mastered_count == 0 and still_learning_count > 0: | |
| # All prerequisites still learning | |
| all_still_learning.append((topic_name, prerequisite_names)) | |
| return all_mastered, mixed_mastered_still_learning, all_still_learning | |
| def main(): | |
| svg_file = "mathacademy.svg" | |
| print("Parsing SVG file...") | |
| nodes, edges, colors = parse_svg_file(svg_file) | |
| print(f"Found {len(nodes)} topics") | |
| print(f"Found {len(edges)} topics with dependencies") | |
| # Debug: show some examples | |
| print("\nColor mapping examples:") | |
| for topic_id, color in list(colors.items())[:5]: | |
| topic_name = nodes.get(topic_id, "Unknown") | |
| status = get_learning_status(color) | |
| print(f" {topic_name}: {color} -> {status}") | |
| print("\nFinding ready topics...") | |
| all_mastered, mixed_mastered_still_learning, all_still_learning = categorize_ready_topics(nodes, edges, colors) | |
| print(f"\n=== CATEGORIZED READY-TO-LEARN TOPICS ===") | |
| print(f"(All topics are unfamiliar with no unfamiliar/just-started prerequisites)") | |
| print() | |
| # Category 1: All prerequisites mastered | |
| print(f"1. ALL PREREQUISITES MASTERED ({len(all_mastered)} topics):") | |
| print(" These topics can be learned immediately since all prerequisites are mastered.") | |
| print() | |
| for i, (topic, prereqs) in enumerate(sorted(all_mastered), 1): | |
| print(f" {i:2d}. {topic}") | |
| for prereq in prereqs: | |
| print(f" └─ {prereq} (mastered)") | |
| print() | |
| # Category 2: Mixed mastered and still learning | |
| print(f"2. MIXED PREREQUISITES ({len(mixed_mastered_still_learning)} topics):") | |
| print(" These topics have at least one mastered prerequisite and the rest are still learning.") | |
| print() | |
| for i, (topic, prereqs) in enumerate(sorted(mixed_mastered_still_learning), 1): | |
| print(f" {i:2d}. {topic}") | |
| for prereq in prereqs: | |
| # Find the status of this prerequisite | |
| prereq_status = "unknown" | |
| for tid, color in colors.items(): | |
| if nodes.get(tid) == prereq: | |
| prereq_status = get_learning_status(color) | |
| break | |
| print(f" └─ {prereq} ({prereq_status})") | |
| print() | |
| # Category 3: All prerequisites still learning | |
| print(f"3. ALL PREREQUISITES STILL LEARNING ({len(all_still_learning)} topics):") | |
| print(" These topics have all prerequisites still being learned.") | |
| print() | |
| for i, (topic, prereqs) in enumerate(sorted(all_still_learning), 1): | |
| print(f" {i:2d}. {topic}") | |
| for prereq in prereqs: | |
| print(f" └─ {prereq} (still learning)") | |
| print() | |
| total_ready = len(all_mastered) + len(mixed_mastered_still_learning) + len(all_still_learning) | |
| print(f"TOTAL READY-TO-LEARN TOPICS: {total_ready}") | |
| # Show some statistics | |
| status_counts = defaultdict(int) | |
| for topic_id, color in colors.items(): | |
| status = get_learning_status(color) | |
| status_counts[status] += 1 | |
| print(f"\nStatus breakdown:") | |
| print(f" Mastered: {status_counts['mastered']}") | |
| print(f" Still learning: {status_counts['still_learning']}") | |
| print(f" Just started: {status_counts['just_started']}") | |
| print(f" Unfamiliar: {status_counts['unfamiliar']}") | |
| print(f" Unknown: {status_counts['unknown']}") | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Parsing SVG file... | |
| Found 223 topics | |
| Found 208 topics with dependencies | |
| Color mapping examples: | |
| Equivalent Expressions With Fractions: 120, 182, 237 -> still_learning | |
| Simplifying Rational Expressions: 120, 182, 237 -> still_learning | |
| Simplifying Rational Expressions by Factoring: 120, 182, 237 -> still_learning | |
| Solving Rational Equations Containing One Fractional Term: 165, 207, 243 -> just_started | |
| Revenue, Cost, and Profit Functions: 242, 242, 242 -> unfamiliar | |
| Finding ready topics... | |
| === CATEGORIZED READY-TO-LEARN TOPICS === | |
| (All topics are unfamiliar with no unfamiliar/just-started prerequisites) | |
| 1. ALL PREREQUISITES MASTERED (11 topics): | |
| These topics can be learned immediately since all prerequisites are mastered. | |
| 1. Distance-Time Graphs | |
| └─ Piecewise Functions (mastered) | |
| └─ Analyzing and Interpreting Graphs of Linear Equations (mastered) | |
| 2. Evaluating Algebraic Radical Expressions | |
| └─ Introduction to Functions (mastered) | |
| 3. Fibonacci Sequences | |
| └─ Recursive Sequences (mastered) | |
| 4. Finding the Nth Term of an Arithmetic Sequence Given Two Terms | |
| └─ The Nth Term of an Arithmetic Sequence (mastered) | |
| └─ Finding the Common Difference of an Arithmetic Sequence (mastered) | |
| 5. Further Rules of Absolute Value | |
| └─ Rules of Absolute Value (mastered) | |
| 6. Graphing Non-Strict Two-Variable Linear Inequalities | |
| └─ Equations of Lines in Slope-Intercept Form (mastered) | |
| 7. Recursive Formulas for Arithmetic Sequences | |
| └─ Recursive Sequences (mastered) | |
| └─ Arithmetic Sequences (mastered) | |
| 8. Simplifying Polynomials | |
| └─ The Degree of a Polynomial (mastered) | |
| 9. Solving Systems of Linear Equations Using Elimination: Two Transformations | |
| └─ Solving Systems of Linear Equations Using Elimination: One Transformation (mastered) | |
| 10. Solving for Variables in Arithmetic Sequences | |
| └─ Finding the Common Difference of an Arithmetic Sequence (mastered) | |
| 11. The Recursive Formula for a Geometric Sequence | |
| └─ Recursive Sequences (mastered) | |
| └─ Introduction to Geometric Sequences (mastered) | |
| 2. MIXED PREREQUISITES (0 topics): | |
| These topics have at least one mastered prerequisite and the rest are still learning. | |
| 3. ALL PREREQUISITES STILL LEARNING (7 topics): | |
| These topics have all prerequisites still being learned. | |
| 1. Compound AND Inequalities | |
| └─ Intersections of Intervals (still learning) | |
| └─ Solving Compound Inequalities (still learning) | |
| 2. Compound OR Inequalities | |
| └─ Solving Compound Inequalities (still learning) | |
| └─ Unions of Intervals (still learning) | |
| 3. Properties of Lines Given in Standard Form | |
| └─ Equations of Lines in Standard Form (still learning) | |
| 4. Solving Linear Equations With Unknown Coefficients | |
| └─ Simplifying Rational Expressions (still learning) | |
| 5. Unit Conversions Using Base Units of Length | |
| └─ Simplifying Rational Expressions (still learning) | |
| 6. Unit Conversions Using Base Units of Mass | |
| └─ Simplifying Rational Expressions (still learning) | |
| 7. Unit Conversions Using Units of Time | |
| └─ Simplifying Rational Expressions (still learning) | |
| TOTAL READY-TO-LEARN TOPICS: 18 | |
| Status breakdown: | |
| Mastered: 22 | |
| Still learning: 6 | |
| Just started: 26 | |
| Unfamiliar: 163 | |
| Unknown: 6 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment