fishertools 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- tests/test_structure_enhancements.py +115 -0
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository for managing code examples and learning scenarios.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Optional, Dict
|
|
6
|
+
from .models import (
|
|
7
|
+
CodeExample, Scenario, ProjectTemplate, LineByLineExplanation,
|
|
8
|
+
ExampleCategory, ProjectType
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExampleRepository:
|
|
13
|
+
"""
|
|
14
|
+
Manages collections of examples and scenarios for Python beginners.
|
|
15
|
+
|
|
16
|
+
Provides categorized examples with step-by-step explanations
|
|
17
|
+
and simple project templates.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, examples_dir: Optional[str] = None):
|
|
21
|
+
"""
|
|
22
|
+
Initialize the example repository.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
examples_dir: Optional directory containing example files
|
|
26
|
+
"""
|
|
27
|
+
self.examples_dir = examples_dir
|
|
28
|
+
self._examples: Dict[str, CodeExample] = {}
|
|
29
|
+
self._scenarios: Dict[str, Scenario] = {}
|
|
30
|
+
self._projects: Dict[str, ProjectTemplate] = {}
|
|
31
|
+
self._initialize_default_examples()
|
|
32
|
+
self._initialize_default_scenarios()
|
|
33
|
+
self._initialize_default_projects()
|
|
34
|
+
|
|
35
|
+
def get_examples_by_topic(self, topic: str) -> List[CodeExample]:
|
|
36
|
+
"""
|
|
37
|
+
Get all examples for a specific topic.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
topic: Topic name (e.g., "lists", "dictionaries", "functions")
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List[CodeExample]: Examples matching the topic
|
|
44
|
+
"""
|
|
45
|
+
return [
|
|
46
|
+
example for example in self._examples.values()
|
|
47
|
+
if topic.lower() in [t.lower() for t in example.topics]
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
def get_examples_by_category(self, category: ExampleCategory) -> List[CodeExample]:
|
|
51
|
+
"""
|
|
52
|
+
Get all examples in a specific category.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
category: Example category
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
List[CodeExample]: Examples in the category
|
|
59
|
+
"""
|
|
60
|
+
return [
|
|
61
|
+
example for example in self._examples.values()
|
|
62
|
+
if example.category == category
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
def get_beginner_scenarios(self) -> List[Scenario]:
|
|
66
|
+
"""
|
|
67
|
+
Get all scenarios suitable for beginners.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List[Scenario]: Beginner-friendly scenarios
|
|
71
|
+
"""
|
|
72
|
+
return [
|
|
73
|
+
scenario for scenario in self._scenarios.values()
|
|
74
|
+
if scenario.difficulty == "beginner"
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
def create_simple_project(self, project_type: ProjectType) -> ProjectTemplate:
|
|
78
|
+
"""
|
|
79
|
+
Create a simple project template with step-by-step instructions.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
project_type: Type of project to create
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
ProjectTemplate: Project template with instructions
|
|
86
|
+
"""
|
|
87
|
+
project_id = f"{project_type.value}_project"
|
|
88
|
+
if project_id in self._projects:
|
|
89
|
+
return self._projects[project_id]
|
|
90
|
+
|
|
91
|
+
# If project doesn't exist, create a basic template
|
|
92
|
+
from .models import ProjectStep
|
|
93
|
+
|
|
94
|
+
basic_step = ProjectStep(
|
|
95
|
+
step_number=1,
|
|
96
|
+
title=f"Create {project_type.value}",
|
|
97
|
+
description=f"Basic {project_type.value} implementation",
|
|
98
|
+
code_snippet="# Add your code here",
|
|
99
|
+
explanation="This is a placeholder project template"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return ProjectTemplate(
|
|
103
|
+
id=project_id,
|
|
104
|
+
title=f"Simple {project_type.value.replace('_', ' ').title()}",
|
|
105
|
+
description=f"A beginner-friendly {project_type.value} project",
|
|
106
|
+
project_type=project_type,
|
|
107
|
+
difficulty="beginner",
|
|
108
|
+
estimated_time=30,
|
|
109
|
+
steps=[basic_step],
|
|
110
|
+
final_code="# Complete implementation"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def explain_example_line_by_line(self, example: CodeExample) -> LineByLineExplanation:
|
|
114
|
+
"""
|
|
115
|
+
Generate line-by-line explanation for a code example.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
example: Code example to explain
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
LineByLineExplanation: Detailed line-by-line explanation
|
|
122
|
+
"""
|
|
123
|
+
from .models import LineExplanation
|
|
124
|
+
|
|
125
|
+
lines = example.code.strip().split('\n')
|
|
126
|
+
line_explanations = []
|
|
127
|
+
key_concepts = set()
|
|
128
|
+
|
|
129
|
+
for i, line in enumerate(lines, 1):
|
|
130
|
+
stripped_line = line.strip()
|
|
131
|
+
|
|
132
|
+
# Skip empty lines and comments (but still include them)
|
|
133
|
+
if not stripped_line or stripped_line.startswith('#'):
|
|
134
|
+
if stripped_line.startswith('#'):
|
|
135
|
+
explanation = "This is a comment that explains the code"
|
|
136
|
+
concepts = ["comments"]
|
|
137
|
+
else:
|
|
138
|
+
explanation = "Empty line for readability"
|
|
139
|
+
concepts = []
|
|
140
|
+
else:
|
|
141
|
+
explanation, concepts = self._analyze_code_line(stripped_line)
|
|
142
|
+
|
|
143
|
+
line_explanations.append(LineExplanation(
|
|
144
|
+
line_number=i,
|
|
145
|
+
code=line,
|
|
146
|
+
explanation=explanation,
|
|
147
|
+
concepts=concepts
|
|
148
|
+
))
|
|
149
|
+
|
|
150
|
+
key_concepts.update(concepts)
|
|
151
|
+
|
|
152
|
+
# Generate summary based on key concepts
|
|
153
|
+
summary = self._generate_explanation_summary(example, list(key_concepts))
|
|
154
|
+
|
|
155
|
+
return LineByLineExplanation(
|
|
156
|
+
example_id=example.id,
|
|
157
|
+
title=f"Line-by-line: {example.title}",
|
|
158
|
+
lines=line_explanations,
|
|
159
|
+
summary=summary,
|
|
160
|
+
key_concepts=list(key_concepts)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def _analyze_code_line(self, line: str) -> tuple[str, List[str]]:
|
|
164
|
+
"""
|
|
165
|
+
Analyze a single line of code and generate explanation.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
line: Code line to analyze
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
tuple: (explanation, concepts)
|
|
172
|
+
"""
|
|
173
|
+
concepts = []
|
|
174
|
+
|
|
175
|
+
# Variable assignment
|
|
176
|
+
if '=' in line and not any(op in line for op in ['==', '!=', '<=', '>=']):
|
|
177
|
+
if line.count('=') == 1 and '=' not in line.replace('=', ''):
|
|
178
|
+
concepts.append("variable assignment")
|
|
179
|
+
var_name = line.split('=')[0].strip()
|
|
180
|
+
if '[' in line and ']' in line:
|
|
181
|
+
concepts.append("lists")
|
|
182
|
+
if line.count('[') == 1 and line.count(']') == 1 and not line.endswith(']'):
|
|
183
|
+
# This is indexing, not list creation
|
|
184
|
+
concepts.extend(["indexing", "list access"])
|
|
185
|
+
explanation = f"Accesses an element from a list using indexing and assigns it to '{var_name}'"
|
|
186
|
+
else:
|
|
187
|
+
explanation = f"Creates a list and assigns it to variable '{var_name}'"
|
|
188
|
+
elif '{' in line and '}' in line:
|
|
189
|
+
concepts.append("dictionaries")
|
|
190
|
+
explanation = f"Creates a dictionary and assigns it to variable '{var_name}'"
|
|
191
|
+
elif 'input(' in line:
|
|
192
|
+
concepts.append("user input")
|
|
193
|
+
explanation = f"Gets input from user and stores it in variable '{var_name}'"
|
|
194
|
+
elif '(' in line and ')' in line:
|
|
195
|
+
concepts.append("function calls")
|
|
196
|
+
explanation = f"Calls a function and assigns the result to '{var_name}'"
|
|
197
|
+
else:
|
|
198
|
+
explanation = f"Assigns a value to variable '{var_name}'"
|
|
199
|
+
|
|
200
|
+
# Function definitions
|
|
201
|
+
elif line.startswith('def '):
|
|
202
|
+
concepts.extend(["functions", "function definition"])
|
|
203
|
+
func_name = line.split('(')[0].replace('def ', '').strip()
|
|
204
|
+
explanation = f"Defines a function named '{func_name}'"
|
|
205
|
+
|
|
206
|
+
# Function calls
|
|
207
|
+
elif '(' in line and ')' in line and not line.startswith('def'):
|
|
208
|
+
concepts.append("function calls")
|
|
209
|
+
if 'print(' in line:
|
|
210
|
+
concepts.append("output")
|
|
211
|
+
explanation = "Prints output to the console"
|
|
212
|
+
elif 'input(' in line:
|
|
213
|
+
concepts.append("user input")
|
|
214
|
+
explanation = "Gets input from the user"
|
|
215
|
+
elif '.append(' in line:
|
|
216
|
+
concepts.extend(["lists", "list methods"])
|
|
217
|
+
explanation = "Adds an item to the end of a list"
|
|
218
|
+
elif '.extend(' in line:
|
|
219
|
+
concepts.extend(["lists", "list methods"])
|
|
220
|
+
explanation = "Adds multiple items to the end of a list"
|
|
221
|
+
elif '.get(' in line:
|
|
222
|
+
concepts.extend(["dictionaries", "dict methods"])
|
|
223
|
+
explanation = "Safely gets a value from a dictionary"
|
|
224
|
+
elif 'int(' in line or 'float(' in line or 'str(' in line:
|
|
225
|
+
concepts.append("type conversion")
|
|
226
|
+
explanation = "Converts a value to a different data type"
|
|
227
|
+
else:
|
|
228
|
+
explanation = "Calls a function to perform an operation"
|
|
229
|
+
|
|
230
|
+
# Control structures
|
|
231
|
+
elif line.startswith('if '):
|
|
232
|
+
concepts.extend(["conditionals", "if statements"])
|
|
233
|
+
explanation = "Checks a condition and executes code if it's true"
|
|
234
|
+
elif line.startswith('elif '):
|
|
235
|
+
concepts.extend(["conditionals", "elif statements"])
|
|
236
|
+
explanation = "Checks an alternative condition"
|
|
237
|
+
elif line.startswith('else:'):
|
|
238
|
+
concepts.extend(["conditionals", "else statements"])
|
|
239
|
+
explanation = "Executes when no previous conditions were true"
|
|
240
|
+
elif line.startswith('while '):
|
|
241
|
+
concepts.extend(["loops", "while loops"])
|
|
242
|
+
explanation = "Repeats code while a condition is true"
|
|
243
|
+
elif line.startswith('for '):
|
|
244
|
+
concepts.extend(["loops", "for loops"])
|
|
245
|
+
explanation = "Repeats code for each item in a sequence"
|
|
246
|
+
|
|
247
|
+
# Exception handling
|
|
248
|
+
elif line.startswith('try:'):
|
|
249
|
+
concepts.extend(["error handling", "try-except"])
|
|
250
|
+
explanation = "Starts a block that might cause an error"
|
|
251
|
+
elif line.startswith('except'):
|
|
252
|
+
concepts.extend(["error handling", "try-except"])
|
|
253
|
+
explanation = "Handles errors that occur in the try block"
|
|
254
|
+
|
|
255
|
+
# Return statements
|
|
256
|
+
elif line.startswith('return '):
|
|
257
|
+
concepts.extend(["functions", "return statements"])
|
|
258
|
+
explanation = "Returns a value from the function"
|
|
259
|
+
|
|
260
|
+
# Import statements
|
|
261
|
+
elif line.startswith('import ') or line.startswith('from '):
|
|
262
|
+
concepts.append("imports")
|
|
263
|
+
explanation = "Imports code from another module"
|
|
264
|
+
|
|
265
|
+
# Default case
|
|
266
|
+
else:
|
|
267
|
+
explanation = "Executes a Python statement"
|
|
268
|
+
|
|
269
|
+
return explanation, concepts
|
|
270
|
+
|
|
271
|
+
def _generate_explanation_summary(self, example: CodeExample, key_concepts: List[str]) -> str:
|
|
272
|
+
"""
|
|
273
|
+
Generate a summary of the code explanation.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
example: The code example
|
|
277
|
+
key_concepts: List of key concepts found in the code
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
str: Summary explanation
|
|
281
|
+
"""
|
|
282
|
+
concept_descriptions = {
|
|
283
|
+
"variable assignment": "storing values in variables",
|
|
284
|
+
"lists": "working with ordered collections",
|
|
285
|
+
"dictionaries": "using key-value data structures",
|
|
286
|
+
"functions": "defining and calling reusable code blocks",
|
|
287
|
+
"conditionals": "making decisions with if/else statements",
|
|
288
|
+
"loops": "repeating code execution",
|
|
289
|
+
"user input": "getting data from users",
|
|
290
|
+
"error handling": "managing potential errors gracefully",
|
|
291
|
+
"output": "displaying results to users"
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if not key_concepts:
|
|
295
|
+
return f"This example demonstrates basic Python syntax: {example.description}"
|
|
296
|
+
|
|
297
|
+
# Get descriptions for found concepts
|
|
298
|
+
found_descriptions = []
|
|
299
|
+
for concept in key_concepts:
|
|
300
|
+
if concept in concept_descriptions:
|
|
301
|
+
found_descriptions.append(concept_descriptions[concept])
|
|
302
|
+
|
|
303
|
+
if found_descriptions:
|
|
304
|
+
concepts_text = ", ".join(found_descriptions[:-1])
|
|
305
|
+
if len(found_descriptions) > 1:
|
|
306
|
+
concepts_text += f", and {found_descriptions[-1]}"
|
|
307
|
+
else:
|
|
308
|
+
concepts_text = found_descriptions[0]
|
|
309
|
+
|
|
310
|
+
return f"This example demonstrates {concepts_text}. {example.description}"
|
|
311
|
+
else:
|
|
312
|
+
return f"This example shows various Python programming concepts. {example.description}"
|
|
313
|
+
|
|
314
|
+
def break_down_complex_concept(self, concept: str, context: str = "") -> List[str]:
|
|
315
|
+
"""
|
|
316
|
+
Break down complex programming concepts into simple steps.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
concept: The complex concept to break down
|
|
320
|
+
context: Additional context about how the concept is used
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List[str]: List of simple explanation steps
|
|
324
|
+
"""
|
|
325
|
+
concept_breakdowns = {
|
|
326
|
+
"list comprehension": [
|
|
327
|
+
"List comprehension is a concise way to create lists",
|
|
328
|
+
"It follows the pattern: [expression for item in iterable]",
|
|
329
|
+
"The expression is applied to each item in the iterable",
|
|
330
|
+
"The results are collected into a new list",
|
|
331
|
+
"It's equivalent to using a for loop but more compact"
|
|
332
|
+
],
|
|
333
|
+
|
|
334
|
+
"dictionary comprehension": [
|
|
335
|
+
"Dictionary comprehension creates dictionaries in one line",
|
|
336
|
+
"It follows the pattern: {key: value for item in iterable}",
|
|
337
|
+
"Each item in the iterable generates a key-value pair",
|
|
338
|
+
"The result is a new dictionary with all the pairs",
|
|
339
|
+
"It's more efficient than using loops to build dictionaries"
|
|
340
|
+
],
|
|
341
|
+
|
|
342
|
+
"exception handling": [
|
|
343
|
+
"Exception handling prevents programs from crashing",
|
|
344
|
+
"Use 'try:' to mark code that might cause an error",
|
|
345
|
+
"Use 'except:' to specify what to do if an error occurs",
|
|
346
|
+
"The program continues running after handling the error",
|
|
347
|
+
"Always handle specific exceptions when possible"
|
|
348
|
+
],
|
|
349
|
+
|
|
350
|
+
"function parameters": [
|
|
351
|
+
"Functions can accept input values called parameters",
|
|
352
|
+
"Parameters are defined in parentheses after the function name",
|
|
353
|
+
"When calling the function, you provide arguments for each parameter",
|
|
354
|
+
"The function uses these values to perform its task",
|
|
355
|
+
"Parameters make functions flexible and reusable"
|
|
356
|
+
],
|
|
357
|
+
|
|
358
|
+
"loops with conditions": [
|
|
359
|
+
"Loops can include conditions to control execution",
|
|
360
|
+
"Use 'if' statements inside loops to check conditions",
|
|
361
|
+
"Use 'continue' to skip the rest of the current iteration",
|
|
362
|
+
"Use 'break' to exit the loop completely",
|
|
363
|
+
"This allows for more complex loop behavior"
|
|
364
|
+
],
|
|
365
|
+
|
|
366
|
+
"nested data structures": [
|
|
367
|
+
"Data structures can contain other data structures",
|
|
368
|
+
"Lists can contain other lists (nested lists)",
|
|
369
|
+
"Dictionaries can contain lists or other dictionaries",
|
|
370
|
+
"Access nested elements using multiple brackets or keys",
|
|
371
|
+
"This allows for organizing complex data hierarchically"
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
# Return breakdown if available, otherwise create a generic one
|
|
376
|
+
if concept.lower() in concept_breakdowns:
|
|
377
|
+
return concept_breakdowns[concept.lower()]
|
|
378
|
+
|
|
379
|
+
# Generic breakdown for unknown concepts
|
|
380
|
+
return [
|
|
381
|
+
f"The concept '{concept}' is an important programming idea",
|
|
382
|
+
f"It helps solve specific problems in your code",
|
|
383
|
+
f"Understanding {concept} will make you a better programmer",
|
|
384
|
+
f"Practice using {concept} in different situations",
|
|
385
|
+
f"Look for examples of {concept} in real code"
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
def get_concept_prerequisites(self, concept: str) -> List[str]:
|
|
389
|
+
"""
|
|
390
|
+
Get the prerequisites needed to understand a concept.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
concept: The concept to check prerequisites for
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
List[str]: List of prerequisite concepts
|
|
397
|
+
"""
|
|
398
|
+
prerequisites_map = {
|
|
399
|
+
"list comprehension": ["lists", "for loops", "expressions"],
|
|
400
|
+
"dictionary comprehension": ["dictionaries", "for loops", "key-value pairs"],
|
|
401
|
+
"exception handling": ["functions", "conditionals"],
|
|
402
|
+
"nested loops": ["for loops", "while loops", "indentation"],
|
|
403
|
+
"function parameters": ["functions", "variables"],
|
|
404
|
+
"lambda functions": ["functions", "expressions"],
|
|
405
|
+
"file operations": ["strings", "exception handling"],
|
|
406
|
+
"class methods": ["classes", "functions", "self parameter"]
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return prerequisites_map.get(concept.lower(), [])
|
|
410
|
+
|
|
411
|
+
def add_example(self, example: CodeExample) -> None:
|
|
412
|
+
"""
|
|
413
|
+
Add a new example to the repository.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
example: Code example to add
|
|
417
|
+
"""
|
|
418
|
+
self._examples[example.id] = example
|
|
419
|
+
|
|
420
|
+
def search_examples(self, query: str, category: Optional[ExampleCategory] = None) -> List[CodeExample]:
|
|
421
|
+
"""
|
|
422
|
+
Search for examples matching a query.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
query: Search query
|
|
426
|
+
category: Optional category filter
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
List[CodeExample]: Matching examples
|
|
430
|
+
"""
|
|
431
|
+
query_lower = query.lower()
|
|
432
|
+
results = []
|
|
433
|
+
|
|
434
|
+
for example in self._examples.values():
|
|
435
|
+
# Filter by category if specified
|
|
436
|
+
if category and example.category != category:
|
|
437
|
+
continue
|
|
438
|
+
|
|
439
|
+
# Search in title, description, topics, and code
|
|
440
|
+
if (query_lower in example.title.lower() or
|
|
441
|
+
query_lower in example.description.lower() or
|
|
442
|
+
any(query_lower in topic.lower() for topic in example.topics) or
|
|
443
|
+
query_lower in example.code.lower() or
|
|
444
|
+
# Handle plural/singular variations
|
|
445
|
+
(query_lower.endswith('s') and query_lower[:-1] in example.title.lower()) or
|
|
446
|
+
(query_lower.endswith('s') and any(query_lower[:-1] in topic.lower() for topic in example.topics)) or
|
|
447
|
+
(not query_lower.endswith('s') and (query_lower + 's') in example.title.lower()) or
|
|
448
|
+
(not query_lower.endswith('s') and any((query_lower + 's') in topic.lower() for topic in example.topics))):
|
|
449
|
+
results.append(example)
|
|
450
|
+
|
|
451
|
+
return results
|
|
452
|
+
|
|
453
|
+
def _initialize_default_examples(self) -> None:
|
|
454
|
+
"""Initialize repository with default examples for beginners."""
|
|
455
|
+
from .models import ProjectStep
|
|
456
|
+
|
|
457
|
+
# List examples
|
|
458
|
+
list_examples = [
|
|
459
|
+
CodeExample(
|
|
460
|
+
id="list_basics",
|
|
461
|
+
title="Working with Lists - Basics",
|
|
462
|
+
description="Learn how to create, access, and modify lists",
|
|
463
|
+
code="""# Creating lists
|
|
464
|
+
fruits = ['apple', 'banana', 'orange']
|
|
465
|
+
numbers = [1, 2, 3, 4, 5]
|
|
466
|
+
|
|
467
|
+
# Accessing elements
|
|
468
|
+
first_fruit = fruits[0] # 'apple'
|
|
469
|
+
last_number = numbers[-1] # 5
|
|
470
|
+
|
|
471
|
+
# Adding elements
|
|
472
|
+
fruits.append('grape')
|
|
473
|
+
numbers.extend([6, 7])
|
|
474
|
+
|
|
475
|
+
# Modifying elements
|
|
476
|
+
fruits[1] = 'blueberry'
|
|
477
|
+
|
|
478
|
+
print(f"Fruits: {fruits}")
|
|
479
|
+
print(f"Numbers: {numbers}")""",
|
|
480
|
+
explanation="Lists are ordered collections that can store multiple items. You can access items by index, add new items, and modify existing ones.",
|
|
481
|
+
difficulty="beginner",
|
|
482
|
+
topics=["lists", "indexing", "append", "extend"],
|
|
483
|
+
prerequisites=[],
|
|
484
|
+
category=ExampleCategory.COLLECTIONS,
|
|
485
|
+
expected_output="Fruits: ['apple', 'blueberry', 'orange', 'grape']\nNumbers: [1, 2, 3, 4, 5, 6, 7]",
|
|
486
|
+
common_mistakes=["Using 1-based indexing instead of 0-based", "Forgetting that negative indices count from the end"]
|
|
487
|
+
),
|
|
488
|
+
|
|
489
|
+
CodeExample(
|
|
490
|
+
id="list_operations",
|
|
491
|
+
title="List Operations and Methods",
|
|
492
|
+
description="Common list operations like sorting, searching, and removing items",
|
|
493
|
+
code="""# Sample list
|
|
494
|
+
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
|
|
495
|
+
|
|
496
|
+
# Sorting
|
|
497
|
+
sorted_numbers = sorted(numbers) # Creates new list
|
|
498
|
+
numbers.sort() # Modifies original list
|
|
499
|
+
|
|
500
|
+
# Searching
|
|
501
|
+
if 5 in numbers:
|
|
502
|
+
position = numbers.index(5)
|
|
503
|
+
print(f"Found 5 at position {position}")
|
|
504
|
+
|
|
505
|
+
# Removing items
|
|
506
|
+
numbers.remove(1) # Removes first occurrence
|
|
507
|
+
last_item = numbers.pop() # Removes and returns last item
|
|
508
|
+
|
|
509
|
+
# List comprehension
|
|
510
|
+
squares = [x**2 for x in range(1, 6)]
|
|
511
|
+
|
|
512
|
+
print(f"Sorted: {sorted_numbers}")
|
|
513
|
+
print(f"Modified: {numbers}")
|
|
514
|
+
print(f"Squares: {squares}")""",
|
|
515
|
+
explanation="Lists have many built-in methods for common operations. Understanding the difference between methods that modify the list and those that return new lists is important.",
|
|
516
|
+
difficulty="beginner",
|
|
517
|
+
topics=["lists", "sorting", "searching", "list comprehension"],
|
|
518
|
+
prerequisites=["list_basics"],
|
|
519
|
+
category=ExampleCategory.COLLECTIONS,
|
|
520
|
+
expected_output="Found 5 at position 4\nSorted: [1, 1, 2, 3, 4, 5, 6, 9]\nModified: [1, 2, 3, 4, 5, 9]\nSquares: [1, 4, 9, 16, 25]",
|
|
521
|
+
common_mistakes=["Confusing sort() and sorted()", "Using remove() when item might not exist"]
|
|
522
|
+
)
|
|
523
|
+
]
|
|
524
|
+
|
|
525
|
+
# Dictionary examples
|
|
526
|
+
dict_examples = [
|
|
527
|
+
CodeExample(
|
|
528
|
+
id="dict_basics",
|
|
529
|
+
title="Working with Dictionaries - Basics",
|
|
530
|
+
description="Learn how to create, access, and modify dictionaries",
|
|
531
|
+
code="""# Creating dictionaries
|
|
532
|
+
student = {
|
|
533
|
+
'name': 'Alice',
|
|
534
|
+
'age': 20,
|
|
535
|
+
'grade': 'A',
|
|
536
|
+
'courses': ['Math', 'Physics']
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
# Accessing values
|
|
540
|
+
name = student['name']
|
|
541
|
+
age = student.get('age', 0) # Safe access with default
|
|
542
|
+
|
|
543
|
+
# Adding/modifying values
|
|
544
|
+
student['email'] = 'alice@example.com'
|
|
545
|
+
student['age'] = 21
|
|
546
|
+
|
|
547
|
+
# Checking if key exists
|
|
548
|
+
if 'grade' in student:
|
|
549
|
+
print(f"{name} has grade: {student['grade']}")
|
|
550
|
+
|
|
551
|
+
# Getting all keys, values, items
|
|
552
|
+
print(f"Keys: {list(student.keys())}")
|
|
553
|
+
print(f"Values: {list(student.values())}")""",
|
|
554
|
+
explanation="Dictionaries store key-value pairs and provide fast lookup by key. Use get() for safe access to avoid KeyError.",
|
|
555
|
+
difficulty="beginner",
|
|
556
|
+
topics=["dictionaries", "dictionary", "key-value pairs", "get method"],
|
|
557
|
+
prerequisites=[],
|
|
558
|
+
category=ExampleCategory.COLLECTIONS,
|
|
559
|
+
expected_output="Alice has grade: A\nKeys: ['name', 'age', 'grade', 'courses', 'email']\nValues: ['Alice', 21, 'A', ['Math', 'Physics'], 'alice@example.com']",
|
|
560
|
+
common_mistakes=["Using [] instead of get() for optional keys", "Trying to access non-existent keys"]
|
|
561
|
+
)
|
|
562
|
+
]
|
|
563
|
+
|
|
564
|
+
# User input examples
|
|
565
|
+
input_examples = [
|
|
566
|
+
CodeExample(
|
|
567
|
+
id="safe_input_basics",
|
|
568
|
+
title="Safe User Input - Basics",
|
|
569
|
+
description="Learn how to safely get and validate user input",
|
|
570
|
+
code="""# Getting basic input
|
|
571
|
+
name = input("Enter your name: ").strip()
|
|
572
|
+
|
|
573
|
+
# Getting and validating numeric input
|
|
574
|
+
while True:
|
|
575
|
+
try:
|
|
576
|
+
age = int(input("Enter your age: "))
|
|
577
|
+
if age < 0:
|
|
578
|
+
print("Age cannot be negative. Please try again.")
|
|
579
|
+
continue
|
|
580
|
+
break
|
|
581
|
+
except ValueError:
|
|
582
|
+
print("Please enter a valid number.")
|
|
583
|
+
|
|
584
|
+
# Getting yes/no input
|
|
585
|
+
while True:
|
|
586
|
+
choice = input("Do you want to continue? (y/n): ").lower().strip()
|
|
587
|
+
if choice in ['y', 'yes']:
|
|
588
|
+
print("Continuing...")
|
|
589
|
+
break
|
|
590
|
+
elif choice in ['n', 'no']:
|
|
591
|
+
print("Stopping...")
|
|
592
|
+
break
|
|
593
|
+
else:
|
|
594
|
+
print("Please enter 'y' or 'n'.")
|
|
595
|
+
|
|
596
|
+
print(f"Hello {name}, you are {age} years old.")""",
|
|
597
|
+
explanation="Always validate user input to prevent errors. Use try-except for type conversion and loops for validation.",
|
|
598
|
+
difficulty="beginner",
|
|
599
|
+
topics=["input", "validation", "try-except", "loops"],
|
|
600
|
+
prerequisites=[],
|
|
601
|
+
category=ExampleCategory.USER_INPUT,
|
|
602
|
+
expected_output="# Output depends on user input",
|
|
603
|
+
common_mistakes=["Not validating input", "Not handling ValueError", "Not stripping whitespace"]
|
|
604
|
+
)
|
|
605
|
+
]
|
|
606
|
+
|
|
607
|
+
# Add all examples to repository
|
|
608
|
+
for example in list_examples + dict_examples + input_examples:
|
|
609
|
+
self._examples[example.id] = example
|
|
610
|
+
|
|
611
|
+
def _initialize_default_scenarios(self) -> None:
|
|
612
|
+
"""Initialize repository with default learning scenarios."""
|
|
613
|
+
# Collections scenario
|
|
614
|
+
collections_scenario = Scenario(
|
|
615
|
+
id="collections_basics",
|
|
616
|
+
title="Python Collections Fundamentals",
|
|
617
|
+
description="Learn the basics of working with lists and dictionaries",
|
|
618
|
+
examples=[self._examples["list_basics"], self._examples["dict_basics"]],
|
|
619
|
+
learning_objectives=[
|
|
620
|
+
"Understand how to create and use lists",
|
|
621
|
+
"Learn dictionary key-value operations",
|
|
622
|
+
"Practice safe data access patterns"
|
|
623
|
+
],
|
|
624
|
+
difficulty="beginner",
|
|
625
|
+
estimated_time=45
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
self._scenarios[collections_scenario.id] = collections_scenario
|
|
629
|
+
|
|
630
|
+
def _initialize_default_projects(self) -> None:
|
|
631
|
+
"""Initialize repository with default project templates."""
|
|
632
|
+
from .models import ProjectStep
|
|
633
|
+
|
|
634
|
+
# Calculator project
|
|
635
|
+
calculator_steps = [
|
|
636
|
+
ProjectStep(
|
|
637
|
+
step_number=1,
|
|
638
|
+
title="Create basic calculator functions",
|
|
639
|
+
description="Define functions for basic arithmetic operations",
|
|
640
|
+
code_snippet="""def add(a, b):
|
|
641
|
+
return a + b
|
|
642
|
+
|
|
643
|
+
def subtract(a, b):
|
|
644
|
+
return a - b
|
|
645
|
+
|
|
646
|
+
def multiply(a, b):
|
|
647
|
+
return a * b
|
|
648
|
+
|
|
649
|
+
def divide(a, b):
|
|
650
|
+
if b == 0:
|
|
651
|
+
return "Error: Cannot divide by zero"
|
|
652
|
+
return a / b""",
|
|
653
|
+
explanation="Start by creating simple functions for each operation. Notice the error handling for division by zero.",
|
|
654
|
+
hints=["Remember to handle division by zero", "Keep functions simple and focused"]
|
|
655
|
+
),
|
|
656
|
+
|
|
657
|
+
ProjectStep(
|
|
658
|
+
step_number=2,
|
|
659
|
+
title="Create the main calculator loop",
|
|
660
|
+
description="Build the user interface and input handling",
|
|
661
|
+
code_snippet="""def calculator():
|
|
662
|
+
print("Simple Calculator")
|
|
663
|
+
print("Operations: +, -, *, /")
|
|
664
|
+
print("Type 'quit' to exit")
|
|
665
|
+
|
|
666
|
+
while True:
|
|
667
|
+
try:
|
|
668
|
+
# Get first number
|
|
669
|
+
first = input("Enter first number (or 'quit'): ")
|
|
670
|
+
if first.lower() == 'quit':
|
|
671
|
+
break
|
|
672
|
+
first = float(first)
|
|
673
|
+
|
|
674
|
+
# Get operation
|
|
675
|
+
operation = input("Enter operation (+, -, *, /): ")
|
|
676
|
+
if operation not in ['+', '-', '*', '/']:
|
|
677
|
+
print("Invalid operation!")
|
|
678
|
+
continue
|
|
679
|
+
|
|
680
|
+
# Get second number
|
|
681
|
+
second = float(input("Enter second number: "))
|
|
682
|
+
|
|
683
|
+
# Calculate result
|
|
684
|
+
if operation == '+':
|
|
685
|
+
result = add(first, second)
|
|
686
|
+
elif operation == '-':
|
|
687
|
+
result = subtract(first, second)
|
|
688
|
+
elif operation == '*':
|
|
689
|
+
result = multiply(first, second)
|
|
690
|
+
elif operation == '/':
|
|
691
|
+
result = divide(first, second)
|
|
692
|
+
|
|
693
|
+
print(f"Result: {result}")
|
|
694
|
+
|
|
695
|
+
except ValueError:
|
|
696
|
+
print("Please enter valid numbers!")
|
|
697
|
+
|
|
698
|
+
# Run the calculator
|
|
699
|
+
calculator()""",
|
|
700
|
+
explanation="The main loop handles user input, validates operations, and calls the appropriate function.",
|
|
701
|
+
hints=["Use try-except for input validation", "Provide clear error messages", "Allow users to exit gracefully"]
|
|
702
|
+
)
|
|
703
|
+
]
|
|
704
|
+
|
|
705
|
+
calculator_project = ProjectTemplate(
|
|
706
|
+
id="calculator_project",
|
|
707
|
+
title="Simple Calculator",
|
|
708
|
+
description="Build a basic calculator that performs arithmetic operations",
|
|
709
|
+
project_type=ProjectType.CALCULATOR,
|
|
710
|
+
difficulty="beginner",
|
|
711
|
+
estimated_time=60,
|
|
712
|
+
steps=calculator_steps,
|
|
713
|
+
final_code="""def add(a, b):
|
|
714
|
+
return a + b
|
|
715
|
+
|
|
716
|
+
def subtract(a, b):
|
|
717
|
+
return a - b
|
|
718
|
+
|
|
719
|
+
def multiply(a, b):
|
|
720
|
+
return a * b
|
|
721
|
+
|
|
722
|
+
def divide(a, b):
|
|
723
|
+
if b == 0:
|
|
724
|
+
return "Error: Cannot divide by zero"
|
|
725
|
+
return a / b
|
|
726
|
+
|
|
727
|
+
def calculator():
|
|
728
|
+
print("Simple Calculator")
|
|
729
|
+
print("Operations: +, -, *, /")
|
|
730
|
+
print("Type 'quit' to exit")
|
|
731
|
+
|
|
732
|
+
while True:
|
|
733
|
+
try:
|
|
734
|
+
first = input("Enter first number (or 'quit'): ")
|
|
735
|
+
if first.lower() == 'quit':
|
|
736
|
+
break
|
|
737
|
+
first = float(first)
|
|
738
|
+
|
|
739
|
+
operation = input("Enter operation (+, -, *, /): ")
|
|
740
|
+
if operation not in ['+', '-', '*', '/']:
|
|
741
|
+
print("Invalid operation!")
|
|
742
|
+
continue
|
|
743
|
+
|
|
744
|
+
second = float(input("Enter second number: "))
|
|
745
|
+
|
|
746
|
+
if operation == '+':
|
|
747
|
+
result = add(first, second)
|
|
748
|
+
elif operation == '-':
|
|
749
|
+
result = subtract(first, second)
|
|
750
|
+
elif operation == '*':
|
|
751
|
+
result = multiply(first, second)
|
|
752
|
+
elif operation == '/':
|
|
753
|
+
result = divide(first, second)
|
|
754
|
+
|
|
755
|
+
print(f"Result: {result}")
|
|
756
|
+
|
|
757
|
+
except ValueError:
|
|
758
|
+
print("Please enter valid numbers!")
|
|
759
|
+
|
|
760
|
+
if __name__ == "__main__":
|
|
761
|
+
calculator()""",
|
|
762
|
+
extensions=[
|
|
763
|
+
"Add more operations (power, square root, etc.)",
|
|
764
|
+
"Add memory functions (store/recall)",
|
|
765
|
+
"Create a GUI version using tkinter",
|
|
766
|
+
"Add calculation history"
|
|
767
|
+
]
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
self._projects[calculator_project.id] = calculator_project
|