createsonline 0.1.26__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 (152) hide show
  1. createsonline/__init__.py +46 -0
  2. createsonline/admin/__init__.py +7 -0
  3. createsonline/admin/content.py +526 -0
  4. createsonline/admin/crud.py +805 -0
  5. createsonline/admin/field_builder.py +559 -0
  6. createsonline/admin/integration.py +482 -0
  7. createsonline/admin/interface.py +2562 -0
  8. createsonline/admin/model_creator.py +513 -0
  9. createsonline/admin/model_manager.py +388 -0
  10. createsonline/admin/modern_dashboard.py +498 -0
  11. createsonline/admin/permissions.py +264 -0
  12. createsonline/admin/user_forms.py +594 -0
  13. createsonline/ai/__init__.py +202 -0
  14. createsonline/ai/fields.py +1226 -0
  15. createsonline/ai/orm.py +325 -0
  16. createsonline/ai/services.py +1244 -0
  17. createsonline/app.py +506 -0
  18. createsonline/auth/__init__.py +8 -0
  19. createsonline/auth/management.py +228 -0
  20. createsonline/auth/models.py +552 -0
  21. createsonline/cli/__init__.py +5 -0
  22. createsonline/cli/commands/__init__.py +122 -0
  23. createsonline/cli/commands/database.py +416 -0
  24. createsonline/cli/commands/info.py +173 -0
  25. createsonline/cli/commands/initdb.py +218 -0
  26. createsonline/cli/commands/project.py +545 -0
  27. createsonline/cli/commands/serve.py +173 -0
  28. createsonline/cli/commands/shell.py +93 -0
  29. createsonline/cli/commands/users.py +148 -0
  30. createsonline/cli/main.py +2041 -0
  31. createsonline/cli/manage.py +274 -0
  32. createsonline/config/__init__.py +9 -0
  33. createsonline/config/app.py +2577 -0
  34. createsonline/config/database.py +179 -0
  35. createsonline/config/docs.py +384 -0
  36. createsonline/config/errors.py +160 -0
  37. createsonline/config/orm.py +43 -0
  38. createsonline/config/request.py +93 -0
  39. createsonline/config/settings.py +176 -0
  40. createsonline/data/__init__.py +23 -0
  41. createsonline/data/dataframe.py +925 -0
  42. createsonline/data/io.py +453 -0
  43. createsonline/data/series.py +557 -0
  44. createsonline/database/__init__.py +60 -0
  45. createsonline/database/abstraction.py +440 -0
  46. createsonline/database/assistant.py +585 -0
  47. createsonline/database/fields.py +442 -0
  48. createsonline/database/migrations.py +132 -0
  49. createsonline/database/models.py +604 -0
  50. createsonline/database.py +438 -0
  51. createsonline/http/__init__.py +28 -0
  52. createsonline/http/client.py +535 -0
  53. createsonline/ml/__init__.py +55 -0
  54. createsonline/ml/classification.py +552 -0
  55. createsonline/ml/clustering.py +680 -0
  56. createsonline/ml/metrics.py +542 -0
  57. createsonline/ml/neural.py +560 -0
  58. createsonline/ml/preprocessing.py +784 -0
  59. createsonline/ml/regression.py +501 -0
  60. createsonline/performance/__init__.py +19 -0
  61. createsonline/performance/cache.py +444 -0
  62. createsonline/performance/compression.py +335 -0
  63. createsonline/performance/core.py +419 -0
  64. createsonline/project_init.py +789 -0
  65. createsonline/routing.py +528 -0
  66. createsonline/security/__init__.py +34 -0
  67. createsonline/security/core.py +811 -0
  68. createsonline/security/encryption.py +349 -0
  69. createsonline/server.py +295 -0
  70. createsonline/static/css/admin.css +263 -0
  71. createsonline/static/css/common.css +358 -0
  72. createsonline/static/css/dashboard.css +89 -0
  73. createsonline/static/favicon.ico +0 -0
  74. createsonline/static/icons/icon-128x128.png +0 -0
  75. createsonline/static/icons/icon-128x128.webp +0 -0
  76. createsonline/static/icons/icon-16x16.png +0 -0
  77. createsonline/static/icons/icon-16x16.webp +0 -0
  78. createsonline/static/icons/icon-180x180.png +0 -0
  79. createsonline/static/icons/icon-180x180.webp +0 -0
  80. createsonline/static/icons/icon-192x192.png +0 -0
  81. createsonline/static/icons/icon-192x192.webp +0 -0
  82. createsonline/static/icons/icon-256x256.png +0 -0
  83. createsonline/static/icons/icon-256x256.webp +0 -0
  84. createsonline/static/icons/icon-32x32.png +0 -0
  85. createsonline/static/icons/icon-32x32.webp +0 -0
  86. createsonline/static/icons/icon-384x384.png +0 -0
  87. createsonline/static/icons/icon-384x384.webp +0 -0
  88. createsonline/static/icons/icon-48x48.png +0 -0
  89. createsonline/static/icons/icon-48x48.webp +0 -0
  90. createsonline/static/icons/icon-512x512.png +0 -0
  91. createsonline/static/icons/icon-512x512.webp +0 -0
  92. createsonline/static/icons/icon-64x64.png +0 -0
  93. createsonline/static/icons/icon-64x64.webp +0 -0
  94. createsonline/static/image/android-chrome-192x192.png +0 -0
  95. createsonline/static/image/android-chrome-512x512.png +0 -0
  96. createsonline/static/image/apple-touch-icon.png +0 -0
  97. createsonline/static/image/favicon-16x16.png +0 -0
  98. createsonline/static/image/favicon-32x32.png +0 -0
  99. createsonline/static/image/favicon.ico +0 -0
  100. createsonline/static/image/favicon.svg +17 -0
  101. createsonline/static/image/icon-128x128.png +0 -0
  102. createsonline/static/image/icon-128x128.webp +0 -0
  103. createsonline/static/image/icon-16x16.png +0 -0
  104. createsonline/static/image/icon-16x16.webp +0 -0
  105. createsonline/static/image/icon-180x180.png +0 -0
  106. createsonline/static/image/icon-180x180.webp +0 -0
  107. createsonline/static/image/icon-192x192.png +0 -0
  108. createsonline/static/image/icon-192x192.webp +0 -0
  109. createsonline/static/image/icon-256x256.png +0 -0
  110. createsonline/static/image/icon-256x256.webp +0 -0
  111. createsonline/static/image/icon-32x32.png +0 -0
  112. createsonline/static/image/icon-32x32.webp +0 -0
  113. createsonline/static/image/icon-384x384.png +0 -0
  114. createsonline/static/image/icon-384x384.webp +0 -0
  115. createsonline/static/image/icon-48x48.png +0 -0
  116. createsonline/static/image/icon-48x48.webp +0 -0
  117. createsonline/static/image/icon-512x512.png +0 -0
  118. createsonline/static/image/icon-512x512.webp +0 -0
  119. createsonline/static/image/icon-64x64.png +0 -0
  120. createsonline/static/image/icon-64x64.webp +0 -0
  121. createsonline/static/image/logo-header-h100.png +0 -0
  122. createsonline/static/image/logo-header-h100.webp +0 -0
  123. createsonline/static/image/logo-header-h200@2x.png +0 -0
  124. createsonline/static/image/logo-header-h200@2x.webp +0 -0
  125. createsonline/static/image/logo.png +0 -0
  126. createsonline/static/js/admin.js +274 -0
  127. createsonline/static/site.webmanifest +35 -0
  128. createsonline/static/templates/admin/base.html +87 -0
  129. createsonline/static/templates/admin/dashboard.html +217 -0
  130. createsonline/static/templates/admin/model_form.html +270 -0
  131. createsonline/static/templates/admin/model_list.html +202 -0
  132. createsonline/static/test_script.js +15 -0
  133. createsonline/static/test_styles.css +59 -0
  134. createsonline/static_files.py +365 -0
  135. createsonline/templates/404.html +100 -0
  136. createsonline/templates/admin_login.html +169 -0
  137. createsonline/templates/base.html +102 -0
  138. createsonline/templates/index.html +151 -0
  139. createsonline/templates.py +205 -0
  140. createsonline/testing.py +322 -0
  141. createsonline/utils.py +448 -0
  142. createsonline/validation/__init__.py +49 -0
  143. createsonline/validation/fields.py +598 -0
  144. createsonline/validation/models.py +504 -0
  145. createsonline/validation/validators.py +561 -0
  146. createsonline/views.py +184 -0
  147. createsonline-0.1.26.dist-info/METADATA +46 -0
  148. createsonline-0.1.26.dist-info/RECORD +152 -0
  149. createsonline-0.1.26.dist-info/WHEEL +5 -0
  150. createsonline-0.1.26.dist-info/entry_points.txt +2 -0
  151. createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
  152. createsonline-0.1.26.dist-info/top_level.txt +1 -0
