Johan713 commited on
Commit
40fb95b
Β·
verified Β·
1 Parent(s): 2147d4a

Update pages/study_roadmap.py

Browse files
Files changed (1) hide show
  1. pages/study_roadmap.py +481 -480
pages/study_roadmap.py CHANGED
@@ -1,481 +1,482 @@
1
- import streamlit as st
2
- import networkx as nx
3
- import plotly.graph_objects as go
4
- from dotenv import load_dotenv
5
- from langchain.chat_models import ChatOpenAI
6
- from langchain.prompts import ChatPromptTemplate
7
- from langchain.output_parsers import PydanticOutputParser
8
- from pydantic import BaseModel, Field
9
- from typing import List, Dict, Optional
10
- import json
11
- import pandas as pd
12
- import time
13
- from datetime import datetime
14
- import random
15
- import re
16
- from PIL import Image
17
- import logging
18
-
19
- # Set up logging
20
- logging.basicConfig(level=logging.INFO)
21
- logger = logging.getLogger(__name__)
22
-
23
- # Load environment variables
24
- load_dotenv()
25
-
26
- AI71_BASE_URL = "https://api.ai71.ai/v1/"
27
- AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
28
-
29
- # Initialize the Falcon model
30
- chat = ChatOpenAI(
31
- model="tiiuae/falcon-180B-chat",
32
- api_key=AI71_API_KEY,
33
- base_url=AI71_BASE_URL,
34
- temperature=0.7,
35
- )
36
-
37
- class RoadmapStep(BaseModel):
38
- title: str
39
- description: str
40
- resources: List[Dict[str, str]] = Field(default_factory=list)
41
- estimated_time: str
42
- how_to_use: Optional[str] = None
43
-
44
- class Roadmap(BaseModel):
45
- steps: Dict[str, RoadmapStep] = Field(default_factory=dict)
46
-
47
- def clean_json(content):
48
- # Remove any leading or trailing whitespace
49
- content = content.strip()
50
-
51
- # Ensure the content starts and ends with curly braces
52
- if not content.startswith('{'):
53
- content = '{' + content
54
- if not content.endswith('}'):
55
- content = content + '}'
56
-
57
- # Remove any newline characters and extra spaces
58
- content = ' '.join(content.split())
59
-
60
- # Escape any unescaped double quotes within string values
61
- content = re.sub(r'(?<!\\)"(?=(?:(?:[^"]*"){2})*[^"]*$)', r'\"', content)
62
-
63
- return content
64
-
65
- def ensure_valid_json(content):
66
- # First, apply our existing cleaning function
67
- content = clean_json(content)
68
-
69
- # Use regex to find and fix unquoted property names
70
- pattern = r'(\{|\,)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:'
71
- content = re.sub(pattern, r'\1 "\2":', content)
72
-
73
- # Replace single quotes with double quotes
74
- content = content.replace("'", '"')
75
-
76
- # Attempt to parse the JSON to catch any remaining issues
77
- try:
78
- json_obj = json.loads(content)
79
- return json.dumps(json_obj) # Return a properly formatted JSON string
80
- except json.JSONDecodeError as e:
81
- # If we still can't parse it, log the error and return None
82
- logger.error(f"Failed to parse JSON after cleaning: {str(e)}")
83
- logger.debug(f"Problematic JSON: {content}")
84
- return None
85
-
86
- def generate_roadmap(topic):
87
- levels = [
88
- "knowledge",
89
- "comprehension",
90
- "application",
91
- "analysis",
92
- "synthesis",
93
- "evaluation"
94
- ]
95
-
96
- roadmap = Roadmap()
97
-
98
- for level in levels:
99
- try:
100
- logger.info(f"Generating roadmap step for topic: {topic} at {level} level")
101
- step = generate_simplified_step(topic, level, chat)
102
- roadmap.steps[level] = step
103
- logger.info(f"Added step for {level} level")
104
-
105
- except Exception as e:
106
- logger.error(f"Error in generate_roadmap for {level}: {str(e)}")
107
- step = create_fallback_step(topic, level, chat)
108
- roadmap.steps[level] = step
109
-
110
- logger.info("Roadmap generation complete")
111
- return roadmap
112
-
113
- def generate_diverse_resources(topic, level):
114
- encoded_topic = topic.replace(' ', '+')
115
- encoded_level = level.replace(' ', '+')
116
-
117
- resource_templates = [
118
- {"title": "Wikipedia", "url": f"https://en.wikipedia.org/wiki/{topic.replace(' ', '_')}"},
119
- {"title": "YouTube Overview", "url": f"https://www.youtube.com/results?search_query={encoded_topic}+{encoded_level}"},
120
- {"title": "Coursera Courses", "url": f"https://www.coursera.org/search?query={encoded_topic}"},
121
- {"title": "edX Courses", "url": f"https://www.edx.org/search?q={encoded_topic}"},
122
- {"title": "Brilliant", "url": f"https://brilliant.org/search/?q={encoded_topic}"},
123
- {"title": "Google Scholar", "url": f"https://scholar.google.com/scholar?q={encoded_topic}"},
124
- {"title": "MIT OpenCourseWare", "url": f"https://ocw.mit.edu/search/?q={encoded_topic}"},
125
- {"title": "Khan Academy", "url": f"https://www.khanacademy.org/search?query={encoded_topic}"},
126
- {"title": "TED Talks", "url": f"https://www.ted.com/search?q={encoded_topic}"},
127
- {"title": "arXiv Papers", "url": f"https://arxiv.org/search/?query={encoded_topic}&searchtype=all"},
128
- {"title": "ResearchGate", "url": f"https://www.researchgate.net/search/publication?q={encoded_topic}"},
129
- {"title": "Academic Earth", "url": f"https://academicearth.org/search/?q={encoded_topic}"},
130
- ]
131
-
132
- # Randomly select 5-7 resources
133
- num_resources = random.randint(5, 7)
134
- selected_resources = random.sample(resource_templates, num_resources)
135
-
136
- return selected_resources
137
-
138
- def create_fallback_step(topic, level, chat):
139
- def generate_component(prompt, default_value):
140
- try:
141
- response = chat.invoke([{"role": "system", "content": prompt}])
142
- return response.content.strip() or default_value
143
- except Exception as e:
144
- logger.error(f"Error generating component: {str(e)}")
145
- return default_value
146
-
147
- # Generate title
148
- title_prompt = f"Create a concise title (max 10 words) for a study step about {topic} at the {level} level of Bloom's Taxonomy."
149
- default_title = f"{level.capitalize()} Step for {topic}"
150
- title = generate_component(title_prompt, default_title)
151
-
152
- # Generate description
153
- description_prompt = f"""Write a detailed description (500-700 words) for a study step about {topic} at the {level} level of Bloom's Taxonomy.
154
- Explain what this step entails, how the user should approach it, and why it's important for mastering the topic at this level.
155
- The description should be specific to {topic} and not a generic explanation of the Bloom's Taxonomy level."""
156
- default_description = f"In this step, you will focus on {topic} at the {level} level. This involves understanding key concepts and theories related to {topic}. Engage with the provided resources to build a strong foundation."
157
- description = generate_component(description_prompt, default_description)
158
-
159
- # Generate estimated time
160
- time_prompt = f"Estimate the time needed to complete a study step about {topic} at the {level} level of Bloom's Taxonomy. Provide the answer in a format like '3-4 days' or '1-2 weeks'."
161
- default_time = "3-4 days"
162
- estimated_time = generate_component(time_prompt, default_time)
163
-
164
- # Generate how to use
165
- how_to_use_prompt = f"""Write a paragraph (100-150 words) on how to effectively use the {level} level of Bloom's Taxonomy when studying {topic}.
166
- Include tips and strategies specific to {topic} at this {level} level."""
167
- default_how_to_use = f"Explore the provided resources and take notes on key concepts related to {topic}. Practice explaining these concepts in your own words to reinforce your understanding at the {level} level."
168
- how_to_use = generate_component(how_to_use_prompt, default_how_to_use)
169
-
170
- return RoadmapStep(
171
- title=title,
172
- description=description,
173
- resources=generate_diverse_resources(topic, level),
174
- estimated_time=estimated_time,
175
- how_to_use=how_to_use
176
- )
177
-
178
- def create_interactive_graph(roadmap):
179
- G = nx.DiGraph()
180
- color_map = {
181
- 'Knowledge': '#FF6B6B',
182
- 'Comprehension': '#4ECDC4',
183
- 'Application': '#45B7D1',
184
- 'Analysis': '#FFA07A',
185
- 'Synthesis': '#98D8C8',
186
- 'Evaluation': '#F9D56E'
187
- }
188
-
189
- for i, (level, step) in enumerate(roadmap.steps.items()):
190
- G.add_node(step.title, level=level.capitalize(), pos=(i, -i))
191
-
192
- pos = nx.get_node_attributes(G, 'pos')
193
-
194
- edge_trace = go.Scatter(
195
- x=[], y=[],
196
- line=dict(width=2, color='#888'),
197
- hoverinfo='none',
198
- mode='lines')
199
-
200
- node_trace = go.Scatter(
201
- x=[], y=[],
202
- mode='markers+text',
203
- hoverinfo='text',
204
- marker=dict(
205
- showscale=False,
206
- color=[],
207
- size=30,
208
- line_width=2
209
- ),
210
- text=[],
211
- textposition="top center"
212
- )
213
-
214
- for node in G.nodes():
215
- x, y = pos[node]
216
- node_trace['x'] += (x,)
217
- node_trace['y'] += (y,)
218
- node_info = f"{node}<br>Level: {G.nodes[node]['level']}"
219
- node_trace['text'] += (node_info,)
220
- node_trace['marker']['color'] += (color_map.get(G.nodes[node]['level'], '#CCCCCC'),)
221
-
222
- fig = go.Figure(data=[edge_trace, node_trace],
223
- layout=go.Layout(
224
- title='Interactive Study Roadmap',
225
- titlefont_size=16,
226
- showlegend=False,
227
- hovermode='closest',
228
- margin=dict(b=20,l=5,r=5,t=40),
229
- annotations=[dict(
230
- text="",
231
- showarrow=False,
232
- xref="paper", yref="paper",
233
- x=0.005, y=-0.002
234
- )],
235
- xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
236
- yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
237
- plot_bgcolor='rgba(0,0,0,0)',
238
- paper_bgcolor='rgba(0,0,0,0)'
239
- ))
240
-
241
- # Add a color legend
242
- for level, color in color_map.items():
243
- fig.add_trace(go.Scatter(
244
- x=[None], y=[None],
245
- mode='markers',
246
- marker=dict(size=10, color=color),
247
- showlegend=True,
248
- name=level
249
- ))
250
-
251
- fig.update_layout(legend=dict(
252
- orientation="h",
253
- yanchor="bottom",
254
- y=1.02,
255
- xanchor="right",
256
- x=1
257
- ))
258
-
259
- return fig
260
-
261
- def get_user_progress(roadmap):
262
- if 'user_progress' not in st.session_state:
263
- st.session_state.user_progress = {}
264
-
265
- for level, step in roadmap.steps.items():
266
- if step.title not in st.session_state.user_progress:
267
- st.session_state.user_progress[step.title] = 0
268
-
269
- return st.session_state.user_progress
270
-
271
- def update_user_progress(step_title, progress):
272
- st.session_state.user_progress[step_title] = progress
273
-
274
- def calculate_overall_progress(progress_dict):
275
- if not progress_dict:
276
- return 0
277
- total_steps = len(progress_dict)
278
- completed_steps = sum(1 for progress in progress_dict.values() if progress == 100)
279
- return (completed_steps / total_steps) * 100
280
-
281
- def generate_simplified_step(topic, level, chat):
282
- prompt = f"""Create a detailed study step for the topic: {topic} at the {level} level of Bloom's Taxonomy.
283
-
284
- Provide:
285
- 1. A descriptive title (max 10 words)
286
- 2. A detailed description (500-700 words) explaining what this step entails, how the user should approach it, and why it's important for mastering the topic at this level. The description should be specific to {topic} and not a generic explanation of the Bloom's Taxonomy level.
287
- 3. Estimated time for completion (e.g., 3-4 days, 1-2 weeks, etc.)
288
- 4. A paragraph (100-150 words) on how to use this level effectively, including tips and strategies specific to {topic} at this {level} level
289
-
290
- Format your response as a valid JSON object with the following structure:
291
- {{
292
- "title": "Step title",
293
- "description": "Step description",
294
- "estimated_time": "Estimated time",
295
- "how_to_use": "Paragraph on how to use this level effectively"
296
- }}
297
- """
298
-
299
- try:
300
- response = chat.invoke([{"role": "system", "content": prompt}])
301
- valid_json = ensure_valid_json(response.content)
302
- if valid_json is None:
303
- raise ValueError("Failed to create valid JSON")
304
-
305
- step_dict = json.loads(valid_json)
306
-
307
- # Generate diverse resources
308
- resources = generate_diverse_resources(topic, level)
309
-
310
- return RoadmapStep(
311
- title=step_dict["title"],
312
- description=step_dict["description"],
313
- resources=resources,
314
- estimated_time=step_dict["estimated_time"],
315
- how_to_use=step_dict["how_to_use"]
316
- )
317
- except Exception as e:
318
- logger.error(f"Error in generate_simplified_step for {level}: {str(e)}")
319
- return create_fallback_step(topic, level, chat)
320
-
321
-
322
-
323
- def display_step(step, level, user_progress):
324
- with st.expander(f"{level.capitalize()}: {step.title}"):
325
- st.write(f"**Description:** {step.description}")
326
- st.write(f"**Estimated Time:** {step.estimated_time}")
327
- st.write("**Resources:**")
328
- for resource in step.resources:
329
- st.markdown(f"- [{resource['title']}]({resource['url']})")
330
- if 'contribution' in resource:
331
- st.write(f" *{resource['contribution']}*")
332
-
333
- # Check if how_to_use exists before displaying it
334
- if step.how_to_use:
335
- st.write("**How to use this level effectively:**")
336
- st.write(step.how_to_use)
337
-
338
- progress = st.slider(f"Progress for {step.title}", 0, 100, user_progress.get(step.title, 0), key=f"progress_{level}")
339
- update_user_progress(step.title, progress)
340
-
341
- def main():
342
- st.set_page_config(page_title="S.H.E.R.L.O.C.K. Study Roadmap Generator", layout="wide")
343
-
344
- # Custom CSS for dark theme
345
- st.markdown("""
346
- <style>
347
- .stApp {
348
- background-color: #1E1E1E;
349
- color: #FFFFFF;
350
- }
351
- .stButton>button {
352
- background-color: #4CAF50;
353
- color: white;
354
- border-radius: 5px;
355
- }
356
- .stProgress > div > div > div > div {
357
- background-color: #4CAF50;
358
- }
359
- .streamlit-expanderHeader {
360
- background-color: #2E2E2E;
361
- color: #FFFFFF;
362
- }
363
- .streamlit-expanderContent {
364
- background-color: #2E2E2E;
365
- color: #FFFFFF;
366
- }
367
- </style>
368
- """, unsafe_allow_html=True)
369
-
370
- st.title("🧠 S.H.E.R.L.O.C.K. Study Roadmap Generator")
371
- st.write("Generate a comprehensive study roadmap based on first principles for any topic.")
372
-
373
- # Sidebar
374
- with st.sidebar:
375
- st.image("https://placekitten.com/300/200", caption="S.H.E.R.L.O.C.K.", use_column_width=True)
376
- st.markdown("""
377
- ## About S.H.E.R.L.O.C.K.
378
- **S**tudy **H**elper for **E**fficient **R**oadmaps and **L**earning **O**ptimization using **C**omprehensive **K**nowledge
379
-
380
- S.H.E.R.L.O.C.K. is your AI-powered study companion, designed to create personalized learning roadmaps for any topic. It breaks down complex subjects into manageable steps, ensuring a comprehensive understanding from fundamentals to advanced concepts.
381
- """)
382
-
383
- st.subheader("πŸ“‹ Todo List")
384
- if 'todos' not in st.session_state:
385
- st.session_state.todos = []
386
-
387
- new_todo = st.text_input("Add a new todo:")
388
- if st.button("Add Todo", key="add_todo"):
389
- if new_todo:
390
- st.session_state.todos.append({"task": new_todo, "completed": False})
391
- st.success("Todo added successfully!")
392
- else:
393
- st.warning("Please enter a todo item.")
394
-
395
- for i, todo in enumerate(st.session_state.todos):
396
- col1, col2, col3 = st.columns([0.05, 0.8, 0.15])
397
- with col1:
398
- todo['completed'] = st.checkbox("", todo['completed'], key=f"todo_{i}")
399
- with col2:
400
- st.write(todo['task'], key=f"todo_text_{i}")
401
- with col3:
402
- if st.button("πŸ—‘οΈ", key=f"delete_{i}", help="Delete todo"):
403
- st.session_state.todos.pop(i)
404
- st.experimental_rerun()
405
-
406
- st.subheader("⏱️ Pomodoro Timer")
407
- pomodoro_duration = st.slider("Pomodoro Duration (minutes)", 1, 60, 25)
408
- if st.button("Start Pomodoro"):
409
- progress_bar = st.progress(0)
410
- for i in range(pomodoro_duration * 60):
411
- time.sleep(1)
412
- progress_bar.progress((i + 1) / (pomodoro_duration * 60))
413
- st.success("Pomodoro completed!")
414
- if 'achievements' not in st.session_state:
415
- st.session_state.achievements = set()
416
- st.session_state.achievements.add("Consistent Learner")
417
-
418
- topic = st.text_input("πŸ“š Enter the topic you want to master:")
419
-
420
- if st.button("πŸš€ Generate Roadmap"):
421
- if topic:
422
- with st.spinner("🧠 Generating your personalized study roadmap..."):
423
- try:
424
- logger.info(f"Starting roadmap generation for topic: {topic}")
425
- roadmap = generate_roadmap(topic)
426
- if roadmap and roadmap.steps:
427
- logger.info("Roadmap generated successfully")
428
- st.session_state.current_roadmap = roadmap
429
- st.session_state.current_topic = topic
430
- st.success("Roadmap generated successfully!")
431
- else:
432
- logger.warning("Generated roadmap is empty or invalid")
433
- st.error("Failed to generate a valid roadmap. Please try again with a different topic.")
434
- except Exception as e:
435
- logger.error(f"Error during roadmap generation: {str(e)}", exc_info=True)
436
- st.error(f"An error occurred while generating the roadmap: {str(e)}")
437
-
438
- if 'current_roadmap' in st.session_state:
439
- st.subheader(f"πŸ“Š Study Roadmap for: {st.session_state.current_topic}")
440
-
441
- roadmap = st.session_state.current_roadmap
442
- fig = create_interactive_graph(roadmap)
443
- fig.update_layout(
444
- plot_bgcolor='rgba(0,0,0,0)',
445
- paper_bgcolor='rgba(0,0,0,0)',
446
- font_color='#FFFFFF'
447
- )
448
- st.plotly_chart(fig, use_container_width=True)
449
-
450
- user_progress = get_user_progress(roadmap)
451
-
452
- levels_description = {
453
- "knowledge": "Understanding and remembering basic facts and concepts",
454
- "comprehension": "Grasping the meaning and interpreting information",
455
- "application": "Using knowledge in new situations",
456
- "analysis": "Breaking information into parts and examining relationships",
457
- "synthesis": "Combining elements to form a new whole",
458
- "evaluation": "Making judgments based on criteria and standards"
459
- }
460
-
461
- for level, step in roadmap.steps.items():
462
- st.header(f"{level.capitalize()} Level")
463
- st.write(f"**Description:** {levels_description[level]}")
464
- st.write("**How to master this level:**")
465
- st.write(f"To master the {level} level, focus on {levels_description[level].lower()}. Engage with the resources provided, practice applying the concepts, and gradually build your understanding. Remember that mastery at this level is crucial before moving to the next.")
466
- display_step(step, level, user_progress)
467
-
468
- overall_progress = calculate_overall_progress(user_progress)
469
- st.progress(overall_progress / 100)
470
- st.write(f"Overall progress: {overall_progress:.2f}%")
471
-
472
- roadmap_json = json.dumps(roadmap.dict(), indent=2)
473
- st.download_button(
474
- label="πŸ“₯ Download Roadmap as JSON",
475
- data=roadmap_json,
476
- file_name="study_roadmap.json",
477
- mime="application/json"
478
- )
479
-
480
- if __name__ == "__main__":
 
