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,440 @@
1
+ """
2
+ CREATESONLINE Database Abstraction Layer
3
+
4
+ Pure Python database abstraction that wraps SQLAlchemy with a clean API.
5
+ """
6
+
7
+ import os
8
+ import logging
9
+ from typing import Any, Dict, List, Optional, Union, Type
10
+ from contextlib import contextmanager
11
+
12
+ # Setup logger
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class DatabaseError(Exception):
17
+ """Custom database error for better error handling"""
18
+ pass
19
+
20
+ # Try to import SQLAlchemy, fallback to internal implementation if not available
21
+ try:
22
+ from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, Text, Boolean, DateTime, Float, JSON, ForeignKey
23
+ from sqlalchemy.orm import sessionmaker, declarative_base, Session
24
+ from sqlalchemy.exc import SQLAlchemyError
25
+ from sqlalchemy.pool import StaticPool
26
+ SQLALCHEMY_AVAILABLE = True
27
+ except ImportError:
28
+ SQLALCHEMY_AVAILABLE = False
29
+ # Create placeholder classes
30
+ class Session: pass
31
+ class SQLAlchemyError(Exception): pass
32
+
33
+
34
+ class Database:
35
+ """
36
+ CREATESONLINE Database Abstraction
37
+
38
+ Provides a clean, simple API that wraps SQLAlchemy complexity.
39
+ Falls back to internal database implementation if SQLAlchemy is not available.
40
+ """
41
+
42
+ _instance = None # Singleton instance
43
+
44
+ def __init__(
45
+ self,
46
+ database_url: Optional[str] = None,
47
+ echo: bool = False,
48
+ pool_size: int = 5,
49
+ max_overflow: int = 10
50
+ ):
51
+ """
52
+ Initialize database
53
+
54
+ Args:
55
+ database_url: Database connection URL
56
+ echo: Enable SQL query logging
57
+ pool_size: Connection pool size
58
+ max_overflow: Maximum overflow connections
59
+ """
60
+ self.database_url = database_url or os.getenv('DATABASE_URL', 'sqlite:///createsonline.db')
61
+ self.echo = echo or os.getenv('DATABASE_ECHO', 'false').lower() == 'true'
62
+
63
+ if SQLALCHEMY_AVAILABLE:
64
+ self._setup_sqlalchemy(pool_size, max_overflow)
65
+ else:
66
+ self._setup_fallback()
67
+
68
+ @classmethod
69
+ def get_instance(cls) -> 'Database':
70
+ """Get singleton database instance"""
71
+ if cls._instance is None:
72
+ cls._instance = cls()
73
+ return cls._instance
74
+
75
+ @classmethod
76
+ def set_instance(cls, instance: 'Database'):
77
+ """Set singleton database instance"""
78
+ cls._instance = instance
79
+
80
+ def _setup_sqlalchemy(self, pool_size: int, max_overflow: int):
81
+ """Setup SQLAlchemy engine and session"""
82
+ try:
83
+ # Configure engine based on database type
84
+ if self.database_url.startswith('sqlite'):
85
+ # SQLite configuration
86
+ engine_kwargs = {
87
+ 'echo': self.echo,
88
+ 'poolclass': StaticPool,
89
+ 'connect_args': {'check_same_thread': False}
90
+ }
91
+ else:
92
+ # PostgreSQL/MySQL configuration
93
+ engine_kwargs = {
94
+ 'echo': self.echo,
95
+ 'pool_size': pool_size,
96
+ 'max_overflow': max_overflow
97
+ }
98
+
99
+ self.engine = create_engine(self.database_url, **engine_kwargs)
100
+ self.metadata = MetaData()
101
+
102
+ # Create session factory
103
+ self.SessionLocal = sessionmaker(
104
+ autocommit=False,
105
+ autoflush=False,
106
+ bind=self.engine
107
+ )
108
+
109
+ # Create declarative base
110
+ self.Base = declarative_base(metadata=self.metadata)
111
+
112
+ self.mode = 'sqlalchemy'
113
+ logger.info("✅ CREATESONLINE: Database initialized with SQLAlchemy")
114
+
115
+ except SQLAlchemyError as e:
116
+ logger.warning(f"SQLAlchemy error: {e}")
117
+ self._setup_fallback()
118
+
119
+ def _setup_fallback(self):
120
+ """Setup fallback database implementation"""
121
+ # Import the existing database implementation
122
+ from .. import database as legacy_db
123
+
124
+ self.legacy_db = legacy_db.DatabaseConnection(self.database_url)
125
+ self.mode = 'legacy'
126
+ logger.info("✅ CREATESONLINE: Database initialized with legacy implementation")
127
+
128
+ def get_session(self) -> Union[Session, 'LegacySession']:
129
+ """Get database session"""
130
+ if self.mode == 'sqlalchemy':
131
+ return self.SessionLocal()
132
+ else:
133
+ return LegacySession(self.legacy_db)
134
+
135
+ @contextmanager
136
+ def session_scope(self):
137
+ """Context manager for database sessions"""
138
+ session = self.get_session()
139
+ try:
140
+ yield session
141
+ if hasattr(session, 'commit'):
142
+ session.commit()
143
+ except Exception as e:
144
+ if hasattr(session, 'rollback'):
145
+ session.rollback()
146
+ raise
147
+ finally:
148
+ if hasattr(session, 'close'):
149
+ session.close()
150
+
151
+ def execute_raw(self, query: str, params: tuple = ()) -> List[Dict[str, Any]]:
152
+ """Execute raw SQL query with proper error handling"""
153
+ try:
154
+ if self.mode == 'sqlalchemy':
155
+ with self.session_scope() as session:
156
+ result = session.execute(query, params)
157
+ if query.strip().upper().startswith('SELECT'):
158
+ return [dict(row) for row in result.fetchall()]
159
+ else:
160
+ return [{'affected_rows': result.rowcount}]
161
+ else:
162
+ return self.legacy_db.execute(query, params)
163
+
164
+ except SQLAlchemyError as e:
165
+ logger.error(f"Database query failed: {query[:100]}... Error: {e}")
166
+ raise DatabaseError(f"Query execution failed: {str(e)}") from e
167
+ except Exception as e:
168
+ logger.error(f"Unexpected error executing query: {query[:100]}... Error: {e}")
169
+ raise DatabaseError(f"Unexpected database error: {str(e)}") from e
170
+
171
+ def create_tables(self, models: List[Type[Any]] = None):
172
+ """Create database tables"""
173
+ if self.mode == 'sqlalchemy':
174
+ if models:
175
+ # Create tables for specific models
176
+ for model in models:
177
+ model.metadata.create_all(bind=self.engine)
178
+ else:
179
+ # Create all tables
180
+ self.Base.metadata.create_all(bind=self.engine)
181
+ else:
182
+ # Legacy implementation already creates tables
183
+ pass
184
+
185
+ def drop_tables(self, models: List[Type[Any]] = None):
186
+ """Drop database tables"""
187
+ if self.mode == 'sqlalchemy':
188
+ if models:
189
+ for model in models:
190
+ model.metadata.drop_all(bind=self.engine)
191
+ else:
192
+ self.Base.metadata.drop_all(bind=self.engine)
193
+ else:
194
+ # For legacy, would need to implement table dropping
195
+ logger.warning("Table dropping not implemented in legacy mode")
196
+
197
+ def get_table_names(self) -> List[str]:
198
+ """Get list of table names"""
199
+ if self.mode == 'sqlalchemy':
200
+ self.metadata.reflect(bind=self.engine)
201
+ return list(self.metadata.tables.keys())
202
+ else:
203
+ # For SQLite legacy implementation
204
+ result = self.legacy_db.execute(
205
+ "SELECT name FROM sqlite_master WHERE type='table'"
206
+ )
207
+ return [row['name'] for row in result]
208
+
209
+ def table_exists(self, table_name: str) -> bool:
210
+ """Check if table exists"""
211
+ return table_name in self.get_table_names()
212
+
213
+ def get_connection_info(self) -> Dict[str, Any]:
214
+ """Get database connection information"""
215
+ return {
216
+ 'database_url': self.database_url,
217
+ 'mode': self.mode,
218
+ 'sqlalchemy_available': SQLALCHEMY_AVAILABLE,
219
+ 'tables': self.get_table_names()
220
+ }
221
+
222
+
223
+ class Connection:
224
+ """Database connection wrapper"""
225
+
226
+ def __init__(self, database: Database):
227
+ self.database = database
228
+ self.session = database.get_session()
229
+
230
+ def execute(self, query: str, params: tuple = ()) -> List[Dict[str, Any]]:
231
+ """Execute SQL query"""
232
+ return self.database.execute_raw(query, params)
233
+
234
+ def commit(self):
235
+ """Commit transaction"""
236
+ if hasattr(self.session, 'commit'):
237
+ self.session.commit()
238
+
239
+ def rollback(self):
240
+ """Rollback transaction"""
241
+ if hasattr(self.session, 'rollback'):
242
+ self.session.rollback()
243
+
244
+ def close(self):
245
+ """Close connection"""
246
+ if hasattr(self.session, 'close'):
247
+ self.session.close()
248
+
249
+ def __enter__(self):
250
+ return self
251
+
252
+ def __exit__(self, exc_type, exc_val, exc_tb):
253
+ if exc_type:
254
+ self.rollback()
255
+ else:
256
+ self.commit()
257
+ self.close()
258
+
259
+
260
+ class Transaction:
261
+ """Database transaction wrapper"""
262
+
263
+ def __init__(self, database: Database):
264
+ self.database = database
265
+ self.session = None
266
+
267
+ def __enter__(self):
268
+ self.session = self.database.get_session()
269
+ if hasattr(self.session, 'begin'):
270
+ self.transaction = self.session.begin()
271
+ return self.session
272
+
273
+ def __exit__(self, exc_type, exc_val, exc_tb):
274
+ if exc_type:
275
+ if hasattr(self.session, 'rollback'):
276
+ self.session.rollback()
277
+ else:
278
+ if hasattr(self.session, 'commit'):
279
+ self.session.commit()
280
+
281
+ if hasattr(self.session, 'close'):
282
+ self.session.close()
283
+
284
+
285
+ class LegacySession:
286
+ """Wrapper for legacy database implementation to provide session-like interface"""
287
+
288
+ def __init__(self, legacy_db):
289
+ self.legacy_db = legacy_db
290
+ self._changes = []
291
+ self._dirty = False
292
+
293
+ def execute(self, query: str, params: tuple = ()):
294
+ """Execute query"""
295
+ return self.legacy_db.execute(query, params)
296
+
297
+ def add(self, instance):
298
+ """Add instance with proper change tracking"""
299
+ self._changes.append(('add', instance))
300
+ self._dirty = True
301
+
302
+ def delete(self, instance):
303
+ """Delete instance with proper change tracking"""
304
+ self._changes.append(('delete', instance))
305
+ self._dirty = True
306
+
307
+ def commit(self):
308
+ """Commit changes - flush pending operations"""
309
+ if not self._dirty:
310
+ return
311
+
312
+
313
+ try:
314
+ for operation, instance in self._changes:
315
+ if operation == 'add':
316
+ self._flush_add(instance)
317
+ elif operation == 'delete':
318
+ self._flush_delete(instance)
319
+
320
+ self._changes.clear()
321
+ self._dirty = False
322
+
323
+ except Exception as e:
324
+ logger.error(f"Failed to commit changes: {e}")
325
+ raise
326
+
327
+ def _flush_add(self, instance):
328
+ """Flush add operation to database"""
329
+ # This is a simplified implementation
330
+ # In a real ORM, this would use the instance's metadata
331
+ table_name = getattr(instance.__class__, '__tablename__', 'unknown')
332
+
333
+ # Extract data from instance
334
+ data = {}
335
+ for attr_name in dir(instance):
336
+ if not attr_name.startswith('_') and not callable(getattr(instance, attr_name)):
337
+ value = getattr(instance, attr_name)
338
+ if value is not None:
339
+ data[attr_name] = value
340
+
341
+ if data:
342
+ result = self.legacy_db.insert(table_name, data)
343
+
344
+ def _flush_delete(self, instance):
345
+ """Flush delete operation to database"""
346
+ table_name = getattr(instance.__class__, '__tablename__', 'unknown')
347
+
348
+ # Use ID if available
349
+ if hasattr(instance, 'id') and instance.id:
350
+ result = self.legacy_db.delete(table_name, {'id': instance.id})
351
+
352
+ def rollback(self):
353
+ """Rollback changes"""
354
+ self._changes.clear()
355
+ self._dirty = False
356
+
357
+ def close(self):
358
+ """Close session"""
359
+ if self._dirty:
360
+ logger.warning("Closing session with uncommitted changes")
361
+ self._changes.clear()
362
+
363
+ def query(self, model_class):
364
+ """Create query (basic implementation)"""
365
+ return LegacyQuery(self.legacy_db, model_class)
366
+
367
+
368
+ class LegacyQuery:
369
+ """Legacy query implementation for compatibility"""
370
+
371
+ def __init__(self, legacy_db, model_class):
372
+ self.legacy_db = legacy_db
373
+ self.model_class = model_class
374
+ self._filters = []
375
+ self._limit = None
376
+ self._offset = None
377
+ self._order_by = []
378
+
379
+ def filter(self, *criteria):
380
+ """Add filter criteria"""
381
+ self._filters.extend(criteria)
382
+ return self
383
+
384
+ def filter_by(self, **kwargs):
385
+ """Add filter by keyword arguments"""
386
+ for key, value in kwargs.items():
387
+ self._filters.append(f"{key} = ?")
388
+ return self
389
+
390
+ def limit(self, limit_value):
391
+ """Add limit"""
392
+ self._limit = limit_value
393
+ return self
394
+
395
+ def offset(self, offset_value):
396
+ """Add offset"""
397
+ self._offset = offset_value
398
+ return self
399
+
400
+ def order_by(self, *columns):
401
+ """Add order by"""
402
+ self._order_by.extend(columns)
403
+ return self
404
+
405
+ def first(self):
406
+ """Get first result"""
407
+ results = self.limit(1).all()
408
+ return results[0] if results else None
409
+
410
+ def all(self):
411
+ """Get all results"""
412
+ # This is a simplified implementation
413
+ # In practice, would need to parse model class and build proper query
414
+ table_name = getattr(self.model_class, '__tablename__', 'unknown')
415
+ query = f"SELECT * FROM {table_name}"
416
+
417
+ if self._filters:
418
+ query += " WHERE " + " AND ".join(self._filters)
419
+
420
+ if self._order_by:
421
+ query += " ORDER BY " + ", ".join(str(col) for col in self._order_by)
422
+
423
+ if self._limit:
424
+ query += f" LIMIT {self._limit}"
425
+
426
+ if self._offset:
427
+ query += f" OFFSET {self._offset}"
428
+
429
+ return self.legacy_db.execute(query)
430
+
431
+ def count(self):
432
+ """Count results"""
433
+ table_name = getattr(self.model_class, '__tablename__', 'unknown')
434
+ query = f"SELECT COUNT(*) as count FROM {table_name}"
435
+
436
+ if self._filters:
437
+ query += " WHERE " + " AND ".join(self._filters)
438
+
439
+ result = self.legacy_db.execute(query)
440
+ return result[0]['count'] if result else 0