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
fishertools/__init__.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fishertools - инструменты, которые делают Python удобнее и безопаснее для новичков
|
|
3
|
+
|
|
4
|
+
Основная функция:
|
|
5
|
+
explain_error() - объясняет ошибки Python в понятных словах
|
|
6
|
+
|
|
7
|
+
Модули:
|
|
8
|
+
errors - система объяснения ошибок
|
|
9
|
+
safe - безопасные утилиты для новичков
|
|
10
|
+
learn - обучающие инструменты
|
|
11
|
+
legacy - функции для обратной совместимости
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.2.1"
|
|
15
|
+
__author__ = "f1sherFM"
|
|
16
|
+
|
|
17
|
+
# Primary API - main interface for users
|
|
18
|
+
from .errors import explain_error
|
|
19
|
+
|
|
20
|
+
# Exception classes for error handling
|
|
21
|
+
from .errors import (
|
|
22
|
+
FishertoolsError, ExplanationError, FormattingError,
|
|
23
|
+
ConfigurationError, PatternError, SafeUtilityError
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Safe utilities - commonly used beginner-friendly functions
|
|
27
|
+
from .safe import (
|
|
28
|
+
safe_get, safe_divide, safe_max, safe_min, safe_sum,
|
|
29
|
+
safe_read_file, safe_write_file, safe_file_exists,
|
|
30
|
+
safe_get_file_size, safe_list_files
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Learning tools - educational functions
|
|
34
|
+
from .learn import (
|
|
35
|
+
generate_example, show_best_practice,
|
|
36
|
+
list_available_concepts, list_available_topics
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Legacy imports for backward compatibility
|
|
40
|
+
from . import utils
|
|
41
|
+
from . import decorators
|
|
42
|
+
from . import helpers
|
|
43
|
+
|
|
44
|
+
# Module imports for advanced users who want to access specific modules
|
|
45
|
+
from . import errors
|
|
46
|
+
from . import safe
|
|
47
|
+
from . import learn
|
|
48
|
+
from . import legacy
|
|
49
|
+
|
|
50
|
+
# New enhancement modules (fishertools-enhancements)
|
|
51
|
+
from . import learning
|
|
52
|
+
from . import documentation
|
|
53
|
+
from . import examples
|
|
54
|
+
from . import config
|
|
55
|
+
from . import integration
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
# Primary API - the main function users should import
|
|
59
|
+
"explain_error",
|
|
60
|
+
|
|
61
|
+
# Exception classes for error handling
|
|
62
|
+
"FishertoolsError", "ExplanationError", "FormattingError",
|
|
63
|
+
"ConfigurationError", "PatternError", "SafeUtilityError",
|
|
64
|
+
|
|
65
|
+
# Safe utilities - direct access to commonly used functions
|
|
66
|
+
"safe_get", "safe_divide", "safe_max", "safe_min", "safe_sum",
|
|
67
|
+
"safe_read_file", "safe_write_file", "safe_file_exists",
|
|
68
|
+
"safe_get_file_size", "safe_list_files",
|
|
69
|
+
|
|
70
|
+
# Learning tools - direct access to educational functions
|
|
71
|
+
"generate_example", "show_best_practice",
|
|
72
|
+
"list_available_concepts", "list_available_topics",
|
|
73
|
+
|
|
74
|
+
# Legacy modules for backward compatibility
|
|
75
|
+
"utils", "decorators", "helpers",
|
|
76
|
+
|
|
77
|
+
# New modules for advanced usage
|
|
78
|
+
"errors", "safe", "learn", "legacy",
|
|
79
|
+
|
|
80
|
+
# Enhancement modules (fishertools-enhancements)
|
|
81
|
+
"learning", "documentation", "examples", "config", "integration"
|
|
82
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration Management Module
|
|
3
|
+
|
|
4
|
+
Handles learning system configuration through various file formats
|
|
5
|
+
with validation and error recovery.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .manager import ConfigurationManager
|
|
9
|
+
from .parser import ConfigurationParser
|
|
10
|
+
from .models import (
|
|
11
|
+
LearningConfig,
|
|
12
|
+
ValidationResult,
|
|
13
|
+
RecoveryAction,
|
|
14
|
+
ConfigError
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ConfigurationManager",
|
|
19
|
+
"ConfigurationParser",
|
|
20
|
+
"LearningConfig",
|
|
21
|
+
"ValidationResult",
|
|
22
|
+
"RecoveryAction",
|
|
23
|
+
"ConfigError"
|
|
24
|
+
]
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration manager for the learning system.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Dict, Any
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from dataclasses import asdict
|
|
9
|
+
from .models import LearningConfig, ValidationResult, RecoveryAction, ConfigError
|
|
10
|
+
from .parser import ConfigurationParser
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConfigurationManager:
|
|
14
|
+
"""
|
|
15
|
+
Manages learning system configuration through various file formats.
|
|
16
|
+
|
|
17
|
+
Handles loading, saving, validation, and error recovery for
|
|
18
|
+
configuration files in JSON, YAML, and TOML formats.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, default_config_path: Optional[str] = None):
|
|
22
|
+
"""
|
|
23
|
+
Initialize the configuration manager.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
default_config_path: Optional path to default configuration file
|
|
27
|
+
"""
|
|
28
|
+
self.default_config_path = default_config_path or self._get_default_config_path()
|
|
29
|
+
self.parser = ConfigurationParser()
|
|
30
|
+
self._current_config: Optional[LearningConfig] = None
|
|
31
|
+
|
|
32
|
+
def _get_default_config_path(self) -> str:
|
|
33
|
+
"""Get the path to the default configuration file."""
|
|
34
|
+
current_dir = os.path.dirname(__file__)
|
|
35
|
+
return os.path.join(current_dir, 'default_config.json')
|
|
36
|
+
|
|
37
|
+
def load_config(self, config_path: str) -> LearningConfig:
|
|
38
|
+
"""
|
|
39
|
+
Load configuration from a file.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
config_path: Path to the configuration file
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
LearningConfig: Loaded configuration
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
FileNotFoundError: If config file doesn't exist
|
|
49
|
+
ValueError: If config format is invalid
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
# Parse the configuration file
|
|
53
|
+
config_data = self.parser.parse_file(config_path)
|
|
54
|
+
|
|
55
|
+
# Validate the configuration
|
|
56
|
+
validation_result = self.parser.validate_structure(config_data)
|
|
57
|
+
|
|
58
|
+
if not validation_result.is_valid:
|
|
59
|
+
error_messages = [error.message for error in validation_result.errors]
|
|
60
|
+
raise ValueError(f"Configuration validation failed: {'; '.join(error_messages)}")
|
|
61
|
+
|
|
62
|
+
# Create LearningConfig object with defaults for missing fields
|
|
63
|
+
default_config = self.get_default_config()
|
|
64
|
+
merged_config = self.merge_configs(default_config, config_data)
|
|
65
|
+
|
|
66
|
+
self._current_config = merged_config
|
|
67
|
+
return merged_config
|
|
68
|
+
|
|
69
|
+
except FileNotFoundError:
|
|
70
|
+
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
|
71
|
+
except Exception as e:
|
|
72
|
+
raise ValueError(f"Failed to load configuration: {e}")
|
|
73
|
+
|
|
74
|
+
def save_config(self, config: LearningConfig, config_path: str) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Save configuration to a file.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
config: Configuration to save
|
|
80
|
+
config_path: Path where to save the configuration
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
IOError: If file cannot be written
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
# Determine format based on file extension
|
|
87
|
+
format_type = self.parser.detect_format(config_path)
|
|
88
|
+
|
|
89
|
+
# Format the configuration
|
|
90
|
+
if format_type.value == 'json':
|
|
91
|
+
content = self.parser.format_to_json(config)
|
|
92
|
+
elif format_type.value == 'yaml':
|
|
93
|
+
content = self.parser.format_to_yaml(config)
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError(f"Unsupported format for saving: {format_type}")
|
|
96
|
+
|
|
97
|
+
# Ensure directory exists
|
|
98
|
+
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
99
|
+
|
|
100
|
+
# Write the file
|
|
101
|
+
with open(config_path, 'w', encoding='utf-8') as f:
|
|
102
|
+
f.write(content)
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
raise IOError(f"Failed to save configuration to {config_path}: {e}")
|
|
106
|
+
|
|
107
|
+
def validate_config(self, config: LearningConfig) -> ValidationResult:
|
|
108
|
+
"""
|
|
109
|
+
Validate a configuration object.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
config: Configuration to validate
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
ValidationResult: Validation result with errors/warnings
|
|
116
|
+
"""
|
|
117
|
+
# Convert config to dict for validation
|
|
118
|
+
config_dict = asdict(config)
|
|
119
|
+
return self.parser.validate_structure(config_dict)
|
|
120
|
+
|
|
121
|
+
def apply_config(self, config: LearningConfig) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Apply configuration to the learning system.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
config: Configuration to apply
|
|
127
|
+
"""
|
|
128
|
+
# Validate the configuration first
|
|
129
|
+
validation_result = self.validate_config(config)
|
|
130
|
+
|
|
131
|
+
if not validation_result.is_valid:
|
|
132
|
+
error_messages = [error.message for error in validation_result.errors]
|
|
133
|
+
raise ValueError(f"Cannot apply invalid configuration: {'; '.join(error_messages)}")
|
|
134
|
+
|
|
135
|
+
# Store as current configuration
|
|
136
|
+
self._current_config = config
|
|
137
|
+
|
|
138
|
+
# Here we would apply the configuration to various system components
|
|
139
|
+
# For now, we just store it as the current configuration
|
|
140
|
+
|
|
141
|
+
def get_default_config(self) -> LearningConfig:
|
|
142
|
+
"""
|
|
143
|
+
Get the default configuration.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
LearningConfig: Default configuration
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
if os.path.exists(self.default_config_path):
|
|
150
|
+
config_data = self.parser.parse_file(self.default_config_path)
|
|
151
|
+
return self._dict_to_config(config_data)
|
|
152
|
+
else:
|
|
153
|
+
# Return hardcoded defaults if file doesn't exist
|
|
154
|
+
return LearningConfig()
|
|
155
|
+
except Exception:
|
|
156
|
+
# Return hardcoded defaults if parsing fails
|
|
157
|
+
return LearningConfig()
|
|
158
|
+
|
|
159
|
+
def merge_configs(self, base_config: LearningConfig, override_config: Dict[str, Any]) -> LearningConfig:
|
|
160
|
+
"""
|
|
161
|
+
Merge configuration with overrides.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
base_config: Base configuration
|
|
165
|
+
override_config: Configuration overrides
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
LearningConfig: Merged configuration
|
|
169
|
+
"""
|
|
170
|
+
# Convert base config to dict
|
|
171
|
+
base_dict = asdict(base_config)
|
|
172
|
+
|
|
173
|
+
# Update with overrides
|
|
174
|
+
base_dict.update(override_config)
|
|
175
|
+
|
|
176
|
+
# Convert back to LearningConfig
|
|
177
|
+
return self._dict_to_config(base_dict)
|
|
178
|
+
|
|
179
|
+
def _dict_to_config(self, config_dict: Dict[str, Any]) -> LearningConfig:
|
|
180
|
+
"""
|
|
181
|
+
Convert dictionary to LearningConfig object.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
config_dict: Configuration dictionary
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
LearningConfig: Configuration object
|
|
188
|
+
"""
|
|
189
|
+
# Filter out unknown fields and create LearningConfig
|
|
190
|
+
valid_fields = {
|
|
191
|
+
field.name for field in LearningConfig.__dataclass_fields__.values()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
filtered_dict = {
|
|
195
|
+
key: value for key, value in config_dict.items()
|
|
196
|
+
if key in valid_fields
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return LearningConfig(**filtered_dict)
|
|
200
|
+
|
|
201
|
+
def get_current_config(self) -> Optional[LearningConfig]:
|
|
202
|
+
"""
|
|
203
|
+
Get the currently loaded configuration.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Optional[LearningConfig]: Current configuration or None if not loaded
|
|
207
|
+
"""
|
|
208
|
+
return self._current_config
|
|
209
|
+
|
|
210
|
+
def handle_config_error(self, error: ConfigError) -> RecoveryAction:
|
|
211
|
+
"""
|
|
212
|
+
Handle configuration errors with appropriate recovery actions.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
error: Configuration error to handle
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
RecoveryAction: Recommended recovery action
|
|
219
|
+
"""
|
|
220
|
+
from ..errors.recovery import get_recovery_manager, ErrorContext, ErrorSeverity
|
|
221
|
+
|
|
222
|
+
recovery_manager = get_recovery_manager()
|
|
223
|
+
|
|
224
|
+
# Determine error severity based on error type
|
|
225
|
+
if isinstance(error, FileNotFoundError):
|
|
226
|
+
severity = ErrorSeverity.MEDIUM
|
|
227
|
+
error_type = "config_file_missing"
|
|
228
|
+
elif "validation" in str(error).lower():
|
|
229
|
+
severity = ErrorSeverity.MEDIUM
|
|
230
|
+
error_type = "config_validation_error"
|
|
231
|
+
elif "parse" in str(error).lower() or "syntax" in str(error).lower():
|
|
232
|
+
severity = ErrorSeverity.HIGH
|
|
233
|
+
error_type = "config_parse_error"
|
|
234
|
+
else:
|
|
235
|
+
severity = ErrorSeverity.MEDIUM
|
|
236
|
+
error_type = "config_general_error"
|
|
237
|
+
|
|
238
|
+
error_context = ErrorContext(
|
|
239
|
+
component="configuration",
|
|
240
|
+
operation="load_config",
|
|
241
|
+
error_type=error_type,
|
|
242
|
+
error_message=str(error),
|
|
243
|
+
severity=severity,
|
|
244
|
+
metadata={"config_path": getattr(error, 'config_path', 'unknown')}
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
return recovery_manager.handle_error(error_context)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for the Configuration Management module.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import List, Optional, Dict, Any, Literal
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigFormat(Enum):
|
|
11
|
+
"""Supported configuration file formats."""
|
|
12
|
+
JSON = "json"
|
|
13
|
+
YAML = "yaml"
|
|
14
|
+
TOML = "toml"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ErrorSeverity(Enum):
|
|
18
|
+
"""Severity levels for configuration errors."""
|
|
19
|
+
WARNING = "warning"
|
|
20
|
+
ERROR = "error"
|
|
21
|
+
CRITICAL = "critical"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RecoveryStrategy(Enum):
|
|
25
|
+
"""Strategies for error recovery."""
|
|
26
|
+
USE_DEFAULTS = "use_defaults"
|
|
27
|
+
PROMPT_USER = "prompt_user"
|
|
28
|
+
FAIL_GRACEFULLY = "fail_gracefully"
|
|
29
|
+
RETRY = "retry"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ConfigError:
|
|
34
|
+
"""Configuration error information."""
|
|
35
|
+
message: str
|
|
36
|
+
field_path: str
|
|
37
|
+
severity: ErrorSeverity
|
|
38
|
+
suggested_fix: Optional[str] = None
|
|
39
|
+
line_number: Optional[int] = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class ValidationResult:
|
|
44
|
+
"""Result of configuration validation."""
|
|
45
|
+
is_valid: bool
|
|
46
|
+
errors: List[ConfigError] = None
|
|
47
|
+
warnings: List[ConfigError] = None
|
|
48
|
+
|
|
49
|
+
def __post_init__(self):
|
|
50
|
+
if self.errors is None:
|
|
51
|
+
self.errors = []
|
|
52
|
+
if self.warnings is None:
|
|
53
|
+
self.warnings = []
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class RecoveryAction:
|
|
58
|
+
"""Action to take for error recovery."""
|
|
59
|
+
strategy: RecoveryStrategy
|
|
60
|
+
message: str
|
|
61
|
+
fallback_config: Optional[Dict[str, Any]] = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class LearningConfig:
|
|
66
|
+
"""Configuration for the learning system."""
|
|
67
|
+
# Basic settings
|
|
68
|
+
default_level: Literal["beginner", "intermediate", "advanced"] = "beginner"
|
|
69
|
+
explanation_verbosity: Literal["brief", "detailed", "comprehensive"] = "detailed"
|
|
70
|
+
|
|
71
|
+
# Visual settings
|
|
72
|
+
visual_aids_enabled: bool = True
|
|
73
|
+
diagram_style: str = "modern"
|
|
74
|
+
color_scheme: str = "default"
|
|
75
|
+
|
|
76
|
+
# Progress and tracking
|
|
77
|
+
progress_tracking_enabled: bool = True
|
|
78
|
+
save_progress_locally: bool = True
|
|
79
|
+
|
|
80
|
+
# Content and examples
|
|
81
|
+
suggested_topics_count: int = 3
|
|
82
|
+
max_examples_per_topic: int = 5
|
|
83
|
+
exercise_difficulty_progression: List[str] = None
|
|
84
|
+
|
|
85
|
+
# Integration
|
|
86
|
+
readthedocs_project: Optional[str] = None
|
|
87
|
+
sphinx_theme: str = "sphinx_rtd_theme"
|
|
88
|
+
|
|
89
|
+
# Advanced settings
|
|
90
|
+
enable_interactive_sessions: bool = True
|
|
91
|
+
session_timeout_minutes: int = 30
|
|
92
|
+
max_hint_count: int = 3
|
|
93
|
+
|
|
94
|
+
def __post_init__(self):
|
|
95
|
+
if self.exercise_difficulty_progression is None:
|
|
96
|
+
self.exercise_difficulty_progression = ["beginner", "intermediate", "advanced"]
|