cryogenic22 commited on
Commit
269e6e2
·
verified ·
1 Parent(s): fa496d8

Update src/components/learning_paths.py

Browse files
Files changed (1) hide show
  1. src/components/learning_paths.py +302 -58
src/components/learning_paths.py CHANGED
@@ -1,89 +1,333 @@
 
 
1
  from datetime import datetime
2
- from typing import Dict, List, Optional
3
- import streamlit as st
4
- from src.services.learning_service import LearningService
5
- from src.utils.session import get_user_progress
 
6
 
7
  class LearningPaths:
8
  def __init__(self):
9
- self.service = LearningService()
 
 
10
 
11
- @staticmethod
12
- def display():
13
- paths = LearningPaths()
14
- paths.display_interface()
 
 
 
 
15
 
16
- def display_interface(self):
17
- """Display the learning paths interface"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  st.header("Learning Paths")
19
 
20
- # Path selection
21
  selected_path = st.selectbox(
22
  "Select Learning Path",
23
- options=list(self.service.curriculum.keys()),
24
- format_func=lambda x: self.service.curriculum[x]['name'],
25
- key="path_selector"
26
  )
27
 
28
- # Display selected path details
29
  if selected_path:
30
- self.display_path_details(selected_path)
31
 
32
- def display_path_details(self, path_id: str):
33
- """Display detailed view of a learning path"""
34
- path = self.service.curriculum[path_id]
35
 
36
- # Path header and description
37
  st.subheader(path['name'])
38
  st.write(path['description'])
39
 
40
- # Prerequisites check
41
  if path['prerequisites']:
42
- prereq_met = self.service.check_prerequisites(path['prerequisites'])
43
- if not prereq_met:
44
  st.warning("⚠️ Please complete the prerequisite paths first: " +
45
- ", ".join([self.service.curriculum[p]['name'] for p in path['prerequisites']]))
46
  return
47
 
48
  # Progress overview
49
- total_modules = len(path['modules'])
50
- progress = self.service.get_path_progress(path_id)
51
- st.progress(progress,
52
- f"Progress: {int(progress * total_modules)}/{total_modules} modules completed")
53
 
54
  # Display modules
55
  for module in path['modules']:
56
- self.display_module(module, path_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- def display_module(self, module: Dict, path_id: str):
59
- """Display individual module with its details and status"""
60
- completed = self.service.is_module_completed(module['id'])
 
61
 
62
- with st.expander(
63
- f"{'' if completed else '📝'} {module['name']} "
64
- f"({module['difficulty']} {module['estimated_hours']}h)",
65
- expanded=not completed
66
- ):
67
- # Module description and concepts
68
- st.write("**Key Concepts:**")
69
- for concept in module['concepts']:
70
- mastery = self.service.get_concept_mastery(concept)
71
- st.write(f"- {concept.replace('_', ' ').title()}: {self.service.format_mastery(mastery)}")
72
-
73
- # Module actions
74
- cols = st.columns([1, 1, 1])
75
 
76
- with cols[0]:
77
- if st.button("Start Learning", key=f"start_{module['id']}", disabled=completed):
78
- self.service.start_module(module['id'], path_id)
79
- st.rerun()
 
 
 
 
80
 
81
- with cols[1]:
82
- if st.button("Take Quiz", key=f"quiz_{module['id']}"):
83
- self.service.launch_quiz(module['id'])
84
- st.rerun()
 
 
85
 
86
- with cols[2]:
87
- if st.button("Mark Complete", key=f"complete_{module['id']}", disabled=completed):
88
- self.service.complete_module(module['id'], path_id)
89
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import json
3
  from datetime import datetime
4
+ import os
5
+ from huggingface_hub import HfApi, Repository
6
+ from pathlib import Path
7
+ import tempfile
8
+ import anthropic
9
 
10
  class LearningPaths:
11
  def __init__(self):
12
+ self.initialize_storage()
13
+ self.initialize_llm()
14
+ self.load_curriculum()
15
 
16
+ def initialize_storage(self):
17
+ """Initialize HuggingFace storage connection"""
18
+ try:
19
+ self.hf_token = os.getenv('HF_TOKEN')
20
+ self.repo_id = os.getenv('HF_REPO_ID', 'default-eduai-content')
21
+ self.api = HfApi(token=self.hf_token)
22
+ except Exception as e:
23
+ st.error(f"Error initializing storage: {e}")
24
 
25
+ def initialize_llm(self):
26
+ """Initialize Anthropic/Claude client"""
27
+ try:
28
+ self.client = anthropic.Anthropic(
29
+ api_key=os.getenv('ANTHROPIC_API_KEY')
30
+ )
31
+ except Exception as e:
32
+ st.error(f"Error initializing LLM: {e}")
33
+
34
+ def load_curriculum(self):
35
+ """Load or initialize curriculum structure"""
36
+ # Try loading from HuggingFace
37
+ curriculum = self.load_from_storage("curriculum.json")
38
+ if curriculum:
39
+ self.curriculum = curriculum
40
+ return
41
+
42
+ # Initialize default curriculum
43
+ self.curriculum = {
44
+ 'python_basics': {
45
+ 'name': 'Python Programming Basics',
46
+ 'description': 'Master the fundamental concepts of Python programming.',
47
+ 'prerequisites': [],
48
+ 'modules': [
49
+ {
50
+ 'id': 'intro_python',
51
+ 'name': 'Introduction to Python',
52
+ 'concepts': ['programming_basics', 'python_environment', 'basic_syntax'],
53
+ 'difficulty': 'beginner',
54
+ 'estimated_hours': 2
55
+ },
56
+ {
57
+ 'id': 'variables_types',
58
+ 'name': 'Variables and Data Types',
59
+ 'concepts': ['variables', 'numbers', 'strings', 'type_conversion'],
60
+ 'difficulty': 'beginner',
61
+ 'estimated_hours': 3
62
+ }
63
+ ]
64
+ },
65
+ 'data_structures': {
66
+ 'name': 'Data Structures',
67
+ 'description': 'Learn essential Python data structures and their operations.',
68
+ 'prerequisites': ['python_basics'],
69
+ 'modules': [
70
+ {
71
+ 'id': 'lists_tuples',
72
+ 'name': 'Lists and Tuples',
73
+ 'concepts': ['list_operations', 'tuple_basics', 'sequence_types'],
74
+ 'difficulty': 'intermediate',
75
+ 'estimated_hours': 4
76
+ },
77
+ {
78
+ 'id': 'dictionaries',
79
+ 'name': 'Dictionaries',
80
+ 'concepts': ['dict_operations', 'key_value_pairs', 'dict_methods'],
81
+ 'difficulty': 'intermediate',
82
+ 'estimated_hours': 3
83
+ }
84
+ ]
85
+ }
86
+ }
87
+
88
+ # Save initial curriculum
89
+ self.save_to_storage(self.curriculum, "curriculum.json")
90
+
91
+ def generate_module_content(self, module_id: str) -> dict:
92
+ """Generate content for a module using Claude"""
93
+ # Find module details
94
+ module = None
95
+ for path in self.curriculum.values():
96
+ for m in path['modules']:
97
+ if m['id'] == module_id:
98
+ module = m
99
+ break
100
+ if module:
101
+ break
102
+
103
+ if not module:
104
+ return None
105
+
106
+ try:
107
+ # Generate content using Claude
108
+ message = self.client.messages.create(
109
+ model="claude-3-opus-20240229",
110
+ max_tokens=1024,
111
+ temperature=0.7,
112
+ system="You are an expert curriculum designer and educator. Create detailed, engaging learning content with clear examples and practice exercises.",
113
+ messages=[{
114
+ "role": "user",
115
+ "content": f"""Create comprehensive learning content for: {module['name']}
116
+
117
+ Include:
118
+ 1. A clear introduction
119
+ 2. Key concepts: {', '.join(module['concepts'])}
120
+ 3. Detailed explanations with examples
121
+ 4. Practice exercises
122
+ 5. A 5-question multiple choice quiz
123
+
124
+ Target difficulty level: {module['difficulty']}
125
+ Format the response as JSON with the following structure:
126
+ {{
127
+ "introduction": "text",
128
+ "sections": [
129
+ {{"title": "string", "content": "text", "examples": []}}
130
+ ],
131
+ "exercises": [
132
+ {{"title": "string", "description": "text", "solution": "text"}}
133
+ ],
134
+ "quiz": {{
135
+ "questions": [
136
+ {{"question": "text", "options": ["A", "B", "C", "D"], "correct": 0}}
137
+ ]
138
+ }}
139
+ }}"""
140
+ }]
141
+ )
142
+
143
+ content = json.loads(message.content[0].text)
144
+
145
+ # Save generated content
146
+ content_path = f"content/{module_id}.json"
147
+ self.save_to_storage(content, content_path)
148
+
149
+ return content
150
+
151
+ except Exception as e:
152
+ st.error(f"Error generating content: {e}")
153
+ return None
154
+
155
+ def get_module_content(self, module_id: str) -> dict:
156
+ """Get or generate module content"""
157
+ # Try loading existing content
158
+ content_path = f"content/{module_id}.json"
159
+ content = self.load_from_storage(content_path)
160
+
161
+ if not content:
162
+ # Generate if doesn't exist
163
+ content = self.generate_module_content(module_id)
164
+
165
+ return content
166
+
167
+ def save_to_storage(self, content: dict, path: str):
168
+ """Save content to HuggingFace"""
169
+ try:
170
+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
171
+ json.dump(content, tmp, indent=2)
172
+ tmp.flush()
173
+ self.api.upload_file(
174
+ path_or_fileobj=tmp.name,
175
+ path_in_repo=path,
176
+ repo_id=self.repo_id,
177
+ repo_type="dataset"
178
+ )
179
+ except Exception as e:
180
+ st.error(f"Error saving to storage: {e}")
181
+
182
+ def load_from_storage(self, path: str) -> dict:
183
+ """Load content from HuggingFace"""
184
+ try:
185
+ content = self.api.download_file(
186
+ repo_id=self.repo_id,
187
+ filename=path,
188
+ repo_type="dataset"
189
+ )
190
+ return json.loads(content)
191
+ except Exception:
192
+ return None
193
+
194
+ def display(self):
195
+ """Display learning paths interface"""
196
  st.header("Learning Paths")
197
 
198
+ # Topic selection
199
  selected_path = st.selectbox(
200
  "Select Learning Path",
201
+ options=list(self.curriculum.keys()),
202
+ format_func=lambda x: self.curriculum[x]['name']
 
203
  )
204
 
 
205
  if selected_path:
206
+ self.display_path_content(selected_path)
207
 
208
+ def display_path_content(self, path_id: str):
209
+ """Display content for selected path"""
210
+ path = self.curriculum[path_id]
211
 
212
+ # Path header
213
  st.subheader(path['name'])
214
  st.write(path['description'])
215
 
216
+ # Check prerequisites
217
  if path['prerequisites']:
218
+ prereqs_met = self.check_prerequisites(path['prerequisites'])
219
+ if not prereqs_met:
220
  st.warning("⚠️ Please complete the prerequisite paths first: " +
221
+ ", ".join([self.curriculum[p]['name'] for p in path['prerequisites']]))
222
  return
223
 
224
  # Progress overview
225
+ progress = self.get_path_progress(path_id)
226
+ st.progress(progress, f"Progress: {int(progress * 100)}%")
 
 
227
 
228
  # Display modules
229
  for module in path['modules']:
230
+ with st.expander(f"📚 {module['name']} ({module['difficulty']})"):
231
+ completed = self.is_module_complete(module['id'])
232
+
233
+ if completed:
234
+ st.success("✅ Module completed!")
235
+
236
+ # Get or generate content
237
+ content = self.get_module_content(module['id'])
238
+ if content:
239
+ # Display introduction
240
+ st.write(content['introduction'])
241
+
242
+ # Display sections
243
+ for section in content['sections']:
244
+ st.markdown(f"### {section['title']}")
245
+ st.write(section['content'])
246
+ if 'examples' in section:
247
+ for example in section['examples']:
248
+ st.code(example)
249
+
250
+ # Display exercises
251
+ st.markdown("### Practice Exercises")
252
+ for exercise in content['exercises']:
253
+ with st.expander(exercise['title']):
254
+ st.write(exercise['description'])
255
+ if st.button("Show Solution", key=f"sol_{module['id']}_{exercise['title']}"):
256
+ st.code(exercise['solution'])
257
+
258
+ # Display quiz
259
+ if not completed:
260
+ st.markdown("### Quiz")
261
+ correct_answers = 0
262
+ total_questions = len(content['quiz']['questions'])
263
+
264
+ for i, q in enumerate(content['quiz']['questions']):
265
+ st.write(f"\n**Question {i+1}:** {q['question']}")
266
+ answer = st.radio(
267
+ "Select your answer:",
268
+ q['options'],
269
+ key=f"quiz_{module['id']}_{i}"
270
+ )
271
+ if answer == q['options'][q['correct']]:
272
+ correct_answers += 1
273
+
274
+ if st.button("Submit Quiz", key=f"submit_{module['id']}"):
275
+ score = correct_answers / total_questions
276
+ if score >= 0.8:
277
+ st.success(f"🎉 Congratulations! Score: {score*100:.0f}%")
278
+ self.complete_module(module['id'], path_id)
279
+ else:
280
+ st.warning(f"Score: {score*100:.0f}%. You need 80% to complete the module.")
281
+ else:
282
+ st.error("Error loading module content")
283
 
284
+ def check_prerequisites(self, prerequisites: list) -> bool:
285
+ """Check if prerequisites are met"""
286
+ if 'completed_modules' not in st.session_state:
287
+ st.session_state.completed_modules = []
288
 
289
+ for prereq in prerequisites:
290
+ prereq_modules = {m['id'] for m in self.curriculum[prereq]['modules']}
291
+ if not prereq_modules.issubset(set(st.session_state.completed_modules)):
292
+ return False
293
+ return True
294
+
295
+ def get_path_progress(self, path_id: str) -> float:
296
+ """Calculate progress percentage for a path"""
297
+ if 'completed_modules' not in st.session_state:
298
+ st.session_state.completed_modules = []
 
 
 
299
 
300
+ path_modules = {m['id'] for m in self.curriculum[path_id]['modules']}
301
+ completed = path_modules.intersection(set(st.session_state.completed_modules))
302
+ return len(completed) / len(path_modules)
303
+
304
+ def is_module_complete(self, module_id: str) -> bool:
305
+ """Check if a module is completed"""
306
+ if 'completed_modules' not in st.session_state:
307
+ st.session_state.completed_modules = []
308
 
309
+ return module_id in st.session_state.completed_modules
310
+
311
+ def complete_module(self, module_id: str, path_id: str):
312
+ """Mark a module as complete and update progress"""
313
+ if 'completed_modules' not in st.session_state:
314
+ st.session_state.completed_modules = []
315
 
316
+ if module_id not in st.session_state.completed_modules:
317
+ st.session_state.completed_modules.append(module_id)
318
+
319
+ # Save progress
320
+ if 'username' in st.session_state:
321
+ progress = {
322
+ 'completed_modules': st.session_state.completed_modules,
323
+ 'last_active': datetime.now().isoformat()
324
+ }
325
+ progress_path = f"progress/{st.session_state.username}.json"
326
+ self.save_to_storage(progress, progress_path)
327
+
328
+ def load_user_progress(self, username: str):
329
+ """Load user progress from storage"""
330
+ progress_path = f"progress/{username}.json"
331
+ progress = self.load_from_storage(progress_path)
332
+ if progress:
333
+ st.session_state.completed_modules = progress.get('completed_modules', [])