Skip to content

Instantly share code, notes, and snippets.

@rahimnathwani
Created October 14, 2025 01:15
Show Gist options
  • Select an option

  • Save rahimnathwani/ff42e0e71b4936e090fa1aae67276526 to your computer and use it in GitHub Desktop.

Select an option

Save rahimnathwani/ff42e0e71b4936e090fa1aae67276526 to your computer and use it in GitHub Desktop.
Math Academy 'up next'
#!/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+)-&gt;(\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()
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