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.
Files changed (81) hide show
  1. fishertools/__init__.py +82 -0
  2. fishertools/config/__init__.py +24 -0
  3. fishertools/config/manager.py +247 -0
  4. fishertools/config/models.py +96 -0
  5. fishertools/config/parser.py +265 -0
  6. fishertools/decorators.py +93 -0
  7. fishertools/documentation/__init__.py +38 -0
  8. fishertools/documentation/api.py +242 -0
  9. fishertools/documentation/generator.py +502 -0
  10. fishertools/documentation/models.py +126 -0
  11. fishertools/documentation/visual.py +583 -0
  12. fishertools/errors/__init__.py +29 -0
  13. fishertools/errors/exceptions.py +191 -0
  14. fishertools/errors/explainer.py +303 -0
  15. fishertools/errors/formatters.py +386 -0
  16. fishertools/errors/models.py +228 -0
  17. fishertools/errors/patterns.py +119 -0
  18. fishertools/errors/recovery.py +467 -0
  19. fishertools/examples/__init__.py +22 -0
  20. fishertools/examples/models.py +118 -0
  21. fishertools/examples/repository.py +770 -0
  22. fishertools/helpers.py +116 -0
  23. fishertools/integration.py +451 -0
  24. fishertools/learn/__init__.py +18 -0
  25. fishertools/learn/examples.py +550 -0
  26. fishertools/learn/tips.py +281 -0
  27. fishertools/learning/__init__.py +32 -0
  28. fishertools/learning/core.py +349 -0
  29. fishertools/learning/models.py +112 -0
  30. fishertools/learning/progress.py +314 -0
  31. fishertools/learning/session.py +500 -0
  32. fishertools/learning/tutorial.py +626 -0
  33. fishertools/legacy/__init__.py +76 -0
  34. fishertools/legacy/deprecated.py +261 -0
  35. fishertools/legacy/deprecation.py +149 -0
  36. fishertools/safe/__init__.py +16 -0
  37. fishertools/safe/collections.py +242 -0
  38. fishertools/safe/files.py +240 -0
  39. fishertools/safe/strings.py +15 -0
  40. fishertools/utils.py +57 -0
  41. fishertools-0.2.1.dist-info/METADATA +256 -0
  42. fishertools-0.2.1.dist-info/RECORD +81 -0
  43. fishertools-0.2.1.dist-info/WHEEL +5 -0
  44. fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
  45. fishertools-0.2.1.dist-info/top_level.txt +2 -0
  46. tests/__init__.py +6 -0
  47. tests/conftest.py +25 -0
  48. tests/test_config/__init__.py +3 -0
  49. tests/test_config/test_basic_config.py +57 -0
  50. tests/test_config/test_config_error_handling.py +287 -0
  51. tests/test_config/test_config_properties.py +435 -0
  52. tests/test_documentation/__init__.py +3 -0
  53. tests/test_documentation/test_documentation_properties.py +253 -0
  54. tests/test_documentation/test_visual_documentation_properties.py +444 -0
  55. tests/test_errors/__init__.py +3 -0
  56. tests/test_errors/test_api.py +301 -0
  57. tests/test_errors/test_error_handling.py +354 -0
  58. tests/test_errors/test_explainer.py +173 -0
  59. tests/test_errors/test_formatters.py +338 -0
  60. tests/test_errors/test_models.py +248 -0
  61. tests/test_errors/test_patterns.py +270 -0
  62. tests/test_examples/__init__.py +3 -0
  63. tests/test_examples/test_example_repository_properties.py +204 -0
  64. tests/test_examples/test_specific_examples.py +303 -0
  65. tests/test_integration.py +298 -0
  66. tests/test_integration_enhancements.py +462 -0
  67. tests/test_learn/__init__.py +3 -0
  68. tests/test_learn/test_examples.py +221 -0
  69. tests/test_learn/test_tips.py +285 -0
  70. tests/test_learning/__init__.py +3 -0
  71. tests/test_learning/test_interactive_learning_properties.py +337 -0
  72. tests/test_learning/test_learning_system_properties.py +194 -0
  73. tests/test_learning/test_progress_tracking_properties.py +279 -0
  74. tests/test_legacy/__init__.py +3 -0
  75. tests/test_legacy/test_backward_compatibility.py +236 -0
  76. tests/test_legacy/test_deprecation_warnings.py +208 -0
  77. tests/test_safe/__init__.py +3 -0
  78. tests/test_safe/test_collections_properties.py +189 -0
  79. tests/test_safe/test_files.py +104 -0
  80. tests/test_structure.py +58 -0
  81. tests/test_structure_enhancements.py +115 -0
