Gyan.AI / backend /app /learning_paths.py
cryogenic22's picture
Rename src/learning_paths.py to backend/app/learning_paths.py
1cdb1ee verified
import streamlit as st
from datetime import datetime
from typing import Dict, List, Optional
import json
class LearningPaths:
def __init__(self):
self.initialize_session_state()
self.load_curriculum()
@staticmethod
def initialize_session_state():
"""Initialize session state for learning paths if not already present"""
if 'learning_state' not in st.session_state:
st.session_state.learning_state = {
'current_path': None,
'current_module': None,
'completed_modules': set(),
'achievements': [],
'quiz_scores': {},
'learning_streak': 0,
'last_active': None,
'mastery_levels': {} # Track concept mastery levels
}
def load_curriculum(self):
"""Load the complete curriculum structure"""
self.curriculum = {
'python_basics': {
'name': 'Python Programming Basics',
'description': 'Master the fundamental concepts of Python programming.',
'prerequisites': [],
'modules': [
{
'id': 'intro_python',
'name': 'Introduction to Python',
'concepts': ['programming_basics', 'python_environment', 'basic_syntax'],
'difficulty': 'beginner',
'estimated_hours': 2
},
{
'id': 'variables_types',
'name': 'Variables and Data Types',
'concepts': ['variables', 'numbers', 'strings', 'type_conversion'],
'difficulty': 'beginner',
'estimated_hours': 3
},
{
'id': 'control_flow',
'name': 'Control Flow',
'concepts': ['conditionals', 'loops', 'break_continue'],
'difficulty': 'beginner',
'estimated_hours': 4
},
{
'id': 'functions_basics',
'name': 'Functions',
'concepts': ['function_definition', 'parameters', 'return_values'],
'difficulty': 'beginner',
'estimated_hours': 4
}
]
},
'data_structures': {
'name': 'Data Structures',
'description': 'Learn essential Python data structures and their operations.',
'prerequisites': ['python_basics'],
'modules': [
{
'id': 'lists_tuples',
'name': 'Lists and Tuples',
'concepts': ['list_operations', 'tuple_basics', 'sequence_types'],
'difficulty': 'intermediate',
'estimated_hours': 4
},
{
'id': 'dictionaries',
'name': 'Dictionaries',
'concepts': ['dict_operations', 'key_value_pairs', 'dict_methods'],
'difficulty': 'intermediate',
'estimated_hours': 3
},
{
'id': 'sets',
'name': 'Sets',
'concepts': ['set_operations', 'set_methods', 'set_theory'],
'difficulty': 'intermediate',
'estimated_hours': 3
},
{
'id': 'advanced_ops',
'name': 'Advanced Operations',
'concepts': ['comprehensions', 'generators', 'iterators'],
'difficulty': 'intermediate',
'estimated_hours': 5
}
]
}
}
def display(self):
"""Display the learning paths interface"""
st.header("Learning Paths")
# Path selection
selected_path = st.selectbox(
"Select Learning Path",
options=list(self.curriculum.keys()),
format_func=lambda x: self.curriculum[x]['name'],
key="path_selector"
)
# Display selected path details
if selected_path:
self.display_path_details(selected_path)
def display_path_details(self, path_id: str):
"""Display detailed view of a learning path"""
path = self.curriculum[path_id]
# Path header and description
st.subheader(path['name'])
st.write(path['description'])
# Prerequisites check
if path['prerequisites']:
prereq_met = self.check_prerequisites(path['prerequisites'])
if not prereq_met:
st.warning("⚠️ Please complete the prerequisite paths first: " +
", ".join([self.curriculum[p]['name'] for p in path['prerequisites']]))
return
# Progress overview
total_modules = len(path['modules'])
completed_modules = len([m for m in path['modules']
if m['id'] in st.session_state.learning_state['completed_modules']])
progress = completed_modules / total_modules
st.progress(progress, f"Progress: {completed_modules}/{total_modules} modules completed")
# Display modules
for module in path['modules']:
self.display_module(module, path_id)
def display_module(self, module: Dict, path_id: str):
"""Display individual module with its details and status"""
completed = module['id'] in st.session_state.learning_state['completed_modules']
with st.expander(
f"{'βœ…' if completed else 'πŸ“'} {module['name']} "
f"({module['difficulty']} β€’ {module['estimated_hours']}h)",
expanded=not completed
):
# Module description and concepts
st.write("**Key Concepts:**")
for concept in module['concepts']:
mastery = self.get_concept_mastery(concept)
st.write(f"- {concept.replace('_', ' ').title()}: {self.format_mastery(mastery)}")
# Module actions
cols = st.columns([1, 1, 1])
with cols[0]:
if st.button("Start Learning", key=f"start_{module['id']}",
disabled=completed):
self.start_module(module['id'], path_id)
with cols[1]:
if st.button("Take Quiz", key=f"quiz_{module['id']}"):
self.launch_quiz(module['id'])
with cols[2]:
if st.button("Mark Complete", key=f"complete_{module['id']}",
disabled=completed):
self.complete_module(module['id'], path_id)
def start_module(self, module_id: str, path_id: str):
"""Handle module start action"""
st.session_state.learning_state['current_module'] = module_id
st.session_state.learning_state['current_path'] = path_id
self.update_learning_streak()
st.rerun()
def complete_module(self, module_id: str, path_id: str):
"""Handle module completion"""
state = st.session_state.learning_state
# Mark module as completed
state['completed_modules'].add(module_id)
# Update concept mastery
module = next(m for m in self.curriculum[path_id]['modules']
if m['id'] == module_id)
for concept in module['concepts']:
self.update_concept_mastery(concept)
# Check for achievements
self.check_achievements(module_id, path_id)
# Update streak
self.update_learning_streak()
st.success(f"πŸŽ‰ Congratulations! You've completed {module['name']}!")
st.rerun()
def launch_quiz(self, module_id: str):
"""Initialize and launch module quiz"""
st.session_state.quiz_active = True
st.session_state.current_quiz_module = module_id
st.rerun()
def check_prerequisites(self, prerequisites: List[str]) -> bool:
"""Check if prerequisites are met"""
for prereq in prerequisites:
prereq_modules = {m['id'] for m in self.curriculum[prereq]['modules']}
if not prereq_modules.issubset(st.session_state.learning_state['completed_modules']):
return False
return True
def update_concept_mastery(self, concept: str, score: float = 0.8):
"""Update mastery level for a concept"""
current = st.session_state.learning_state['mastery_levels'].get(concept, 0.0)
st.session_state.learning_state['mastery_levels'][concept] = min(1.0, current + score)
def get_concept_mastery(self, concept: str) -> float:
"""Get current mastery level for a concept"""
return st.session_state.learning_state['mastery_levels'].get(concept, 0.0)
@staticmethod
def format_mastery(mastery: float) -> str:
"""Format mastery level for display"""
if mastery >= 0.8:
return "🌟 Mastered"
elif mastery >= 0.5:
return "πŸ“ˆ In Progress"
else:
return "πŸ”Έ Not Started"
def update_learning_streak(self):
"""Update the user's learning streak"""
state = st.session_state.learning_state
today = datetime.now().date()
if state['last_active'] is None:
state['learning_streak'] = 1
else:
last_active = datetime.strptime(state['last_active'], "%Y-%m-%d").date()
if (today - last_active).days == 1:
state['learning_streak'] += 1
elif (today - last_active).days > 1:
state['learning_streak'] = 1
state['last_active'] = today.strftime("%Y-%m-%d")
def check_achievements(self, module_id: str, path_id: str):
"""Check and award achievements"""
state = st.session_state.learning_state
achievements = []
# First module completion
if len(state['completed_modules']) == 1:
achievements.append({
'name': 'First Steps! πŸŽ‰',
'description': 'Completed your first module',
'date': datetime.now().strftime("%Y-%m-%d")
})
# Path completion
path_modules = {m['id'] for m in self.curriculum[path_id]['modules']}
if path_modules.issubset(state['completed_modules']):
achievements.append({
'name': f'Path Master: {self.curriculum[path_id]["name"]} πŸ†',
'description': f'Completed the entire {self.curriculum[path_id]["name"]} path',
'date': datetime.now().strftime("%Y-%m-%d")
})
# Learning streak achievements
streak_achievements = {
7: 'Week Warrior! πŸ—“οΈ',
30: 'Monthly Master! πŸ“…',
100: 'Centurion! πŸ’―'
}
for days, name in streak_achievements.items():
if state['learning_streak'] >= days:
achievement_exists = any(a['name'] == name for a in state['achievements'])
if not achievement_exists:
achievements.append({
'name': name,
'description': f'Maintained a {days}-day learning streak',
'date': datetime.now().strftime("%Y-%m-%d")
})
# Add new achievements
state['achievements'].extend(achievements)
# Display new achievements
for achievement in achievements:
st.balloons()
st.success(f"πŸ† New Achievement: {achievement['name']} - {achievement['description']}")