createsonline/utils.py ADDED
@@ -0,0 +1,448 @@
1
+ # createsonline/utils.py
2
+ """
3
+ CREATESONLINE Shared Utilities Module
4
+
5
+ Common functions and utilities used across the framework.
6
+ Consolidates duplicate code patterns identified in the codebase.
7
+ """
8
+
9
+ import os
10
+ import hashlib
11
+ import secrets
12
+ import logging
13
+ from datetime import datetime, timedelta
14
+ from typing import Dict, Any, List, Union, Callable
15
+
16
+ # Setup logging
17
+ logger = logging.getLogger("createsonline.utils")
18
+
19
+
20
+ # Common Import Error Handler
21
+ def get_import_error(module_name: str, feature_name: str = None) -> str:
22
+ """
23
+ Standardized import error message for optional dependencies.
24
+
25
+ Args:
26
+ module_name: Name of the missing module
27
+ feature_name: Optional feature name that requires the module
28
+
29
+ Returns:
30
+ Formatted error message
31
+ """
32
+ if feature_name:
33
+ return f"❌ {feature_name} requires {module_name}. Install with: pip install {module_name}"
34
+ return f"❌ Module '{module_name}' not found. Install with: pip install {module_name}"
35
+
36
+
37
+ # Base Info Pattern
38
+ class BaseInfoProvider:
39
+ """Base class for standardized info dictionary patterns"""
40
+
41
+ def __init__(self, name: str, version: str = "1.0.0"):
42
+ self.name = name
43
+ self.version = version
44
+ self.available = True
45
+ self.features = []
46
+ self.dependencies = []
47
+
48
+ def get_info(self) -> Dict[str, Any]:
49
+ """Get standardized info dictionary"""
50
+ return {
51
+ 'name': self.name,
52
+ 'version': self.version,
53
+ 'available': self.available,
54
+ 'features': self.features,
55
+ 'dependencies': self.dependencies,
56
+ 'timestamp': datetime.utcnow().isoformat(),
57
+ 'framework': 'CREATESONLINE'
58
+ }
59
+
60
+ def add_feature(self, feature: str, description: str = None):
61
+ """Add a feature to the info"""
62
+ self.features.append({
63
+ 'name': feature,
64
+ 'description': description or f"Feature: {feature}",
65
+ 'enabled': True
66
+ })
67
+
68
+ def add_dependency(self, name: str, version: str = None, optional: bool = False):
69
+ """Add a dependency to the info"""
70
+ self.dependencies.append({
71
+ 'name': name,
72
+ 'version': version,
73
+ 'optional': optional,
74
+ 'available': self._check_dependency(name)
75
+ })
76
+
77
+ def _check_dependency(self, name: str) -> bool:
78
+ """Check if dependency is available"""
79
+ try:
80
+ __import__(name)
81
+ return True
82
+ except ImportError:
83
+ return False
84
+
85
+
86
+ # Metrics Base Class
87
+ class BaseMetrics:
88
+ """Base metrics collection mixin"""
89
+
90
+ def __init__(self):
91
+ self.requests = 0
92
+ self.errors = 0
93
+ self.start_time = datetime.utcnow()
94
+ self.last_activity = datetime.utcnow()
95
+
96
+ def increment_requests(self):
97
+ """Increment request counter"""
98
+ self.requests += 1
99
+ self.last_activity = datetime.utcnow()
100
+
101
+ def increment_errors(self):
102
+ """Increment error counter"""
103
+ self.errors += 1
104
+ self.last_activity = datetime.utcnow()
105
+
106
+ def get_stats(self) -> Dict[str, Any]:
107
+ """Get standardized stats dictionary"""
108
+ uptime = datetime.utcnow() - self.start_time
109
+
110
+ return {
111
+ 'requests': self.requests,
112
+ 'errors': self.errors,
113
+ 'uptime_seconds': int(uptime.total_seconds()),
114
+ 'uptime_human': self._format_uptime(uptime),
115
+ 'last_activity': self.last_activity.isoformat(),
116
+ 'error_rate': (self.errors / max(self.requests, 1)) * 100,
117
+ 'status': 'healthy' if self.errors == 0 else 'degraded'
118
+ }
119
+
120
+ def _format_uptime(self, uptime: timedelta) -> str:
121
+ """Format uptime in human readable format"""
122
+ days = uptime.days
123
+ hours = uptime.seconds // 3600
124
+ minutes = (uptime.seconds % 3600) // 60
125
+
126
+ if days > 0:
127
+ return f"{days}d {hours}h {minutes}m"
128
+ elif hours > 0:
129
+ return f"{hours}h {minutes}m"
130
+ else:
131
+ return f"{minutes}m"
132
+
133
+
134
+ # Response Formatter
135
+ class ResponseFormatter:
136
+ """Standardized response formatting"""
137
+
138
+ @staticmethod
139
+ def success(data: Any = None, message: str = "Success") -> Dict[str, Any]:
140
+ """Format success response"""
141
+ return {
142
+ 'status': 'success',
143
+ 'message': message,
144
+ 'data': data,
145
+ 'timestamp': datetime.utcnow().isoformat(),
146
+ 'framework': 'CREATESONLINE'
147
+ }
148
+
149
+ @staticmethod
150
+ def error(message: str, code: int = 500, details: Any = None) -> Dict[str, Any]:
151
+ """Format error response"""
152
+ return {
153
+ 'status': 'error',
154
+ 'message': message,
155
+ 'code': code,
156
+ 'details': details,
157
+ 'timestamp': datetime.utcnow().isoformat(),
158
+ 'framework': 'CREATESONLINE'
159
+ }
160
+
161
+ @staticmethod
162
+ def paginated(data: List[Any], page: int, per_page: int, total: int) -> Dict[str, Any]:
163
+ """Format paginated response"""
164
+ return {
165
+ 'status': 'success',
166
+ 'data': data,
167
+ 'pagination': {
168
+ 'page': page,
169
+ 'per_page': per_page,
170
+ 'total': total,
171
+ 'pages': (total + per_page - 1) // per_page,
172
+ 'has_next': page * per_page < total,
173
+ 'has_prev': page > 1
174
+ },
175
+ 'timestamp': datetime.utcnow().isoformat(),
176
+ 'framework': 'CREATESONLINE'
177
+ }
178
+
179
+
180
+ # Security Utilities
181
+ class SecurityUtils:
182
+ """Common security utilities"""
183
+
184
+ @staticmethod
185
+ def generate_token(length: int = 32) -> str:
186
+ """Generate secure random token"""
187
+ return secrets.token_urlsafe(length)
188
+
189
+ @staticmethod
190
+ def hash_password(password: str, salt: str = None) -> Dict[str, str]:
191
+ """
192
+ Hash password with salt - recommends bcrypt for production.
193
+
194
+ IMPORTANT: SHA-256 fallback is for development only!
195
+ For production use, install bcrypt: pip install bcrypt
196
+
197
+ Args:
198
+ password: Plain text password to hash
199
+ salt: Optional salt (used only for SHA-256 fallback, ignored for bcrypt)
200
+
201
+ Returns:
202
+ Dict with 'hash', 'salt', and 'method' keys
203
+ """
204
+ if salt is None:
205
+ salt = secrets.token_hex(16)
206
+
207
+ # Try to use bcrypt if available, fallback to SHA-256
208
+ try:
209
+ import bcrypt
210
+ # bcrypt handles salting internally
211
+ password_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
212
+ return {
213
+ 'hash': password_hash,
214
+ 'salt': '', # bcrypt includes salt in hash
215
+ 'method': 'bcrypt'
216
+ }
217
+ except ImportError:
218
+ # WARNING: SHA-256 fallback is for development only!
219
+ # For production use, install bcrypt: pip install bcrypt
220
+ password_hash = hashlib.sha256(password.encode()).hexdigest()
221
+ return {
222
+ 'hash': password_hash,
223
+ 'salt': '', # Empty salt for consistency with verify_password fallback
224
+ 'method': 'sha256'
225
+ }
226
+
227
+ @staticmethod
228
+ def verify_password(password: str, stored_hash: str, salt: str = None) -> bool:
229
+ """Verify password against stored hash (consolidated implementation)"""
230
+ # Try bcrypt first if hash looks like bcrypt format
231
+ if stored_hash.startswith(('$2a$', '$2b$', '$2y$')):
232
+ try:
233
+ import bcrypt
234
+ return bcrypt.checkpw(password.encode('utf-8'), stored_hash.encode('utf-8'))
235
+ except ImportError:
236
+ pass
237
+
238
+ # Fallback to SHA-256 verification
239
+ if salt:
240
+ # Salted hash verification
241
+ computed_hash = hashlib.sha256((password + salt).encode()).hexdigest()
242
+ return secrets.compare_digest(computed_hash, stored_hash)
243
+ else:
244
+ # Simple hash verification (for backward compatibility)
245
+ computed_hash = hashlib.sha256(password.encode()).hexdigest()
246
+ return secrets.compare_digest(computed_hash, stored_hash)
247
+
248
+ @staticmethod
249
+ def sanitize_input(value: str, max_length: int = 1000, escape_html: bool = False) -> str:
250
+ """Enhanced input sanitization with optional HTML escaping"""
251
+ if not isinstance(value, str):
252
+ value = str(value)
253
+
254
+ # Remove null bytes and limit length
255
+ value = value.replace('\x00', '').strip()[:max_length]
256
+
257
+ # Optional HTML escaping for template safety
258
+ if escape_html:
259
+ # Basic HTML entity escaping
260
+ value = (value
261
+ .replace('&', '&amp;')
262
+ .replace('<', '&lt;')
263
+ .replace('>', '&gt;')
264
+ .replace('"', '&quot;')
265
+ .replace("'", '&#x27;'))
266
+
267
+ return value
268
+
269
+
270
+ # File Utilities
271
+ class FileUtils:
272
+ """Common file operations"""
273
+
274
+ @staticmethod
275
+ def ensure_directory(path: str) -> bool:
276
+ """Ensure directory exists, create if needed"""
277
+ try:
278
+ os.makedirs(path, exist_ok=True)
279
+ return True
280
+ except Exception as e:
281
+ logger.error(f"Failed to create directory {path}: {e}")
282
+ return False
283
+
284
+ @staticmethod
285
+ def safe_filename(filename: str) -> str:
286
+ """Generate safe filename"""
287
+ # Remove dangerous characters
288
+ safe_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_."
289
+ safe_filename = ''.join(c for c in filename if c in safe_chars)
290
+
291
+ # Limit length and ensure it's not empty
292
+ safe_filename = safe_filename[:100]
293
+ if not safe_filename:
294
+ safe_filename = f"file_{secrets.token_hex(8)}"
295
+
296
+ return safe_filename
297
+
298
+ @staticmethod
299
+ def get_file_info(path: str, include_error_details: bool = False) -> Dict[str, Any]:
300
+ """Get file information with optional error details for debugging"""
301
+ try:
302
+ stat = os.stat(path)
303
+ return {
304
+ 'path': path,
305
+ 'size': stat.st_size,
306
+ 'modified': datetime.fromtimestamp(stat.st_mtime).isoformat(),
307
+ 'created': datetime.fromtimestamp(stat.st_ctime).isoformat(),
308
+ 'is_file': os.path.isfile(path),
309
+ 'is_dir': os.path.isdir(path),
310
+ 'exists': True
311
+ }
312
+ except Exception as e:
313
+ result = {
314
+ 'path': path,
315
+ 'exists': False
316
+ }
317
+
318
+ # Include error details in development/debug mode
319
+ if include_error_details:
320
+ result['error'] = str(e)
321
+ result['error_type'] = type(e).__name__
322
+
323
+ return result
324
+
325
+
326
+ # Validation Utilities
327
+ class ValidationUtils:
328
+ """Common validation functions"""
329
+
330
+ @staticmethod
331
+ def is_valid_email(email: str) -> bool:
332
+ """Basic email validation"""
333
+ import re
334
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
335
+ return bool(re.match(pattern, email))
336
+
337
+ @staticmethod
338
+ def is_strong_password(password: str) -> Dict[str, Union[bool, List[str]]]:
339
+ """Check password strength"""
340
+ issues = []
341
+
342
+ if len(password) < 8:
343
+ issues.append("Password must be at least 8 characters long")
344
+
345
+ if not any(c.isupper() for c in password):
346
+ issues.append("Password must contain at least one uppercase letter")
347
+
348
+ if not any(c.islower() for c in password):
349
+ issues.append("Password must contain at least one lowercase letter")
350
+
351
+ if not any(c.isdigit() for c in password):
352
+ issues.append("Password must contain at least one number")
353
+
354
+ return {
355
+ 'is_strong': len(issues) == 0,
356
+ 'issues': issues
357
+ }
358
+
359
+ @staticmethod
360
+ def validate_required_fields(data: Dict[str, Any], required_fields: List[str]) -> Dict[str, Union[bool, List[str]]]:
361
+ """Validate required fields in data"""
362
+ missing = []
363
+
364
+ for field in required_fields:
365
+ if field not in data or data[field] is None or data[field] == '':
366
+ missing.append(field)
367
+
368
+ return {
369
+ 'is_valid': len(missing) == 0,
370
+ 'missing_fields': missing
371
+ }
372
+
373
+
374
+ # Environment Utilities
375
+ class EnvUtils:
376
+ """Environment variable utilities"""
377
+
378
+ @staticmethod
379
+ def get_env_bool(key: str, default: bool = False) -> bool:
380
+ """Get boolean from environment variable"""
381
+ value = os.getenv(key, str(default)).lower()
382
+ return value in ('true', '1', 'yes', 'on')
383
+
384
+ @staticmethod
385
+ def get_env_int(key: str, default: int = 0) -> int:
386
+ """Get integer from environment variable"""
387
+ try:
388
+ return int(os.getenv(key, default))
389
+ except (ValueError, TypeError):
390
+ return default
391
+
392
+ @staticmethod
393
+ def get_env_list(key: str, default: List[str] = None, separator: str = ',') -> List[str]:
394
+ """Get list from environment variable"""
395
+ value = os.getenv(key)
396
+ if not value:
397
+ return default or []
398
+
399
+ return [item.strip() for item in value.split(separator) if item.strip()]
400
+
401
+ @staticmethod
402
+ def get_database_config() -> Dict[str, Any]:
403
+ """Get database configuration from environment"""
404
+ return {
405
+ 'url': os.getenv('DATABASE_URL', 'sqlite:///createsonline.db'),
406
+ 'pool_size': EnvUtils.get_env_int('DATABASE_POOL_SIZE', 5),
407
+ 'max_overflow': EnvUtils.get_env_int('DATABASE_MAX_OVERFLOW', 10),
408
+ 'echo': EnvUtils.get_env_bool('DATABASE_ECHO', False),
409
+ 'auto_create_tables': EnvUtils.get_env_bool('DATABASE_AUTO_CREATE', True)
410
+ }
411
+
412
+
413
+ # Decorator Utilities
414
+ def retry(max_attempts: int = 3, delay: float = 1.0, backoff: float = 2.0):
415
+ """Retry decorator with exponential backoff"""
416
+ def decorator(func: Callable) -> Callable:
417
+ def wrapper(*args, **kwargs):
418
+ for attempt in range(max_attempts):
419
+ try:
420
+ return func(*args, **kwargs)
421
+ except Exception as e:
422
+ if attempt == max_attempts - 1:
423
+ raise e
424
+
425
+ import time
426
+ time.sleep(delay * (backoff ** attempt))
427
+
428
+ return wrapper
429
+ return decorator
430
+
431
+
432
+ def timing(func: Callable) -> Callable:
433
+ """Timing decorator to measure function execution time"""
434
+ def wrapper(*args, **kwargs):
435
+ start_time = datetime.utcnow()
436
+ try:
437
+ result = func(*args, **kwargs)
438
+ end_time = datetime.utcnow()
439
+ duration = (end_time - start_time).total_seconds()
440
+ logger.debug(f"{func.__name__} executed in {duration:.3f}s")
441
+ return result
442
+ except Exception as e:
443
+ end_time = datetime.utcnow()
444
+ duration = (end_time - start_time).total_seconds()
445
+ logger.error(f"{func.__name__} failed after {duration:.3f}s: {e}")
446
+ raise
447
+
448
+ return wrapper
@@ -0,0 +1,49 @@
1
+ """
2
+ CREATESONLINE Internal Validation System
3
+
4
+ Pure Python validation system with zero external dependencies.
5
+ Lightweight replacement for Pydantic with AI-native features.
6
+ """
7
+
8
+ from .models import BaseModel, ValidationError
9
+ from .fields import (
10
+ Field, StringField, IntField, FloatField, BoolField,
11
+ EmailField, URLField, DateField, ListField, DictField,
12
+ OptionalField, ChoiceField
13
+ )
14
+ from .validators import (
15
+ validator, required, min_length, max_length,
16
+ min_value, max_value, regex_validator, email_validator,
17
+ url_validator
18
+ )
19
+
20
+ __all__ = [
21
+ # Core classes
22
+ 'BaseModel',
23
+ 'ValidationError',
24
+
25
+ # Fields
26
+ 'Field',
27
+ 'StringField',
28
+ 'IntField',
29
+ 'FloatField',
30
+ 'BoolField',
31
+ 'EmailField',
32
+ 'URLField',
33
+ 'DateField',
34
+ 'ListField',
35
+ 'DictField',
36
+ 'OptionalField',
37
+ 'ChoiceField',
38
+
39
+ # Validators
40
+ 'validator',
41
+ 'required',
42
+ 'min_length',
43
+ 'max_length',
44
+ 'min_value',
45
+ 'max_value',
46
+ 'regex_validator',
47
+ 'email_validator',
48
+ 'url_validator'
49
+ ]