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
@@ -0,0 +1,160 @@
1
+ """
2
+ CREATESONLINE Error Handling
3
+
4
+ Error handling and error page generation.
5
+ """
6
+ from typing import Tuple
7
+
8
+
9
+ class HTTPException(Exception):
10
+ """HTTP exception with status code and detail message"""
11
+
12
+ def __init__(self, status_code: int, detail: str = None):
13
+ self.status_code = status_code
14
+ self.detail = detail
15
+ super().__init__(detail)
16
+
17
+
18
+ class ErrorPageGenerator:
19
+ """Generate error pages for HTTP errors"""
20
+
21
+ @staticmethod
22
+ def generate_error_page(
23
+ status_code: int,
24
+ error_message: str,
25
+ path: str = "",
26
+ method: str = "GET",
27
+ details: str = ""
28
+ ) -> str:
29
+ """Generate error page HTML"""
30
+ status_text = {
31
+ 400: "Bad Request",
32
+ 401: "Unauthorized",
33
+ 403: "Forbidden",
34
+ 404: "Not Found",
35
+ 405: "Method Not Allowed",
36
+ 500: "Internal Server Error",
37
+ 502: "Bad Gateway",
38
+ 503: "Service Unavailable"
39
+ }.get(status_code, "Error")
40
+
41
+ html = f"""
42
+ <!DOCTYPE html>
43
+ <html lang="en">
44
+ <head>
45
+ <meta charset="UTF-8">
46
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
47
+ <title>{status_code} {status_text} - CREATESONLINE</title>
48
+ <style>
49
+ * {{ margin: 0; padding: 0; box-sizing: border-box; }}
50
+
51
+ body {{
52
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
53
+ background: linear-gradient(135deg, #000000 0%, #1a1a1a 100%);
54
+ color: #fff;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ min-height: 100vh;
59
+ padding: 2rem;
60
+ }}
61
+
62
+ .error-container {{
63
+ text-align: center;
64
+ max-width: 600px;
65
+ }}
66
+
67
+ .error-code {{
68
+ font-size: 8rem;
69
+ font-weight: 900;
70
+ margin-bottom: 1rem;
71
+ background: linear-gradient(135deg, #ef4444, #f59e0b);
72
+ -webkit-background-clip: text;
73
+ -webkit-text-fill-color: transparent;
74
+ background-clip: text;
75
+ }}
76
+
77
+ .error-title {{
78
+ font-size: 2rem;
79
+ margin-bottom: 1rem;
80
+ font-weight: 700;
81
+ }}
82
+
83
+ .error-message {{
84
+ font-size: 1.125rem;
85
+ color: #d1d5db;
86
+ margin-bottom: 2rem;
87
+ }}
88
+
89
+ .error-details {{
90
+ background: rgba(255, 255, 255, 0.05);
91
+ border: 1px solid rgba(255, 255, 255, 0.1);
92
+ border-radius: 8px;
93
+ padding: 1.5rem;
94
+ margin-bottom: 2rem;
95
+ text-align: left;
96
+ font-family: 'Monaco', monospace;
97
+ font-size: 0.875rem;
98
+ }}
99
+
100
+ .detail-row {{
101
+ display: grid;
102
+ grid-template-columns: 150px 1fr;
103
+ gap: 1rem;
104
+ margin-bottom: 0.5rem;
105
+ }}
106
+
107
+ .detail-label {{
108
+ color: #6366f1;
109
+ font-weight: 600;
110
+ }}
111
+
112
+ .detail-value {{
113
+ color: #d1d5db;
114
+ word-break: break-all;
115
+ }}
116
+
117
+ .back-link {{
118
+ display: inline-block;
119
+ padding: 0.75rem 1.5rem;
120
+ background: #6366f1;
121
+ color: white;
122
+ text-decoration: none;
123
+ border-radius: 6px;
124
+ transition: all 0.3s ease;
125
+ }}
126
+
127
+ .back-link:hover {{
128
+ background: #4f46e5;
129
+ transform: translateY(-2px);
130
+ }}
131
+ </style>
132
+ </head>
133
+ <body>
134
+ <div class="error-container">
135
+ <div class="error-code">{status_code}</div>
136
+ <h1 class="error-title">{status_text}</h1>
137
+ <p class="error-message">{error_message}</p>
138
+
139
+ <div class="error-details">
140
+ <div class="detail-row">
141
+ <span class="detail-label">Status:</span>
142
+ <span class="detail-value">{status_code} {status_text}</span>
143
+ </div>
144
+ <div class="detail-row">
145
+ <span class="detail-label">Method:</span>
146
+ <span class="detail-value">{method}</span>
147
+ </div>
148
+ <div class="detail-row">
149
+ <span class="detail-label">Path:</span>
150
+ <span class="detail-value">{path}</span>
151
+ </div>
152
+ {f'<div class="detail-row"><span class="detail-label">Details:</span><span class="detail-value">{details}</span></div>' if details else ''}
153
+ </div>
154
+
155
+ <a href="/" class="back-link">← Back to Home</a>
156
+ </div>
157
+ </body>
158
+ </html>
159
+ """
160
+ return html
@@ -0,0 +1,43 @@
1
+ """Compatibility layer for the AI-enhanced ORM.
2
+
3
+ The original test-suite and packaging metadata expect to import the
4
+ ``AIEnhancedORM`` class from ``createsonline.config.orm``. During the
5
+ repository's refactor the implementation was moved to
6
+ ``createsonline.ai.orm`` without leaving a public shim behind, which now
7
+ breaks both the automated tests and downstream users installing the PyPI
8
+ package. Re-introduce the module as a light-weight re-export so that the
9
+ documented import path keeps working while the actual implementation
10
+ remains in :mod:`createsonline.ai.orm`.
11
+
12
+ Only the public classes that are required by the tests are re-exported
13
+ today. Should additional helpers be needed in the future they can be
14
+ added here without changing the underlying ORM module again.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from createsonline.ai.orm import AIBaseModel, AIEnhancedORM, Base
20
+
21
+ __all__ = ["AIEnhancedORM", "AIBaseModel", "Base"]
22
+
23
+
24
+ def __getattr__(name: str):
25
+ """Provide a helpful error for unexpected attribute access.
26
+
27
+ The compatibility module purposefully keeps a small surface area. If
28
+ another symbol is requested we forward the attribute access to the
29
+ original module and raise an informative :class:`AttributeError` when
30
+ the symbol does not exist. This mirrors the behaviour developers were
31
+ accustomed to before the refactor while still pointing them to the
32
+ new canonical location.
33
+ """
34
+
35
+ from createsonline import ai
36
+
37
+ try:
38
+ return getattr(ai.orm, name)
39
+ except AttributeError as exc: # pragma: no cover - defensive branch
40
+ raise AttributeError(
41
+ f"module 'createsonline.config.orm' has no attribute '{name}'. "
42
+ "The ORM implementation now lives in 'createsonline.ai.orm'."
43
+ ) from exc
@@ -0,0 +1,93 @@
1
+ """
2
+ CREATESONLINE Internal Request Handling
3
+
4
+ Request object and utilities for handling HTTP requests in internal ASGI mode.
5
+ """
6
+ from typing import Dict, Any, Optional
7
+ from urllib.parse import parse_qs
8
+
9
+
10
+ class CreatesonlineInternalRequest:
11
+ """Internal request object for zero-dependency mode"""
12
+
13
+ def __init__(self, scope, receive):
14
+ self.scope = scope
15
+ self.receive = receive
16
+ self.method = scope.get('method', 'GET')
17
+ self.path = scope.get('path', '/')
18
+ self.url = self._build_url(scope)
19
+ self.query_params = self._parse_query_string(scope.get('query_string', b'').decode())
20
+ self.headers = self._parse_headers(scope.get('headers', []))
21
+ self._body = None
22
+ self._json_data = None
23
+
24
+ def _build_url(self, scope) -> str:
25
+ """Build full URL from ASGI scope"""
26
+ scheme = scope.get('scheme', 'http')
27
+ server = scope.get('server', ('localhost', 8000))
28
+ host, port = server
29
+ path = scope.get('path', '/')
30
+ query_string = scope.get('query_string', b'').decode()
31
+
32
+ if (scheme == 'https' and port == 443) or (scheme == 'http' and port == 80):
33
+ url = f"{scheme}://{host}{path}"
34
+ else:
35
+ url = f"{scheme}://{host}:{port}{path}"
36
+
37
+ if query_string:
38
+ url += f"?{query_string}"
39
+
40
+ return url
41
+
42
+ def _parse_query_string(self, query_string: str) -> Dict[str, Any]:
43
+ """Parse query string into dictionary"""
44
+ if not query_string:
45
+ return {}
46
+ return {k: v[0] if len(v) == 1 else v for k, v in parse_qs(query_string).items()}
47
+
48
+ def _parse_headers(self, headers: list) -> Dict[str, str]:
49
+ """Parse ASGI headers format to dictionary"""
50
+ header_dict = {}
51
+ for name, value in headers:
52
+ key = name.decode('latin-1').lower()
53
+ header_dict[key] = value.decode('latin-1')
54
+ return header_dict
55
+
56
+ async def json(self) -> Dict[str, Any]:
57
+ """Parse request body as JSON"""
58
+ if self._json_data is None:
59
+ import json
60
+ body = await self._get_body()
61
+ try:
62
+ self._json_data = json.loads(body.decode('utf-8'))
63
+ except (json.JSONDecodeError, UnicodeDecodeError):
64
+ self._json_data = {}
65
+ return self._json_data
66
+
67
+ async def body(self) -> bytes:
68
+ """Get raw request body"""
69
+ return await self._get_body()
70
+
71
+ async def _get_body(self) -> bytes:
72
+ """Read body from request stream"""
73
+ if self._body is None:
74
+ body_parts = []
75
+ while True:
76
+ message = await self.receive()
77
+ if message['type'] == 'http.request':
78
+ body_parts.append(message.get('body', b''))
79
+ if not message.get('more_body', False):
80
+ break
81
+ self._body = b''.join(body_parts)
82
+ return self._body
83
+
84
+ @property
85
+ def client_ip(self) -> Optional[str]:
86
+ """Get client IP address"""
87
+ client = self.scope.get('client')
88
+ return client[0] if client else None
89
+
90
+ @property
91
+ def is_secure(self) -> bool:
92
+ """Check if request is HTTPS"""
93
+ return self.scope.get('scheme') == 'https'
@@ -0,0 +1,176 @@
1
+ # createsonline/config/settings.py
2
+ """
3
+ CREATESONLINE Settings Management
4
+
5
+ Simple, unified settings for CREATESONLINE applications.
6
+ """
7
+ import os
8
+ from typing import Dict, Any, Optional
9
+ from pathlib import Path
10
+
11
+ class CreatesonlineSettings:
12
+ """Simple settings manager for CREATESONLINE framework"""
13
+
14
+ def __init__(self, env_file: Optional[str] = None):
15
+ """Initialize settings from environment variables and .env file"""
16
+ self.env_file = env_file or '.env'
17
+ self._load_env_file()
18
+
19
+ def _load_env_file(self):
20
+ """Load environment variables from .env file"""
21
+ env_path = Path(self.env_file)
22
+ if env_path.exists():
23
+ try:
24
+ from dotenv import load_dotenv
25
+ load_dotenv(env_path)
26
+ except ImportError:
27
+ # dotenv not installed, skip loading
28
+ pass
29
+
30
+ # Core application settings
31
+ @property
32
+ def DEBUG(self) -> bool:
33
+ return os.getenv('DEBUG', 'false').lower() in ('true', '1', 'yes')
34
+
35
+ @property
36
+ def SECRET_KEY(self) -> str:
37
+ return os.getenv('SECRET_KEY', 'createsonline-dev-key-change-in-production')
38
+
39
+ @property
40
+ def HOST(self) -> str:
41
+ return os.getenv('HOST', '127.0.0.1')
42
+
43
+ @property
44
+ def PORT(self) -> int:
45
+ return int(os.getenv('PORT', '8000'))
46
+
47
+ @property
48
+ def ENVIRONMENT(self) -> str:
49
+ return os.getenv('ENVIRONMENT', 'development')
50
+
51
+ # Database settings
52
+ @property
53
+ def DATABASE_URL(self) -> str:
54
+ return os.getenv('DATABASE_URL', 'sqlite:///./createsonline.db')
55
+
56
+ @property
57
+ def DATABASE_ECHO(self) -> bool:
58
+ return os.getenv('DATABASE_ECHO', 'false').lower() in ('true', '1', 'yes')
59
+
60
+ # AI settings
61
+ @property
62
+ def OPENAI_API_KEY(self) -> Optional[str]:
63
+ return os.getenv('OPENAI_API_KEY')
64
+
65
+ @property
66
+ def ANTHROPIC_API_KEY(self) -> Optional[str]:
67
+ return os.getenv('ANTHROPIC_API_KEY')
68
+
69
+ @property
70
+ def AI_CACHE_TTL(self) -> int:
71
+ return int(os.getenv('AI_CACHE_TTL', '3600')) # 1 hour
72
+
73
+ @property
74
+ def AI_ENABLED(self) -> bool:
75
+ return os.getenv('AI_ENABLED', 'true').lower() in ('true', '1', 'yes')
76
+
77
+ # Admin settings
78
+ @property
79
+ def ADMIN_ENABLED(self) -> bool:
80
+ return os.getenv('ADMIN_ENABLED', 'true').lower() in ('true', '1', 'yes')
81
+
82
+ @property
83
+ def ADMIN_PATH(self) -> str:
84
+ return os.getenv('ADMIN_PATH', '/admin')
85
+
86
+ # Authentication settings
87
+ @property
88
+ def AUTH_ENABLED(self) -> bool:
89
+ return os.getenv('AUTH_ENABLED', 'true').lower() in ('true', '1', 'yes')
90
+
91
+ @property
92
+ def SESSION_TIMEOUT(self) -> int:
93
+ return int(os.getenv('SESSION_TIMEOUT', '3600')) # 1 hour
94
+
95
+ @property
96
+ def PASSWORD_MIN_LENGTH(self) -> int:
97
+ return int(os.getenv('PASSWORD_MIN_LENGTH', '8'))
98
+
99
+ # CORS settings
100
+ @property
101
+ def CORS_ORIGINS(self) -> list:
102
+ origins = os.getenv('CORS_ORIGINS', '')
103
+ if origins:
104
+ return [origin.strip() for origin in origins.split(',')]
105
+ return ['*'] if self.DEBUG else []
106
+
107
+ # Static files settings (Django-style)
108
+ @property
109
+ def STATIC_URL(self) -> str:
110
+ """URL prefix for static files"""
111
+ return os.getenv('STATIC_URL', '/static/')
112
+
113
+ @property
114
+ def STATIC_ROOT(self) -> Optional[Path]:
115
+ """Directory for collected static files (for production)"""
116
+ static_root = os.getenv('STATIC_ROOT')
117
+ return Path(static_root) if static_root else None
118
+
119
+ @property
120
+ def STATICFILES_DIRS(self) -> list:
121
+ """Additional directories to search for static files"""
122
+ # Get from environment (comma-separated paths)
123
+ dirs_str = os.getenv('STATICFILES_DIRS', '')
124
+ if dirs_str:
125
+ return [Path(d.strip()) for d in dirs_str.split(',') if d.strip()]
126
+
127
+ # Auto-discover: Look for 'static' directory in project root
128
+ base_dir = Path.cwd()
129
+ static_dir = base_dir / "static"
130
+
131
+ if static_dir.exists():
132
+ return [static_dir]
133
+ return []
134
+
135
+ @property
136
+ def TEMPLATE_DIRS(self) -> list:
137
+ """Directories to search for templates"""
138
+ # Get from environment
139
+ dirs_str = os.getenv('TEMPLATE_DIRS', '')
140
+ if dirs_str:
141
+ return [Path(d.strip()) for d in dirs_str.split(',') if d.strip()]
142
+
143
+ # Auto-discover: Look for 'templates' directory in project root
144
+ base_dir = Path.cwd()
145
+ template_dir = base_dir / "templates"
146
+
147
+ if template_dir.exists():
148
+ return [template_dir]
149
+ return []
150
+
151
+ def get(self, key: str, default: Any = None) -> Any:
152
+ """Get setting value by key"""
153
+ return getattr(self, key, default)
154
+
155
+ def to_dict(self) -> Dict[str, Any]:
156
+ """Convert settings to dictionary"""
157
+ return {
158
+ 'DEBUG': self.DEBUG,
159
+ 'SECRET_KEY': self.SECRET_KEY,
160
+ 'HOST': self.HOST,
161
+ 'PORT': self.PORT,
162
+ 'ENVIRONMENT': self.ENVIRONMENT,
163
+ 'DATABASE_URL': self.DATABASE_URL,
164
+ 'DATABASE_ECHO': self.DATABASE_ECHO,
165
+ 'AI_ENABLED': self.AI_ENABLED,
166
+ 'AI_CACHE_TTL': self.AI_CACHE_TTL,
167
+ 'ADMIN_ENABLED': self.ADMIN_ENABLED,
168
+ 'ADMIN_PATH': self.ADMIN_PATH,
169
+ 'AUTH_ENABLED': self.AUTH_ENABLED,
170
+ 'SESSION_TIMEOUT': self.SESSION_TIMEOUT,
171
+ 'PASSWORD_MIN_LENGTH': self.PASSWORD_MIN_LENGTH,
172
+ 'CORS_ORIGINS': self.CORS_ORIGINS,
173
+ }
174
+
175
+ # Global settings instance
176
+ settings = CreatesonlineSettings()
@@ -0,0 +1,23 @@
1
+ """
2
+ CREATESONLINE Internal Data Structures Module
3
+
4
+ Pure Python data manipulation library with zero external dependencies.
5
+ Lightweight replacement for Pandas with AI-native features.
6
+ """
7
+
8
+ from .dataframe import CreatesonlineDataFrame
9
+ from .series import CreatesonlineSeries
10
+ from .io import read_csv, read_json, to_csv, to_json
11
+
12
+ __all__ = [
13
+ 'CreatesonlineDataFrame',
14
+ 'CreatesonlineSeries',
15
+ 'read_csv',
16
+ 'read_json',
17
+ 'to_csv',
18
+ 'to_json'
19
+ ]
20
+
21
+ # Convenience aliases
22
+ DataFrame = CreatesonlineDataFrame
23
+ Series = CreatesonlineSeries