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,322 @@
1
+ # createsonline/testing.py
2
+ """
3
+ CREATESONLINE Testing Module
4
+
5
+ Provides test client and utilities for testing CREATESONLINE applications.
6
+ Pure Python implementation without external dependencies.
7
+ """
8
+
9
+ import json
10
+ import asyncio
11
+ import logging
12
+ from typing import Dict, Any, Union
13
+ from urllib.parse import urlencode, urlparse, parse_qs
14
+
15
+ # Setup logging
16
+ logger = logging.getLogger("createsonline.testing")
17
+
18
+
19
+ class TestResponse:
20
+ """Test response object mimicking HTTP response"""
21
+
22
+ def __init__(self, status_code: int, content: Union[str, bytes, dict], headers: Dict[str, str] = None):
23
+ self.status_code = status_code
24
+ self.headers = headers or {}
25
+
26
+ if isinstance(content, dict):
27
+ self._content = json.dumps(content).encode()
28
+ self.headers.setdefault('content-type', 'application/json')
29
+ elif isinstance(content, str):
30
+ self._content = content.encode()
31
+ self.headers.setdefault('content-type', 'text/plain')
32
+ else:
33
+ self._content = content
34
+
35
+ @property
36
+ def content(self) -> bytes:
37
+ return self._content
38
+
39
+ @property
40
+ def text(self) -> str:
41
+ return self._content.decode()
42
+
43
+ def json(self) -> Dict[str, Any]:
44
+ """Parse response as JSON"""
45
+ try:
46
+ return json.loads(self.text)
47
+ except json.JSONDecodeError:
48
+ raise ValueError("Response is not valid JSON")
49
+
50
+
51
+ class TestClient:
52
+ """
53
+ Test client for CREATESONLINE applications.
54
+ Provides methods to test HTTP endpoints without external dependencies.
55
+ """
56
+
57
+ def __init__(self, app):
58
+ self.app = app
59
+ self.base_url = "http://testserver"
60
+ self._loop = None
61
+
62
+ def _get_loop(self):
63
+ """Get or create event loop for async operations"""
64
+ if self._loop is None or self._loop.is_closed():
65
+ try:
66
+ # Try to get existing loop
67
+ self._loop = asyncio.get_event_loop()
68
+ if self._loop.is_closed():
69
+ raise RuntimeError("Loop is closed")
70
+ except RuntimeError:
71
+ # Create new loop if none exists
72
+ self._loop = asyncio.new_event_loop()
73
+ asyncio.set_event_loop(self._loop)
74
+ return self._loop
75
+
76
+ def _build_request(self, method: str, url: str, **kwargs) -> Dict[str, Any]:
77
+ """Build request object from parameters"""
78
+ # Parse URL
79
+ parsed = urlparse(url if url.startswith('http') else self.base_url + url)
80
+
81
+ request = {
82
+ 'method': method.upper(),
83
+ 'url': url,
84
+ 'path': parsed.path,
85
+ 'query_params': parse_qs(parsed.query),
86
+ 'headers': kwargs.get('headers', {}),
87
+ 'body': None
88
+ }
89
+
90
+ # Handle JSON data
91
+ if 'json' in kwargs:
92
+ request['body'] = json.dumps(kwargs['json'])
93
+ request['headers']['content-type'] = 'application/json'
94
+
95
+ # Handle form data
96
+ elif 'data' in kwargs:
97
+ if isinstance(kwargs['data'], dict):
98
+ request['body'] = urlencode(kwargs['data'])
99
+ request['headers']['content-type'] = 'application/x-www-form-urlencoded'
100
+ else:
101
+ request['body'] = kwargs['data']
102
+
103
+ # Handle files
104
+ elif 'files' in kwargs:
105
+ # Simple file handling (for testing purposes)
106
+ request['body'] = str(kwargs['files'])
107
+ request['headers']['content-type'] = 'multipart/form-data'
108
+
109
+ return request
110
+
111
+ async def _execute_request(self, request: Dict[str, Any]) -> TestResponse:
112
+ """Execute request against the CREATESONLINE app"""
113
+ try:
114
+ # Find matching route in app
115
+ path = request['path']
116
+ method = request['method']
117
+ route_key = f"{method}:{path}"
118
+
119
+ # Check if app has routes (support both internal and full app)
120
+ routes = None
121
+ if hasattr(self.app, 'routes') and isinstance(self.app.routes, dict):
122
+ routes = self.app.routes
123
+ elif hasattr(self.app, '_internal_routes'):
124
+ routes = self.app._internal_routes
125
+
126
+ if routes and route_key in routes:
127
+ handler = routes[route_key]
128
+
129
+ # Create mock request object for handler
130
+ class MockRequest:
131
+ def __init__(self, method, path, query_params, headers, body):
132
+ self.method = method
133
+ self.path = path
134
+ self.query_params = query_params
135
+ self.headers = headers
136
+ self._body = body
137
+
138
+ async def json(self):
139
+ """Async json method for handlers that await request.json()"""
140
+ if self._body and 'json' in self.headers.get('content-type', ''):
141
+ return json.loads(self._body)
142
+ return {}
143
+
144
+ mock_request = MockRequest(
145
+ method, path,
146
+ request.get('query_params', {}),
147
+ request.get('headers', {}),
148
+ request.get('body', '')
149
+ )
150
+
151
+ # Call the handler
152
+ if asyncio.iscoroutinefunction(handler):
153
+ result = await handler(mock_request)
154
+ else:
155
+ result = handler(mock_request)
156
+
157
+ # Handle different response types including (data, status) and (data, status, headers) tuples
158
+ if isinstance(result, tuple):
159
+ if len(result) == 3:
160
+ data, status, headers = result
161
+ return TestResponse(status, data, headers)
162
+ elif len(result) == 2:
163
+ data, status = result
164
+ return TestResponse(status, data)
165
+ elif isinstance(result, dict):
166
+ return TestResponse(200, result)
167
+ elif isinstance(result, (str, bytes)):
168
+ return TestResponse(200, result)
169
+ else:
170
+ return TestResponse(200, str(result))
171
+ else:
172
+ # Try fallback to app's HTTP handler if available
173
+ if hasattr(self.app, '_handle_http'):
174
+ # Parse URL to get query string
175
+ from urllib.parse import urlparse
176
+ parsed = urlparse(request.get('url', path))
177
+ query_string = parsed.query.encode() if parsed.query else b''
178
+
179
+ # Build ASGI scope for internal handler
180
+ scope = {
181
+ 'type': 'http',
182
+ 'method': method,
183
+ 'path': path,
184
+ 'query_string': query_string,
185
+ 'headers': [(k.encode(), v.encode()) for k, v in request.get('headers', {}).items()]
186
+ }
187
+
188
+ # Mock receive function
189
+ async def receive():
190
+ return {
191
+ 'type': 'http.request',
192
+ 'body': request['body'].encode() if request.get('body') else b'',
193
+ 'more_body': False
194
+ }
195
+
196
+ # Mock send function to capture response
197
+ response_data = {'status': 200, 'body': b'', 'headers': []}
198
+
199
+ async def send(message):
200
+ if message['type'] == 'http.response.start':
201
+ response_data['status'] = message['status']
202
+ response_data['headers'] = message.get('headers', [])
203
+ elif message['type'] == 'http.response.body':
204
+ response_data['body'] += message.get('body', b'')
205
+
206
+ # Execute request through app (use __call__ for ASGI)
207
+ await self.app(scope, receive, send)
208
+
209
+ # Convert headers back to dict
210
+ headers = {k.decode(): v.decode() for k, v in response_data['headers']}
211
+
212
+ # Try to parse as JSON, fallback to text
213
+ body = response_data['body']
214
+ if headers.get('content-type', '').startswith('application/json'):
215
+ try:
216
+ content = json.loads(body.decode())
217
+ except:
218
+ content = body.decode()
219
+ else:
220
+ content = body.decode()
221
+
222
+ return TestResponse(response_data['status'], content, headers)
223
+ else:
224
+ return TestResponse(404, {"error": "Not found", "path": path, "method": method})
225
+
226
+ except Exception as e:
227
+ logger.error(f"Test request error: {e}")
228
+ return TestResponse(500, {"error": f"Internal server error: {str(e)}"})
229
+
230
+ def request(self, method: str, url: str, **kwargs) -> TestResponse:
231
+ """Generic request method with proper async handling"""
232
+ request = self._build_request(method, url, **kwargs)
233
+
234
+ try:
235
+ # Check if we're in an async context
236
+ import asyncio
237
+ try:
238
+ current_loop = asyncio.get_running_loop()
239
+ # We're already in a running loop - need to create new loop
240
+ import concurrent.futures
241
+ import threading
242
+
243
+ def run_in_new_loop():
244
+ new_loop = asyncio.new_event_loop()
245
+ asyncio.set_event_loop(new_loop)
246
+ try:
247
+ return new_loop.run_until_complete(self._execute_request(request))
248
+ finally:
249
+ new_loop.close()
250
+
251
+ with concurrent.futures.ThreadPoolExecutor() as executor:
252
+ future = executor.submit(run_in_new_loop)
253
+ return future.result()
254
+
255
+ except RuntimeError:
256
+ # No running loop - we can create our own
257
+ loop = self._get_loop()
258
+ return loop.run_until_complete(self._execute_request(request))
259
+
260
+ except Exception as e:
261
+ logger.error(f"Request execution failed: {e}")
262
+ return TestResponse(500, {"error": f"Test execution failed: {str(e)}"})
263
+
264
+ def get(self, url: str, **kwargs) -> TestResponse:
265
+ """Send GET request"""
266
+ return self.request('GET', url, **kwargs)
267
+
268
+ def post(self, url: str, **kwargs) -> TestResponse:
269
+ """Send POST request"""
270
+ return self.request('POST', url, **kwargs)
271
+
272
+ def put(self, url: str, **kwargs) -> TestResponse:
273
+ """Send PUT request"""
274
+ return self.request('PUT', url, **kwargs)
275
+
276
+ def patch(self, url: str, **kwargs) -> TestResponse:
277
+ """Send PATCH request"""
278
+ return self.request('PATCH', url, **kwargs)
279
+
280
+ def delete(self, url: str, **kwargs) -> TestResponse:
281
+ """Send DELETE request"""
282
+ return self.request('DELETE', url, **kwargs)
283
+
284
+ def options(self, url: str, **kwargs) -> TestResponse:
285
+ """Send OPTIONS request"""
286
+ return self.request('OPTIONS', url, **kwargs)
287
+
288
+ def head(self, url: str, **kwargs) -> TestResponse:
289
+ """Send HEAD request"""
290
+ return self.request('HEAD', url, **kwargs)
291
+
292
+
293
+ # Test utilities
294
+ class TestDatabase:
295
+ """In-memory test database for testing"""
296
+
297
+ def __init__(self):
298
+ from .database import DatabaseConnection
299
+ self.db = DatabaseConnection('sqlite:///:memory:')
300
+
301
+ def reset(self):
302
+ """Reset test database"""
303
+ # Drop all tables and recreate
304
+ cursor = self.db.connection.cursor()
305
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
306
+ tables = cursor.fetchall()
307
+
308
+ for table in tables:
309
+ cursor.execute(f"DROP TABLE {table[0]}")
310
+
311
+ self.db.connection.commit()
312
+ self.db._create_default_tables()
313
+
314
+ def seed_admin_user(self, username: str = "admin", email: str = "admin@test.com", password: str = "admin123"):
315
+ """Create test admin user"""
316
+ return self.db.create_admin_user(username, email, password)
317
+
318
+
319
+ def create_test_app():
320
+ """Create a test CREATESONLINE app"""
321
+ from . import create_app
322
+ return create_app(title="Test App", debug=False)