481
  main()
 
1
+ import streamlit as st
2
+ import networkx as nx
3
+ import plotly.graph_objects as go
4
+ from dotenv import load_dotenv
5
+ from langchain.chat_models import ChatOpenAI
6
+ from langchain.prompts import ChatPromptTemplate
7
+ from langchain.output_parsers import PydanticOutputParser
8
+ from pydantic import BaseModel, Field
9
+ from typing import List, Dict, Optional
10
+ import json
11
+ import os
12
+ import pandas as pd
13
+ import time
14
+ from datetime import datetime
15
+ import random
16
+ import re
17
+ from PIL import Image
18
+ import logging
19
+
20
+ # Set up logging
21
+ logging.basicConfig(level=logging.INFO)
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Load environment variables
25
+ load_dotenv()
26
+
27
+ AI71_BASE_URL = "https://api.ai71.ai/v1/"
28
+ AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
29
+
30
+ # Initialize the Falcon model
31
+ chat = ChatOpenAI(
32
+ model="tiiuae/falcon-180B-chat",
33
+ api_key=AI71_API_KEY,
34
+ base_url=AI71_BASE_URL,
35
+ temperature=0.7,
36
+ )
37
+
38
+ class RoadmapStep(BaseModel):
39
+ title: str
40
+ description: str
41
+ resources: List[Dict[str, str]] = Field(default_factory=list)
42
+ estimated_time: str
43
+ how_to_use: Optional[str] = None
44
+
45
+ class Roadmap(BaseModel):
46
+ steps: Dict[str, RoadmapStep] = Field(default_factory=dict)
47
+
48
+ def clean_json(content):
49
+ # Remove any leading or trailing whitespace
50
+ content = content.strip()
51
+
52
+ # Ensure the content starts and ends with curly braces
53
+ if not content.startswith('{'):
54
+ content = '{' + content
55
+ if not content.endswith('}'):
56
+ content = content + '}'
57
+
58
+ # Remove any newline characters and extra spaces
59
+ content = ' '.join(content.split())
60
+
61
+ # Escape any unescaped double quotes within string values
62
+ content = re.sub(r'(?<!\\)"(?=(?:(?:[^"]*"){2})*[^"]*$)', r'\"', content)
63
+
64
+ return content
65
+
66
+ def ensure_valid_json(content):
67
+ # First, apply our existing cleaning function
68
+ content = clean_json(content)
69
+
70
+ # Use regex to find and fix unquoted property names
71
+ pattern = r'(\{|\,)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:'
72
+ content = re.sub(pattern, r'\1 "\2":', content)
73
+
74
+ # Replace single quotes with double quotes
75
+ content = content.replace("'", '"')
76
+
77
+ # Attempt to parse the JSON to catch any remaining issues
78
+ try:
79
+ json_obj = json.loads(content)
80
+ return json.dumps(json_obj) # Return a properly formatted JSON string
81
+ except json.JSONDecodeError as e:
82
+ # If we still can't parse it, log the error and return None
83
+ logger.error(f"Failed to parse JSON after cleaning: {str(e)}")
84
+ logger.debug(f"Problematic JSON: {content}")
85
+ return None
86
+
87
+ def generate_roadmap(topic):
88
+ levels = [
89
+ "knowledge",
90
+ "comprehension",
91
+ "application",
92
+ "analysis",
93
+ "synthesis",
94
+ "evaluation"
95
+ ]
96
+
97
+ roadmap = Roadmap()
98
+
99
+ for level in levels:
100
+ try:
101
+ logger.info(f"Generating roadmap step for topic: {topic} at {level} level")
102
+ step = generate_simplified_step(topic, level, chat)
103
+ roadmap.steps[level] = step
104
+ logger.info(f"Added step for {level} level")
105
+
106
+ except Exception as e:
107
+ logger.error(f"Error in generate_roadmap for {level}: {str(e)}")
108
+ step = create_fallback_step(topic, level, chat)
109
+ roadmap.steps[level] = step
110
+
111
+ logger.info("Roadmap generation complete")
112
+ return roadmap
113
+
114
+ def generate_diverse_resources(topic, level):
115
+ encoded_topic = topic.replace(' ', '+')
116
+ encoded_level = level.replace(' ', '+')
117
+
118
+ resource_templates = [
119
+ {"title": "Wikipedia", "url": f"https://en.wikipedia.org/wiki/{topic.replace(' ', '_')}"},
120
+ {"title": "YouTube Overview", "url": f"https://www.youtube.com/results?search_query={encoded_topic}+{encoded_level}"},
121
+ {"title": "Coursera Courses", "url": f"https://www.coursera.org/search?query={encoded_topic}"},
122
+ {"title": "edX Courses", "url": f"https://www.edx.org/search?q={encoded_topic}"},
123
+ {"title": "Brilliant", "url": f"https://brilliant.org/search/?q={encoded_topic}"},
124
+ {"title": "Google Scholar", "url": f"https://scholar.google.com/scholar?q={encoded_topic}"},
125
+ {"title": "MIT OpenCourseWare", "url": f"https://ocw.mit.edu/search/?q={encoded_topic}"},
126
+ {"title": "Khan Academy", "url": f"https://www.khanacademy.org/search?query={encoded_topic}"},
127
+ {"title": "TED Talks", "url": f"https://www.ted.com/search?q={encoded_topic}"},
128
+ {"title": "arXiv Papers", "url": f"https://arxiv.org/search/?query={encoded_topic}&searchtype=all"},
129
+ {"title": "ResearchGate", "url": f"https://www.researchgate.net/search/publication?q={encoded_topic}"},
130
+ {"title": "Academic Earth", "url": f"https://academicearth.org/search/?q={encoded_topic}"},
131
+ ]
132
+
133
+ # Randomly select 5-7 resources
134
+ num_resources = random.randint(5, 7)
135
+ selected_resources = random.sample(resource_templates, num_resources)
136
+
137
+ return selected_resources
138
+
139
+ def create_fallback_step(topic, level, chat):
140
+ def generate_component(prompt, default_value):
141
+ try:
142
+ response = chat.invoke([{"role": "system", "content": prompt}])
143
+ return response.content.strip() or default_value
144
+ except Exception as e:
145
+ logger.error(f"Error generating component: {str(e)}")
146
+ return default_value
147
+
148
+ # Generate title
149
+ title_prompt = f"Create a concise title (max 10 words) for a study step about {topic} at the {level} level of Bloom's Taxonomy."
150
+ default_title = f"{level.capitalize()} Step for {topic}"
151
+ title = generate_component(title_prompt, default_title)
152
+
153
+ # Generate description
154
+ description_prompt = f"""Write a detailed description (500-700 words) for a study step about {topic} at the {level} level of Bloom's Taxonomy.
155
+ Explain what this step entails, how the user should approach it, and why it's important for mastering the topic at this level.
156
+ The description should be specific to {topic} and not a generic explanation of the Bloom's Taxonomy level."""
157
+ default_description = f"In this step, you will focus on {topic} at the {level} level. This involves understanding key concepts and theories related to {topic}. Engage with the provided resources to build a strong foundation."
158
+ description = generate_component(description_prompt, default_description)
159
+
160
+ # Generate estimated time
161
+ time_prompt = f"Estimate the time needed to complete a study step about {topic} at the {level} level of Bloom's Taxonomy. Provide the answer in a format like '3-4 days' or '1-2 weeks'."
162
+ default_time = "3-4 days"
163
+ estimated_time = generate_component(time_prompt, default_time)
164
+
165
+ # Generate how to use
166
+ how_to_use_prompt = f"""Write a paragraph (100-150 words) on how to effectively use the {level} level of Bloom's Taxonomy when studying {topic}.
167
+ Include tips and strategies specific to {topic} at this {level} level."""
168
+ default_how_to_use = f"Explore the provided resources and take notes on key concepts related to {topic}. Practice explaining these concepts in your own words to reinforce your understanding at the {level} level."
169
+ how_to_use = generate_component(how_to_use_prompt, default_how_to_use)
170
+
171
+ return RoadmapStep(
172
+ title=title,
173
+ description=description,
174
+ resources=generate_diverse_resources(topic, level),
175
+ estimated_time=estimated_time,
176
+ how_to_use=how_to_use
177
+ )
178
+
179
+ def create_interactive_graph(roadmap):
180
+ G = nx.DiGraph()
181
+ color_map = {
182
+ 'Knowledge': '#FF6B6B',
183
+ 'Comprehension': '#4ECDC4',
184
+ 'Application': '#45B7D1',
185
+ 'Analysis': '#FFA07A',
186
+ 'Synthesis': '#98D8C8',
187
+ 'Evaluation': '#F9D56E'
188
+ }
189
+
190
+ for i, (level, step) in enumerate(roadmap.steps.items()):
191
+ G.add_node(step.title, level=level.capitalize(), pos=(i, -i))
192
+
193
+ pos = nx.get_node_attributes(G, 'pos')
194
+
195
+ edge_trace = go.Scatter(
196
+ x=[], y=[],
197
+ line=dict(width=2, color='#888'),
198
+ hoverinfo='none',
199
+ mode='lines')
200
+
201
+ node_trace = go.Scatter(
202
+ x=[], y=[],
203
+ mode='markers+text',
204
+ hoverinfo='text',
205
+ marker=dict(
206
+ showscale=False,
207
+ color=[],
208
+ size=30,
209
+ line_width=2
210
+ ),
211
+ text=[],
212
+ textposition="top center"
213
+ )
214
+
215
+ for node in G.nodes():
216
+ x, y = pos[node]
217
+ node_trace['x'] += (x,)
218
+ node_trace['y'] += (y,)
219
+ node_info = f"{node}<br>Level: {G.nodes[node]['level']}"
220
+ node_trace['text'] += (node_info,)
221
+ node_trace['marker']['color'] += (color_map.get(G.nodes[node]['level'], '#CCCCCC'),)
222
+
223
+ fig = go.Figure(data=[edge_trace, node_trace],
224
+ layout=go.Layout(
225
+ title='Interactive Study Roadmap',
226
+ titlefont_size=16,
227
+ showlegend=False,
228
+ hovermode='closest',
229
+ margin=dict(b=20,l=5,r=5,t=40),
230
+ annotations=[dict(
231
+ text="",
232
+ showarrow=False,
233
+ xref="paper", yref="paper",
234
+ x=0.005, y=-0.002
235
+ )],
236
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
237
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
238
+ plot_bgcolor='rgba(0,0,0,0)',
239
+ paper_bgcolor='rgba(0,0,0,0)'
240
+ ))
241
+
242
+ # Add a color legend
243
+ for level, color in color_map.items():
244
+ fig.add_trace(go.Scatter(
245
+ x=[None], y=[None],
246
+ mode='markers',
247
+ marker=dict(size=10, color=color),
248
+ showlegend=True,
249
+ name=level
250
+ ))
251
+
252
+ fig.update_layout(legend=dict(
253
+ orientation="h",
254
+ yanchor="bottom",
255
+ y=1.02,
256
+ xanchor="right",
257
+ x=1
258
+ ))
259
+
260
+ return fig
261
+
262
+ def get_user_progress(roadmap):
263
+ if 'user_progress' not in st.session_state:
264
+ st.session_state.user_progress = {}
265
+
266
+ for level, step in roadmap.steps.items():
267
+ if step.title not in st.session_state.user_progress:
268
+ st.session_state.user_progress[step.title] = 0
269
+
270
+ return st.session_state.user_progress
271
+
272
+ def update_user_progress(step_title, progress):
273
+ st.session_state.user_progress[step_title] = progress
274
+
275
+ def calculate_overall_progress(progress_dict):
276
+ if not progress_dict:
277
+ return 0
278
+ total_steps = len(progress_dict)
279
+ completed_steps = sum(1 for progress in progress_dict.values() if progress == 100)
280
+ return (completed_steps / total_steps) * 100
281
+
282
+ def generate_simplified_step(topic, level, chat):
283
+ prompt = f"""Create a detailed study step for the topic: {topic} at the {level} level of Bloom's Taxonomy.
284
+
285
+ Provide:
286
+ 1. A descriptive title (max 10 words)
287
+ 2. A detailed description (500-700 words) explaining what this step entails, how the user should approach it, and why it's important for mastering the topic at this level. The description should be specific to {topic} and not a generic explanation of the Bloom's Taxonomy level.
288
+ 3. Estimated time for completion (e.g., 3-4 days, 1-2 weeks, etc.)
289
+ 4. A paragraph (100-150 words) on how to use this level effectively, including tips and strategies specific to {topic} at this {level} level
290
+
291
+ Format your response as a valid JSON object with the following structure:
292
+ {{
293
+ "title": "Step title",
294
+ "description": "Step description",
295
+ "estimated_time": "Estimated time",
296
+ "how_to_use": "Paragraph on how to use this level effectively"
297
+ }}
298
+ """
299
+
300
+ try:
301
+ response = chat.invoke([{"role": "system", "content": prompt}])
302
+ valid_json = ensure_valid_json(response.content)
303
+ if valid_json is None:
304
+ raise ValueError("Failed to create valid JSON")
305
+
306
+ step_dict = json.loads(valid_json)
307
+
308
+ # Generate diverse resources
309
+ resources = generate_diverse_resources(topic, level)
310
+
311
+ return RoadmapStep(
312
+ title=step_dict["title"],
313
+ description=step_dict["description"],
314
+ resources=resources,
315
+ estimated_time=step_dict["estimated_time"],
316
+ how_to_use=step_dict["how_to_use"]
317
+ )
318
+ except Exception as e:
319
+ logger.error(f"Error in generate_simplified_step for {level}: {str(e)}")
320
+ return create_fallback_step(topic, level, chat)
321
+
322
+
323
+
324
+ def display_step(step, level, user_progress):
325
+ with st.expander(f"{level.capitalize()}: {step.title}"):
326
+ st.write(f"**Description:** {step.description}")
327
+ st.write(f"**Estimated Time:** {step.estimated_time}")
328
+ st.write("**Resources:**")
329
+ for resource in step.resources:
330
+ st.markdown(f"- [{resource['title']}]({resource['url']})")
331
+ if 'contribution' in resource:
332
+ st.write(f" *{resource['contribution']}*")
333
+
334
+ # Check if how_to_use exists before displaying it
335
+ if step.how_to_use:
336
+ st.write("**How to use this level effectively:**")
337
+ st.write(step.how_to_use)
338
+
339
+ progress = st.slider(f"Progress for {step.title}", 0, 100, user_progress.get(step.title, 0), key=f"progress_{level}")
340
+ update_user_progress(step.title, progress)
341
+
342
+ def main():
343
+ st.set_page_config(page_title="S.H.E.R.L.O.C.K. Study Roadmap Generator", layout="wide")
344
+
345
+ # Custom CSS for dark theme
346
+ st.markdown("""
347
+ <style>
348
+ .stApp {
349
+ background-color: #1E1E1E;
350
+ color: #FFFFFF;
351
+ }
352
+ .stButton>button {
353
+ background-color: #4CAF50;
354
+ color: white;
355
+ border-radius: 5px;
356
+ }
357
+ .stProgress > div > div > div > div {
358
+ background-color: #4CAF50;
359
+ }
360
+ .streamlit-expanderHeader {
361
+ background-color: #2E2E2E;
362
+ color: #FFFFFF;
363
+ }
364
+ .streamlit-expanderContent {
365
+ background-color: #2E2E2E;
366
+ color: #FFFFFF;
367
+ }
368
+ </style>
369
+ """, unsafe_allow_html=True)
370
+
371
+ st.title("🧠 S.H.E.R.L.O.C.K. Study Roadmap Generator")
372
+ st.write("Generate a comprehensive study roadmap based on first principles for any topic.")
373
+
374
+ # Sidebar
375
+ with st.sidebar:
376
+ st.image("https://placekitten.com/300/200", caption="S.H.E.R.L.O.C.K.", use_column_width=True)
377
+ st.markdown("""
378
+ ## About S.H.E.R.L.O.C.K.
379
+ **S**tudy **H**elper for **E**fficient **R**oadmaps and **L**earning **O**ptimization using **C**omprehensive **K**nowledge
380
+
381
+ S.H.E.R.L.O.C.K. is your AI-powered study companion, designed to create personalized learning roadmaps for any topic. It breaks down complex subjects into manageable steps, ensuring a comprehensive understanding from fundamentals to advanced concepts.
382
+ """)
383
+
384
+ st.subheader("πŸ“‹ Todo List")
385
+ if 'todos' not in st.session_state:
386
+ st.session_state.todos = []
387
+
388
+ new_todo = st.text_input("Add a new todo:")
389
+ if st.button("Add Todo", key="add_todo"):
390
+ if new_todo:
391
+ st.session_state.todos.append({"task": new_todo, "completed": False})
392
+ st.success("Todo added successfully!")
393
+ else:
394
+ st.warning("Please enter a todo item.")
395
+
396
+ for i, todo in enumerate(st.session_state.todos):
397
+ col1, col2, col3 = st.columns([0.05, 0.8, 0.15])
398
+ with col1:
399
+ todo['completed'] = st.checkbox("", todo['completed'], key=f"todo_{i}")
400
+ with col2:
401
+ st.write(todo['task'], key=f"todo_text_{i}")
402
+ with col3:
403
+ if st.button("πŸ—‘οΈ", key=f"delete_{i}", help="Delete todo"):
404
+ st.session_state.todos.pop(i)
405
+ st.experimental_rerun()
406
+
407
+ st.subheader("⏱️ Pomodoro Timer")
408
+ pomodoro_duration = st.slider("Pomodoro Duration (minutes)", 1, 60, 25)
409
+ if st.button("Start Pomodoro"):
410
+ progress_bar = st.progress(0)
411
+ for i in range(pomodoro_duration * 60):
412
+ time.sleep(1)
413
+ progress_bar.progress((i + 1) / (pomodoro_duration * 60))
414
+ st.success("Pomodoro completed!")
415
+ if 'achievements' not in st.session_state:
416
+ st.session_state.achievements = set()
417
+ st.session_state.achievements.add("Consistent Learner")
418
+
419
+ topic = st.text_input("πŸ“š Enter the topic you want to master:")
420
+
421
+ if st.button("πŸš€ Generate Roadmap"):
422
+ if topic:
423
+ with st.spinner("🧠 Generating your personalized study roadmap..."):
424
+ try:
425
+ logger.info(f"Starting roadmap generation for topic: {topic}")
426
+ roadmap = generate_roadmap(topic)
427
+ if roadmap and roadmap.steps:
428
+ logger.info("Roadmap generated successfully")
429
+ st.session_state.current_roadmap = roadmap
430
+ st.session_state.current_topic = topic
431
+ st.success("Roadmap generated successfully!")
432
+ else:
433
+ logger.warning("Generated roadmap is empty or invalid")
434
+ st.error("Failed to generate a valid roadmap. Please try again with a different topic.")
435
+ except Exception as e:
436
+ logger.error(f"Error during roadmap generation: {str(e)}", exc_info=True)
437
+ st.error(f"An error occurred while generating the roadmap: {str(e)}")
438
+
439
+ if 'current_roadmap' in st.session_state:
440
+ st.subheader(f"πŸ“Š Study Roadmap for: {st.session_state.current_topic}")
441
+
442
+ roadmap = st.session_state.current_roadmap
443
+ fig = create_interactive_graph(roadmap)
444
+ fig.update_layout(
445
+ plot_bgcolor='rgba(0,0,0,0)',
446
+ paper_bgcolor='rgba(0,0,0,0)',
447
+ font_color='#FFFFFF'
448
+ )
449
+ st.plotly_chart(fig, use_container_width=True)
450
+
451
+ user_progress = get_user_progress(roadmap)
452
+
453
+ levels_description = {
454
+ "knowledge": "Understanding and remembering basic facts and concepts",
455
+ "comprehension": "Grasping the meaning and interpreting information",
456
+ "application": "Using knowledge in new situations",
457
+ "analysis": "Breaking information into parts and examining relationships",
458
+ "synthesis": "Combining elements to form a new whole",
459
+ "evaluation": "Making judgments based on criteria and standards"
460
+ }
461
+
462
+ for level, step in roadmap.steps.items():
463
+ st.header(f"{level.capitalize()} Level")
464
+ st.write(f"**Description:** {levels_description[level]}")
465
+ st.write("**How to master this level:**")
466
+ st.write(f"To master the {level} level, focus on {levels_description[level].lower()}. Engage with the resources provided, practice applying the concepts, and gradually build your understanding. Remember that mastery at this level is crucial before moving to the next.")
467
+ display_step(step, level, user_progress)
468
+
469
+ overall_progress = calculate_overall_progress(user_progress)
470
+ st.progress(overall_progress / 100)
471
+ st.write(f"Overall progress: {overall_progress:.2f}%")
472
+
473
+ roadmap_json = json.dumps(roadmap.dict(), indent=2)
474
+ st.download_button(
475
+ label="πŸ“₯ Download Roadmap as JSON",
476
+ data=roadmap_json,
477
+ file_name="study_roadmap.json",
478
+ mime="application/json"
479
+ )
480
+
481
+ if __name__ == "__main__":
482
  main()