@@ -0,0 +1,281 @@
1
+ """
2
+ Best practices and tips for learning Python.
3
+
4
+ This module contains functions to show Python best practices
5
+ for common programming concepts that beginners encounter.
6
+ """
7
+
8
+ from typing import Dict, List
9
+
10
+
11
+ # Database of best practices for different Python topics
12
+ BEST_PRACTICES: Dict[str, Dict[str, str]] = {
13
+ "variables": {
14
+ "title": "Переменные в Python",
15
+ "practices": """
16
+ 🔹 Используйте описательные имена переменных:
17
+ ❌ Плохо: x = 25
18
+ ✅ Хорошо: age = 25
19
+
20
+ 🔹 Используйте snake_case для имен переменных:
21
+ ❌ Плохо: firstName = "Иван"
22
+ ✅ Хорошо: first_name = "Иван"
23
+
24
+ 🔹 Избегайте зарезервированных слов:
25
+ ❌ Плохо: list = [1, 2, 3]
26
+ ✅ Хорошо: numbers = [1, 2, 3]
27
+
28
+ 🔹 Используйте константы для неизменяемых значений:
29
+ ✅ MAX_ATTEMPTS = 3
30
+ ✅ PI = 3.14159
31
+ """,
32
+ "example": """
33
+ # Хороший пример использования переменных
34
+ user_name = "Анна"
35
+ user_age = 28
36
+ is_active = True
37
+ MAX_LOGIN_ATTEMPTS = 3
38
+
39
+ print(f"Пользователь {user_name}, возраст {user_age}")
40
+ """
41
+ },
42
+
43
+ "functions": {
44
+ "title": "Функции в Python",
45
+ "practices": """
46
+ 🔹 Используйте docstring для документации:
47
+ def calculate_area(radius):
48
+ \"\"\"Вычисляет площадь круга по радиусу.\"\"\"
49
+ return 3.14159 * radius ** 2
50
+
51
+ 🔹 Используйте type hints для ясности:
52
+ def greet(name: str) -> str:
53
+ return f"Привет, {name}!"
54
+
55
+ 🔹 Функции должны делать одну вещь хорошо:
56
+ ❌ Плохо: функция, которая и читает файл, и обрабатывает данные
57
+ ✅ Хорошо: отдельные функции для чтения и обработки
58
+
59
+ 🔹 Используйте значения по умолчанию разумно:
60
+ def create_user(name: str, role: str = "user"):
61
+ return {"name": name, "role": role}
62
+ """,
63
+ "example": """
64
+ def calculate_discount(price: float, discount_percent: float = 10.0) -> float:
65
+ \"\"\"
66
+ Вычисляет цену со скидкой.
67
+
68
+ Args:
69
+ price: Исходная цена товара
70
+ discount_percent: Процент скидки (по умолчанию 10%)
71
+
72
+ Returns:
73
+ Цена со скидкой
74
+ \"\"\"
75
+ if price < 0:
76
+ raise ValueError("Цена не может быть отрицательной")
77
+
78
+ discount_amount = price * (discount_percent / 100)
79
+ return price - discount_amount
80
+
81
+ # Использование
82
+ final_price = calculate_discount(1000.0, 15.0)
83
+ print(f"Цена со скидкой: {final_price}")
84
+ """
85
+ },
86
+
87
+ "lists": {
88
+ "title": "Работа со списками",
89
+ "practices": """
90
+ 🔹 Используйте list comprehensions для простых операций:
91
+ ✅ squares = [x**2 for x in range(10)]
92
+ ❌ Избегайте сложных comprehensions
93
+
94
+ 🔹 Проверяйте границы при доступе к элементам:
95
+ if 0 <= index < len(my_list):
96
+ value = my_list[index]
97
+
98
+ 🔹 Используйте enumerate() для индекса и значения:
99
+ for i, item in enumerate(items):
100
+ print(f"{i}: {item}")
101
+
102
+ 🔹 Используйте методы списков эффективно:
103
+ ✅ items.append(new_item) # Добавить в конец
104
+ ✅ items.extend(other_list) # Добавить несколько элементов
105
+ """,
106
+ "example": """
107
+ # Хорошие практики работы со списками
108
+ fruits = ["яблоко", "банан", "апельсин"]
109
+
110
+ # Безопасный доступ к элементам
111
+ def get_fruit(fruits_list: list, index: int) -> str:
112
+ if 0 <= index < len(fruits_list):
113
+ return fruits_list[index]
114
+ return "Фрукт не найден"
115
+
116
+ # Использование enumerate
117
+ print("Список фруктов:")
118
+ for i, fruit in enumerate(fruits, 1):
119
+ print(f"{i}. {fruit}")
120
+
121
+ # List comprehension для преобразования
122
+ uppercase_fruits = [fruit.upper() for fruit in fruits]
123
+ print(f"Заглавными буквами: {uppercase_fruits}")
124
+ """
125
+ },
126
+
127
+ "dictionaries": {
128
+ "title": "Работа со словарями",
129
+ "practices": """
130
+ 🔹 Используйте get() для безопасного доступа:
131
+ ✅ value = my_dict.get("key", "default")
132
+ ❌ value = my_dict["key"] # Может вызвать KeyError
133
+
134
+ 🔹 Проверяйте наличие ключей:
135
+ if "key" in my_dict:
136
+ # Безопасно использовать my_dict["key"]
137
+
138
+ 🔹 Используйте items() для итерации:
139
+ for key, value in my_dict.items():
140
+ print(f"{key}: {value}")
141
+
142
+ 🔹 Используйте dict comprehensions:
143
+ ✅ squares = {x: x**2 for x in range(5)}
144
+ """,
145
+ "example": """
146
+ # Хорошие практики работы со словарями
147
+ student_grades = {
148
+ "Анна": 85,
149
+ "Борис": 92,
150
+ "Вера": 78
151
+ }
152
+
153
+ # Безопасный доступ к значениям
154
+ def get_grade(students: dict, name: str) -> str:
155
+ grade = students.get(name)
156
+ if grade is not None:
157
+ return f"Оценка {name}: {grade}"
158
+ return f"Студент {name} не найден"
159
+
160
+ # Итерация по словарю
161
+ print("Все оценки:")
162
+ for student, grade in student_grades.items():
163
+ status = "отлично" if grade >= 90 else "хорошо" if grade >= 80 else "удовлетворительно"
164
+ print(f"{student}: {grade} ({status})")
165
+
166
+ # Добавление нового студента
167
+ student_grades["Григорий"] = 88
168
+ """
169
+ },
170
+
171
+ "error_handling": {
172
+ "title": "Обработка ошибок",
173
+ "practices": """
174
+ 🔹 Используйте конкретные типы исключений:
175
+ ❌ except Exception: # Слишком общее
176
+ ✅ except ValueError: # Конкретное исключение
177
+
178
+ 🔹 Всегда обрабатывайте ошибки осмысленно:
179
+ try:
180
+ result = risky_operation()
181
+ except ValueError as e:
182
+ print(f"Ошибка значения: {e}")
183
+ return None
184
+
185
+ 🔹 Используйте finally для очистки ресурсов:
186
+ try:
187
+ file = open("data.txt")
188
+ # работа с файлом
189
+ finally:
190
+ file.close()
191
+
192
+ 🔹 Или лучше используйте контекстные менеджеры:
193
+ with open("data.txt") as file:
194
+ # файл автоматически закроется
195
+ """,
196
+ "example": """
197
+ def safe_divide(a: float, b: float) -> float:
198
+ \"\"\"
199
+ Безопасное деление с обработкой ошибок.
200
+ \"\"\"
201
+ try:
202
+ if b == 0:
203
+ raise ValueError("Деление на ноль невозможно")
204
+
205
+ result = a / b
206
+ return result
207
+
208
+ except TypeError:
209
+ print("Ошибка: аргументы должны быть числами")
210
+ return 0.0
211
+ except ValueError as e:
212
+ print(f"Ошибка значения: {e}")
213
+ return 0.0
214
+
215
+ # Использование
216
+ print(safe_divide(10, 2)) # 5.0
217
+ print(safe_divide(10, 0)) # Обработка деления на ноль
218
+ print(safe_divide("10", 2)) # Обработка неправильного типа
219
+ """
220
+ }
221
+ }
222
+
223
+
224
+ def show_best_practice(topic: str) -> None:
225
+ """
226
+ Show best practices for a specific Python topic.
227
+
228
+ Args:
229
+ topic: The Python topic to show best practices for.
230
+ Available topics: variables, functions, lists, dictionaries, error_handling
231
+
232
+ Displays formatted best practices with examples to the console.
233
+ """
234
+ topic_lower = topic.lower().strip()
235
+
236
+ if topic_lower not in BEST_PRACTICES:
237
+ available_topics = ", ".join(BEST_PRACTICES.keys())
238
+ print(f"❌ Тема '{topic}' не найдена.")
239
+ print(f"📚 Доступные темы: {available_topics}")
240
+ return
241
+
242
+ practice_data = BEST_PRACTICES[topic_lower]
243
+
244
+ print("=" * 60)
245
+ print(f"📖 {practice_data['title']}")
246
+ print("=" * 60)
247
+ print()
248
+ print("🎯 ЛУЧШИЕ ПРАКТИКИ:")
249
+ print(practice_data['practices'])
250
+ print()
251
+ print("💡 ПРИМЕР КОДА:")
252
+ print(practice_data['example'])
253
+ print("=" * 60)
254
+
255
+
256
+ def list_available_topics() -> List[str]:
257
+ """
258
+ Get a list of all available best practice topics.
259
+
260
+ Returns:
261
+ List of available topic names
262
+ """
263
+ return list(BEST_PRACTICES.keys())
264
+
265
+
266
+ def get_topic_summary(topic: str) -> str:
267
+ """
268
+ Get a brief summary of a best practice topic.
269
+
270
+ Args:
271
+ topic: The topic to get summary for
272
+
273
+ Returns:
274
+ Brief summary string or error message
275
+ """
276
+ topic_lower = topic.lower().strip()
277
+
278
+ if topic_lower not in BEST_PRACTICES:
279
+ return f"Тема '{topic}' не найдена"
280
+
281
+ return BEST_PRACTICES[topic_lower]['title']
@@ -0,0 +1,32 @@
1
+ """
2
+ Learning System Module
3
+
4
+ Provides comprehensive learning tools for Python beginners using fishertools.
5
+ Includes tutorial engine, progress tracking, and interactive learning sessions.
6
+ """
7
+
8
+ from .core import LearningSystem
9
+ from .tutorial import TutorialEngine
10
+ from .progress import ProgressSystem
11
+ from .session import InteractiveSessionManager
12
+ from .models import (
13
+ StepExplanation,
14
+ InteractiveExercise,
15
+ LearningProgress,
16
+ TutorialSession,
17
+ ValidationResult,
18
+ CodeContext
19
+ )
20
+
21
+ __all__ = [
22
+ "LearningSystem",
23
+ "TutorialEngine",
24
+ "ProgressSystem",
25
+ "InteractiveSessionManager",
26
+ "StepExplanation",
27
+ "InteractiveExercise",
28
+ "LearningProgress",
29
+ "TutorialSession",
30
+ "ValidationResult",
31
+ "CodeContext"
32
+ ]
@@ -0,0 +1,349 @@
1
+ """
2
+ Core Learning System implementation.
3
+ """
4
+
5
+ import ast
6
+ import uuid
7
+ from datetime import datetime
8
+ from typing import List, Optional, Dict, Set
9
+ from .models import (
10
+ TutorialSession, StepExplanation, DifficultyLevel,
11
+ CodeContext, LearningProgress, InteractiveExercise, ExerciseStatus
12
+ )
13
+
14
+
15
+ class LearningSystem:
16
+ """
17
+ Central component coordinating all learning activities.
18
+
19
+ Provides step-by-step explanations, tutorial management,
20
+ and progress tracking for Python beginners.
21
+ """
22
+
23
+ def __init__(self, config_path: Optional[str] = None):
24
+ """Initialize the learning system with optional configuration."""
25
+ self.config_path = config_path
26
+ self._tutorial_engine = None
27
+ self._progress_system = None
28
+ self._session_manager = None
29
+
30
+ # Topic relationships for suggesting related topics
31
+ self._topic_relationships = {
32
+ "variables": ["data_types", "operators", "input_output"],
33
+ "data_types": ["variables", "type_conversion", "strings"],
34
+ "lists": ["loops", "indexing", "list_methods", "collections"],
35
+ "dictionaries": ["lists", "collections", "key_value_pairs"],
36
+ "loops": ["lists", "conditionals", "iteration"],
37
+ "functions": ["parameters", "return_values", "scope"],
38
+ "conditionals": ["boolean_logic", "comparison_operators"],
39
+ "error_handling": ["exceptions", "try_except", "debugging"],
40
+ "file_operations": ["strings", "error_handling", "paths"],
41
+ "classes": ["objects", "methods", "inheritance"],
42
+ "modules": ["imports", "packages", "namespaces"]
43
+ }
44
+
45
+ # Content adaptation templates by level
46
+ self._level_adaptations = {
47
+ DifficultyLevel.BEGINNER: {
48
+ "vocabulary": "simple",
49
+ "examples": "basic",
50
+ "detail_level": "high",
51
+ "prerequisites": "minimal"
52
+ },
53
+ DifficultyLevel.INTERMEDIATE: {
54
+ "vocabulary": "technical",
55
+ "examples": "practical",
56
+ "detail_level": "medium",
57
+ "prerequisites": "some"
58
+ },
59
+ DifficultyLevel.ADVANCED: {
60
+ "vocabulary": "advanced",
61
+ "examples": "complex",
62
+ "detail_level": "low",
63
+ "prerequisites": "extensive"
64
+ }
65
+ }
66
+
67
+ def start_tutorial(self, topic: str, level: str = "beginner") -> TutorialSession:
68
+ """
69
+ Start a new tutorial session for the given topic and level.
70
+
71
+ Args:
72
+ topic: The topic to learn (e.g., "lists", "functions", "error_handling")
73
+ level: Difficulty level ("beginner", "intermediate", "advanced")
74
+
75
+ Returns:
76
+ TutorialSession: A new tutorial session object
77
+
78
+ Raises:
79
+ ValueError: If topic or level is invalid
80
+ """
81
+ # Validate level
82
+ try:
83
+ difficulty_level = DifficultyLevel(level)
84
+ except ValueError:
85
+ raise ValueError(f"Invalid level '{level}'. Must be one of: beginner, intermediate, advanced")
86
+
87
+ # Validate topic (basic validation - could be expanded)
88
+ if not topic or not isinstance(topic, str):
89
+ raise ValueError("Topic must be a non-empty string")
90
+
91
+ # Generate session ID
92
+ session_id = str(uuid.uuid4())
93
+
94
+ # Create basic exercises for the topic (simplified for now)
95
+ exercises = self._create_exercises_for_topic(topic, difficulty_level)
96
+
97
+ # Create tutorial session
98
+ session = TutorialSession(
99
+ session_id=session_id,
100
+ topic=topic,
101
+ level=difficulty_level,
102
+ start_time=datetime.now(),
103
+ exercises=exercises
104
+ )
105
+
106
+ return session
107
+
108
+ def get_step_by_step_explanation(self, code: str, context: Optional[CodeContext] = None) -> List[StepExplanation]:
109
+ """
110
+ Generate step-by-step explanation for the given code.
111
+
112
+ Args:
113
+ code: Python code to explain
114
+ context: Optional context information for better explanations
115
+
116
+ Returns:
117
+ List[StepExplanation]: Detailed explanations for each step
118
+ """
119
+ if not code or not isinstance(code, str):
120
+ raise ValueError("Code must be a non-empty string")
121
+
122
+ explanations = []
123
+
124
+ try:
125
+ # Parse the code into an AST
126
+ tree = ast.parse(code.strip())
127
+
128
+ # Generate explanations for each statement
129
+ for i, node in enumerate(ast.walk(tree)):
130
+ if isinstance(node, (ast.stmt, ast.expr)):
131
+ explanation = self._explain_ast_node(node, i + 1, context)
132
+ if explanation:
133
+ explanations.append(explanation)
134
+
135
+ except SyntaxError as e:
136
+ # If code has syntax errors, provide a basic explanation
137
+ explanations.append(StepExplanation(
138
+ step_number=1,
139
+ description=f"Syntax error in code: {str(e)}",
140
+ code_snippet=code,
141
+ related_concepts=["syntax", "debugging"]
142
+ ))
143
+
144
+ return explanations
145
+
146
+ def suggest_related_topics(self, current_topic: str) -> List[str]:
147
+ """
148
+ Suggest related topics based on the current topic.
149
+
150
+ Args:
151
+ current_topic: The topic currently being studied
152
+
153
+ Returns:
154
+ List[str]: List of related topic names
155
+ """
156
+ if not current_topic or not isinstance(current_topic, str):
157
+ return []
158
+
159
+ # Get direct relationships
160
+ related = self._topic_relationships.get(current_topic.lower(), [])
161
+
162
+ # Add topics that reference the current topic
163
+ for topic, relationships in self._topic_relationships.items():
164
+ if current_topic.lower() in relationships and topic not in related:
165
+ related.append(topic)
166
+
167
+ return related[:5] # Limit to 5 suggestions
168
+
169
+ def adapt_content_for_level(self, content: str, level: str) -> str:
170
+ """
171
+ Adapt content complexity for the specified level.
172
+
173
+ Args:
174
+ content: Original content to adapt
175
+ level: Target difficulty level
176
+
177
+ Returns:
178
+ str: Adapted content appropriate for the level
179
+ """
180
+ if not content or not isinstance(content, str):
181
+ return content
182
+
183
+ try:
184
+ difficulty_level = DifficultyLevel(level)
185
+ except ValueError:
186
+ return content # Return original if invalid level
187
+
188
+ adaptation = self._level_adaptations[difficulty_level]
189
+
190
+ # Apply adaptations based on level
191
+ if difficulty_level == DifficultyLevel.BEGINNER:
192
+ # Add more explanatory text and simpler vocabulary
193
+ adapted = self._simplify_vocabulary(content)
194
+ adapted = self._add_beginner_context(adapted)
195
+ elif difficulty_level == DifficultyLevel.INTERMEDIATE:
196
+ # Balance between detail and conciseness
197
+ adapted = self._add_practical_context(content)
198
+ else: # ADVANCED
199
+ # More concise, assume prior knowledge
200
+ adapted = self._make_concise(content)
201
+
202
+ return adapted
203
+
204
+ def track_progress(self, user_id: str, topic: str, completed: bool) -> None:
205
+ """
206
+ Track user progress for a specific topic.
207
+
208
+ Args:
209
+ user_id: Unique identifier for the user
210
+ topic: Topic that was studied
211
+ completed: Whether the topic was completed successfully
212
+ """
213
+ if not self._progress_system:
214
+ from .progress import ProgressSystem
215
+ self._progress_system = ProgressSystem()
216
+
217
+ self._progress_system.update_progress(user_id, topic, completed)
218
+
219
+ def get_user_progress(self, user_id: str) -> Optional[LearningProgress]:
220
+ """
221
+ Get current progress for a user.
222
+
223
+ Args:
224
+ user_id: Unique identifier for the user
225
+
226
+ Returns:
227
+ Optional[LearningProgress]: User's progress or None if not found
228
+ """
229
+ if not self._progress_system:
230
+ from .progress import ProgressSystem
231
+ self._progress_system = ProgressSystem()
232
+
233
+ return self._progress_system.get_progress(user_id)
234
+ def _create_exercises_for_topic(self, topic: str, level: DifficultyLevel) -> List[InteractiveExercise]:
235
+ """Create basic exercises for a given topic and level."""
236
+ exercises = []
237
+
238
+ # Basic exercise templates by topic
239
+ exercise_templates = {
240
+ "variables": {
241
+ "title": "Working with Variables",
242
+ "description": "Practice creating and using variables",
243
+ "starter_code": "# Create a variable called 'name' and assign your name to it\n",
244
+ "expected_output": "Variable assignment"
245
+ },
246
+ "lists": {
247
+ "title": "List Operations",
248
+ "description": "Practice creating and manipulating lists",
249
+ "starter_code": "# Create a list of your favorite colors\ncolors = []\n",
250
+ "expected_output": "List with items"
251
+ },
252
+ "functions": {
253
+ "title": "Function Definition",
254
+ "description": "Practice defining and calling functions",
255
+ "starter_code": "# Define a function that greets a person\ndef greet(name):\n pass\n",
256
+ "expected_output": "Function definition"
257
+ }
258
+ }
259
+
260
+ template = exercise_templates.get(topic.lower())
261
+ if template:
262
+ exercise = InteractiveExercise(
263
+ id=str(uuid.uuid4()),
264
+ title=template["title"],
265
+ description=template["description"],
266
+ starter_code=template["starter_code"],
267
+ expected_output=template["expected_output"],
268
+ hints=["Think about the basic syntax", "Check the examples"],
269
+ difficulty_level=level,
270
+ topic=topic
271
+ )
272
+ exercises.append(exercise)
273
+
274
+ return exercises
275
+
276
+ def _explain_ast_node(self, node: ast.AST, step_number: int, context: Optional[CodeContext]) -> Optional[StepExplanation]:
277
+ """Generate explanation for an AST node."""
278
+ if isinstance(node, ast.Assign):
279
+ return StepExplanation(
280
+ step_number=step_number,
281
+ description="Variable assignment: storing a value in a variable",
282
+ code_snippet=ast.unparse(node) if hasattr(ast, 'unparse') else str(node),
283
+ related_concepts=["variables", "assignment"]
284
+ )
285
+ elif isinstance(node, ast.FunctionDef):
286
+ return StepExplanation(
287
+ step_number=step_number,
288
+ description=f"Function definition: creating a reusable block of code named '{node.name}'",
289
+ code_snippet=ast.unparse(node) if hasattr(ast, 'unparse') else str(node),
290
+ related_concepts=["functions", "definition", "parameters"]
291
+ )
292
+ elif isinstance(node, ast.Call):
293
+ return StepExplanation(
294
+ step_number=step_number,
295
+ description="Function call: executing a function with given arguments",
296
+ code_snippet=ast.unparse(node) if hasattr(ast, 'unparse') else str(node),
297
+ related_concepts=["functions", "calls", "arguments"]
298
+ )
299
+ elif isinstance(node, ast.If):
300
+ return StepExplanation(
301
+ step_number=step_number,
302
+ description="Conditional statement: executing code based on a condition",
303
+ code_snippet=ast.unparse(node) if hasattr(ast, 'unparse') else str(node),
304
+ related_concepts=["conditionals", "boolean_logic"]
305
+ )
306
+
307
+ return None
308
+
309
+ def _simplify_vocabulary(self, content: str) -> str:
310
+ """Simplify vocabulary for beginners."""
311
+ # Basic vocabulary replacements
312
+ replacements = {
313
+ "instantiate": "create",
314
+ "initialize": "set up",
315
+ "parameter": "input value",
316
+ "argument": "value you pass in",
317
+ "iterate": "go through each item",
318
+ "concatenate": "join together"
319
+ }
320
+
321
+ result = content
322
+ for technical, simple in replacements.items():
323
+ result = result.replace(technical, simple)
324
+
325
+ return result
326
+
327
+ def _add_beginner_context(self, content: str) -> str:
328
+ """Add extra context for beginners."""
329
+ return f"For beginners: {content}\n\nRemember: Take your time and don't worry if this seems complex at first!"
330
+
331
+ def _add_practical_context(self, content: str) -> str:
332
+ """Add practical context for intermediate learners."""
333
+ return f"{content}\n\nPractical tip: This concept is commonly used in real-world programming."
334
+
335
+ def _make_concise(self, content: str) -> str:
336
+ """Make content more concise for advanced learners."""
337
+ # Remove beginner-specific phrases
338
+ phrases_to_remove = [
339
+ "For beginners: ",
340
+ "Remember: ",
341
+ "Don't worry if this seems complex",
342
+ "Take your time"
343
+ ]
344
+
345
+ result = content
346
+ for phrase in phrases_to_remove:
347
+ result = result.replace(phrase, "")
348
+
349
+ return result.strip()