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,552 @@
1
+ # createsonline/auth/models.py
2
+ """
3
+ CREATESONLINE Authentication Models
4
+
5
+ User, Group, and Permission models for CREATESONLINE applications.
6
+ """
7
+ import sqlalchemy as sa
8
+ from sqlalchemy.ext.declarative import declarative_base
9
+ from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, ForeignKey, Table
10
+ from sqlalchemy.orm import relationship, validates
11
+ from datetime import datetime, timedelta
12
+ import hashlib
13
+ import secrets
14
+ import hmac
15
+ from base64 import b64encode, b64decode
16
+ from typing import Optional, List
17
+ import re
18
+
19
+ # Create base class for CREATESONLINE models
20
+ Base = declarative_base()
21
+
22
+ # Pure Python password hashing functions
23
+ def hash_password(password: str, salt: Optional[bytes] = None) -> str:
24
+ """
25
+ Hash password using PBKDF2 with SHA-256 (pure Python implementation)
26
+
27
+ Args:
28
+ password: Plain text password
29
+ salt: Optional salt bytes, generated if not provided
30
+
31
+ Returns:
32
+ Hashed password string in format: pbkdf2_sha256$iterations$salt$hash
33
+ """
34
+ if salt is None:
35
+ salt = secrets.token_bytes(32)
36
+
37
+ iterations = 100000 # OWASP recommended minimum
38
+
39
+ # Use PBKDF2 with SHA-256
40
+ key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, iterations)
41
+
42
+ # Encode salt and key as base64
43
+ salt_b64 = b64encode(salt).decode('ascii')
44
+ key_b64 = b64encode(key).decode('ascii')
45
+
46
+ return f"pbkdf2_sha256${iterations}${salt_b64}${key_b64}"
47
+
48
+
49
+ def verify_password(password: str, hashed: str) -> bool:
50
+ """
51
+ Verify password against hash
52
+
53
+ Args:
54
+ password: Plain text password to verify
55
+ hashed: Hashed password from database
56
+
57
+ Returns:
58
+ True if password matches
59
+ """
60
+ try:
61
+ # Parse hash format: pbkdf2_sha256$iterations$salt$hash
62
+ parts = hashed.split('$')
63
+ if len(parts) != 4 or parts[0] != 'pbkdf2_sha256':
64
+ return False
65
+
66
+ iterations = int(parts[1])
67
+ salt = b64decode(parts[2].encode('ascii'))
68
+ stored_key = b64decode(parts[3].encode('ascii'))
69
+
70
+ # Hash the provided password with same salt and iterations
71
+ new_key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, iterations)
72
+
73
+ # Use constant-time comparison to prevent timing attacks
74
+ return hmac.compare_digest(stored_key, new_key)
75
+
76
+ except (ValueError, TypeError, IndexError):
77
+ return False
78
+
79
+ # Many-to-many association tables
80
+ user_groups = Table(
81
+ 'createsonline_user_groups',
82
+ Base.metadata,
83
+ Column('user_id', Integer, ForeignKey('createsonline_users.id'), primary_key=True),
84
+ Column('group_id', Integer, ForeignKey('createsonline_groups.id'), primary_key=True)
85
+ )
86
+
87
+ user_permissions = Table(
88
+ 'createsonline_user_permissions',
89
+ Base.metadata,
90
+ Column('user_id', Integer, ForeignKey('createsonline_users.id'), primary_key=True),
91
+ Column('permission_id', Integer, ForeignKey('createsonline_permissions.id'), primary_key=True)
92
+ )
93
+
94
+ group_permissions = Table(
95
+ 'createsonline_group_permissions',
96
+ Base.metadata,
97
+ Column('group_id', Integer, ForeignKey('createsonline_groups.id'), primary_key=True),
98
+ Column('permission_id', Integer, ForeignKey('createsonline_permissions.id'), primary_key=True)
99
+ )
100
+
101
+ class User(Base):
102
+ """
103
+ CREATESONLINE User Model
104
+
105
+ Core user model for authentication and authorization.
106
+ Provides all essential user management features.
107
+ """
108
+ __tablename__ = "createsonline_users"
109
+
110
+ # Primary fields
111
+ id = Column(Integer, primary_key=True, index=True)
112
+ username = Column(String(150), unique=True, nullable=False, index=True)
113
+ email = Column(String(254), unique=True, nullable=False, index=True)
114
+
115
+ # Personal information
116
+ first_name = Column(String(150), nullable=True)
117
+ last_name = Column(String(150), nullable=True)
118
+
119
+ # Authentication
120
+ password_hash = Column(String(128), nullable=False)
121
+
122
+ # Permissions and status
123
+ is_active = Column(Boolean, default=True, nullable=False)
124
+ is_staff = Column(Boolean, default=False, nullable=False)
125
+ is_superuser = Column(Boolean, default=False, nullable=False)
126
+
127
+ # Timestamps
128
+ date_joined = Column(DateTime, default=datetime.utcnow, nullable=False)
129
+ last_login = Column(DateTime, nullable=True)
130
+
131
+ # Profile information
132
+ profile_picture = Column(String(255), nullable=True)
133
+ bio = Column(Text, nullable=True)
134
+
135
+ # Security
136
+ failed_login_attempts = Column(Integer, default=0)
137
+ account_locked_until = Column(DateTime, nullable=True)
138
+ password_reset_token = Column(String(100), nullable=True)
139
+ email_verification_token = Column(String(100), nullable=True)
140
+ email_verified = Column(Boolean, default=False)
141
+
142
+ # Relationships
143
+ groups = relationship(
144
+ "Group",
145
+ secondary=user_groups,
146
+ back_populates="users"
147
+ )
148
+
149
+ user_permissions = relationship(
150
+ "Permission",
151
+ secondary=user_permissions,
152
+ back_populates="users"
153
+ )
154
+
155
+ @validates('username')
156
+ def validate_username(self, key, username):
157
+ """Validate username format"""
158
+ if not username:
159
+ raise ValueError("Username is required")
160
+ if len(username) < 3:
161
+ raise ValueError("Username must be at least 3 characters")
162
+ if not re.match(r'^[a-zA-Z0-9_]+$', username):
163
+ raise ValueError("Username can only contain letters, numbers, and underscores")
164
+ return username
165
+
166
+ @validates('email')
167
+ def validate_email(self, key, email):
168
+ """Validate email format"""
169
+ if not email:
170
+ raise ValueError("Email is required")
171
+ email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
172
+ if not re.match(email_pattern, email):
173
+ raise ValueError("Invalid email format")
174
+ return email.lower()
175
+
176
+ def set_password(self, password: str) -> None:
177
+ """
178
+ Set user password with hashing
179
+
180
+ Args:
181
+ password: Plain text password
182
+ """
183
+ if len(password) < 8:
184
+ raise ValueError("Password must be at least 8 characters")
185
+
186
+ self.password_hash = hash_password(password)
187
+
188
+ def verify_password(self, password: str) -> bool:
189
+ """
190
+ Verify user password
191
+
192
+ Args:
193
+ password: Plain text password to verify
194
+
195
+ Returns:
196
+ True if password is correct
197
+ """
198
+ return verify_password(password, self.password_hash)
199
+
200
+ def check_password(self, password: str) -> bool:
201
+ """Alias for verify_password"""
202
+ return self.verify_password(password)
203
+
204
+ @property
205
+ def full_name(self) -> str:
206
+ """Get user's full name"""
207
+ if self.first_name and self.last_name:
208
+ return f"{self.first_name} {self.last_name}".strip()
209
+ elif self.first_name:
210
+ return self.first_name
211
+ elif self.last_name:
212
+ return self.last_name
213
+ return self.username
214
+
215
+ @property
216
+ def is_authenticated(self) -> bool:
217
+ """Check if user is authenticated (always True for valid user objects)"""
218
+ return True
219
+
220
+ @property
221
+ def is_anonymous(self) -> bool:
222
+ """Check if user is anonymous (always False for user objects)"""
223
+ return False
224
+
225
+ def get_all_permissions(self) -> List[str]:
226
+ """
227
+ Get all permissions for this user (direct + group permissions)
228
+
229
+ Returns:
230
+ List of permission codenames
231
+ """
232
+ permissions = set()
233
+
234
+ # Add direct permissions
235
+ for perm in self.user_permissions:
236
+ permissions.add(f"{perm.content_type}.{perm.codename}")
237
+
238
+ # Add group permissions
239
+ for group in self.groups:
240
+ for perm in group.permissions:
241
+ permissions.add(f"{perm.content_type}.{perm.codename}")
242
+
243
+ return list(permissions)
244
+
245
+ def has_permission(self, permission: str) -> bool:
246
+ """
247
+ Check if user has a specific permission
248
+
249
+ Args:
250
+ permission: Permission string in format "app.codename"
251
+
252
+ Returns:
253
+ True if user has permission
254
+ """
255
+ if self.is_superuser:
256
+ return True
257
+
258
+ return permission in self.get_all_permissions()
259
+
260
+ def has_module_permission(self, app_label: str) -> bool:
261
+ """
262
+ Check if user has any permission for a module/app
263
+
264
+ Args:
265
+ app_label: Application label
266
+
267
+ Returns:
268
+ True if user has any permission for the app
269
+ """
270
+ if self.is_superuser:
271
+ return True
272
+
273
+ permissions = self.get_all_permissions()
274
+ return any(perm.startswith(f"{app_label}.") for perm in permissions)
275
+
276
+ def generate_password_reset_token(self) -> str:
277
+ """Generate a password reset token"""
278
+ token = secrets.token_urlsafe(32)
279
+ self.password_reset_token = token
280
+ return token
281
+
282
+ def generate_email_verification_token(self) -> str:
283
+ """Generate an email verification token"""
284
+ token = secrets.token_urlsafe(32)
285
+ self.email_verification_token = token
286
+ return token
287
+
288
+ def is_account_locked(self) -> bool:
289
+ """Check if account is currently locked"""
290
+ if self.account_locked_until is None:
291
+ return False
292
+ return datetime.utcnow() < self.account_locked_until
293
+
294
+ def lock_account(self, minutes: int = 30) -> None:
295
+ """Lock account for specified minutes"""
296
+ self.account_locked_until = datetime.utcnow() + timedelta(minutes=minutes)
297
+ self.failed_login_attempts = 0
298
+
299
+ def unlock_account(self) -> None:
300
+ """Unlock account"""
301
+ self.account_locked_until = None
302
+ self.failed_login_attempts = 0
303
+
304
+ def record_login_attempt(self, success: bool) -> None:
305
+ """Record login attempt"""
306
+ if success:
307
+ self.failed_login_attempts = 0
308
+ self.last_login = datetime.utcnow()
309
+ self.unlock_account()
310
+ else:
311
+ self.failed_login_attempts += 1
312
+ if self.failed_login_attempts >= 5:
313
+ self.lock_account()
314
+
315
+ def __repr__(self) -> str:
316
+ return f"<User(id={self.id}, username='{self.username}', email='{self.email}')>"
317
+
318
+ def __str__(self) -> str:
319
+ return self.username
320
+
321
+ class Group(Base):
322
+ """
323
+ CREATESONLINE Group Model
324
+
325
+ Groups are a way to categorize users and assign permissions.
326
+ """
327
+ __tablename__ = "createsonline_groups"
328
+
329
+ # Primary fields
330
+ id = Column(Integer, primary_key=True, index=True)
331
+ name = Column(String(150), unique=True, nullable=False, index=True)
332
+ description = Column(Text, nullable=True)
333
+
334
+ # Timestamps
335
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
336
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
337
+
338
+ # Relationships
339
+ users = relationship(
340
+ "User",
341
+ secondary=user_groups,
342
+ back_populates="groups"
343
+ )
344
+
345
+ permissions = relationship(
346
+ "Permission",
347
+ secondary=group_permissions,
348
+ back_populates="groups"
349
+ )
350
+
351
+ @validates('name')
352
+ def validate_name(self, key, name):
353
+ """Validate group name"""
354
+ if not name:
355
+ raise ValueError("Group name is required")
356
+ if len(name) < 2:
357
+ raise ValueError("Group name must be at least 2 characters")
358
+ return name
359
+
360
+ def add_permission(self, permission: 'Permission') -> None:
361
+ """Add permission to group"""
362
+ if permission not in self.permissions:
363
+ self.permissions.append(permission)
364
+
365
+ def remove_permission(self, permission: 'Permission') -> None:
366
+ """Remove permission from group"""
367
+ if permission in self.permissions:
368
+ self.permissions.remove(permission)
369
+
370
+ def has_permission(self, permission_codename: str) -> bool:
371
+ """Check if group has specific permission"""
372
+ return any(
373
+ perm.codename == permission_codename
374
+ for perm in self.permissions
375
+ )
376
+
377
+ def __repr__(self) -> str:
378
+ return f"<Group(id={self.id}, name='{self.name}')>"
379
+
380
+ def __str__(self) -> str:
381
+ return self.name
382
+
383
+ class Permission(Base):
384
+ """
385
+ CREATESONLINE Permission Model
386
+
387
+ Permissions define what actions users can perform.
388
+ """
389
+ __tablename__ = "createsonline_permissions"
390
+
391
+ # Primary fields
392
+ id = Column(Integer, primary_key=True, index=True)
393
+ name = Column(String(255), nullable=False) # Human-readable name
394
+ codename = Column(String(100), nullable=False, index=True) # Machine-readable code
395
+ content_type = Column(String(100), nullable=False, index=True) # App/model name
396
+
397
+ # Timestamps
398
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
399
+
400
+ # Relationships
401
+ users = relationship(
402
+ "User",
403
+ secondary=user_permissions,
404
+ back_populates="user_permissions"
405
+ )
406
+
407
+ groups = relationship(
408
+ "Group",
409
+ secondary=group_permissions,
410
+ back_populates="permissions"
411
+ )
412
+
413
+ # Unique constraint on codename + content_type
414
+ __table_args__ = (
415
+ sa.UniqueConstraint('codename', 'content_type', name='unique_permission'),
416
+ )
417
+
418
+ @validates('codename')
419
+ def validate_codename(self, key, codename):
420
+ """Validate permission codename"""
421
+ if not codename:
422
+ raise ValueError("Permission codename is required")
423
+ if not re.match(r'^[a-z_]+$', codename):
424
+ raise ValueError("Codename can only contain lowercase letters and underscores")
425
+ return codename
426
+
427
+ @validates('content_type')
428
+ def validate_content_type(self, key, content_type):
429
+ """Validate content type"""
430
+ if not content_type:
431
+ raise ValueError("Content type is required")
432
+ return content_type
433
+
434
+ @property
435
+ def natural_key(self) -> str:
436
+ """Get natural key for permission"""
437
+ return f"{self.content_type}.{self.codename}"
438
+
439
+ def __repr__(self) -> str:
440
+ return f"<Permission(id={self.id}, name='{self.name}', codename='{self.codename}')>"
441
+
442
+ def __str__(self) -> str:
443
+ return f"{self.content_type} | {self.name}"
444
+
445
+ # Helper functions for creating default permissions
446
+ def create_default_permissions() -> List[Permission]:
447
+ """Create default CREATESONLINE permissions"""
448
+ default_permissions = [
449
+ # User management
450
+ Permission(
451
+ name="Can add user",
452
+ codename="add_user",
453
+ content_type="auth"
454
+ ),
455
+ Permission(
456
+ name="Can change user",
457
+ codename="change_user",
458
+ content_type="auth"
459
+ ),
460
+ Permission(
461
+ name="Can delete user",
462
+ codename="delete_user",
463
+ content_type="auth"
464
+ ),
465
+ Permission(
466
+ name="Can view user",
467
+ codename="view_user",
468
+ content_type="auth"
469
+ ),
470
+
471
+ # Group management
472
+ Permission(
473
+ name="Can add group",
474
+ codename="add_group",
475
+ content_type="auth"
476
+ ),
477
+ Permission(
478
+ name="Can change group",
479
+ codename="change_group",
480
+ content_type="auth"
481
+ ),
482
+ Permission(
483
+ name="Can delete group",
484
+ codename="delete_group",
485
+ content_type="auth"
486
+ ),
487
+ Permission(
488
+ name="Can view group",
489
+ codename="view_group",
490
+ content_type="auth"
491
+ ),
492
+
493
+ # Admin access
494
+ Permission(
495
+ name="Can access admin",
496
+ codename="access_admin",
497
+ content_type="admin"
498
+ ),
499
+ Permission(
500
+ name="Can view admin dashboard",
501
+ codename="view_dashboard",
502
+ content_type="admin"
503
+ ),
504
+
505
+ # AI features
506
+ Permission(
507
+ name="Can use AI features",
508
+ codename="use_ai",
509
+ content_type="ai"
510
+ ),
511
+ Permission(
512
+ name="Can manage AI models",
513
+ codename="manage_ai_models",
514
+ content_type="ai"
515
+ ),
516
+ ]
517
+
518
+ return default_permissions
519
+
520
+ def create_superuser(
521
+ username: str,
522
+ email: str,
523
+ password: str,
524
+ first_name: str = "",
525
+ last_name: str = ""
526
+ ) -> User:
527
+ """
528
+ Create a CREATESONLINE superuser
529
+
530
+ Args:
531
+ username: Username for superuser
532
+ email: Email for superuser
533
+ password: Password for superuser
534
+ first_name: Optional first name
535
+ last_name: Optional last name
536
+
537
+ Returns:
538
+ Created User instance
539
+ """
540
+ user = User(
541
+ username=username,
542
+ email=email,
543
+ first_name=first_name,
544
+ last_name=last_name,
545
+ is_staff=True,
546
+ is_superuser=True,
547
+ is_active=True,
548
+ email_verified=True
549
+ )
550
+ user.set_password(password)
551
+
552
+ return user
@@ -0,0 +1,5 @@
1
+ # createsonline/cli/__init__.py
2
+ from .main import app as cli_app
3
+
4
+ __all__ = ['cli_app']
5
+
@@ -0,0 +1,122 @@
1
+ import logging`n_logger = logging.getLogger("createsonline.cli.commands")`n# createsonline/cli/commands/__init__.py
2
+ """
3
+ CREATESONLINE CLI Commands
4
+
5
+ Individual command modules for the CREATESONLINE CLI.
6
+ FIXED: Lazy imports to prevent dependency crashes when optional packages missing
7
+ """
8
+
9
+ # Lazy import pattern - don't import commands eagerly
10
+ # This prevents ImportError if rich/typer/uvicorn are missing
11
+ __all__ = [
12
+ "serve_command",
13
+ "dev_command",
14
+ "prod_command",
15
+ "info_command",
16
+ "version_command",
17
+ "new_command",
18
+ "shell_command",
19
+ "createsuperuser_command",
20
+ "db_ai_query_command",
21
+ "db_rollback_command",
22
+ "db_audit_log_command"
23
+ ]
24
+
25
+ def _lazy_import_serve():
26
+ """Lazy import serve commands"""
27
+ try:
28
+ from .serve import serve_command, dev_command, prod_command
29
+ return serve_command, dev_command, prod_command
30
+ except ImportError as e:
31
+ def _missing_command(*args, **kwargs):
32
+ _logger.error(f"Command requires additional dependencies: {e}")
33
+ _logger.info("Install with: pip install uvicorn")
34
+ return
35
+ return _missing_command, _missing_command, _missing_command
36
+
37
+ def _lazy_import_info():
38
+ """Lazy import info commands"""
39
+ try:
40
+ from .info import info_command, version_command
41
+ return info_command, version_command
42
+ except ImportError as e:
43
+ def _missing_command(*args, **kwargs):
44
+ _logger.error(f"Command requires additional dependencies: {e}")
45
+ _logger.info("Install with: pip install rich")
46
+ return
47
+ return _missing_command, _missing_command
48
+
49
+ def _lazy_import_project():
50
+ """Lazy import project commands"""
51
+ try:
52
+ from .project import new_command
53
+ return new_command
54
+ except ImportError as e:
55
+ def _missing_command(*args, **kwargs):
56
+ _logger.error(f"Command requires additional dependencies: {e}")
57
+ _logger.info("Install with: pip install rich typer")
58
+ return
59
+ return _missing_command
60
+
61
+ def _lazy_import_shell():
62
+ """Lazy import shell commands"""
63
+ try:
64
+ from .shell import shell_command
65
+ return shell_command
66
+ except ImportError as e:
67
+ def _missing_command(*args, **kwargs):
68
+ _logger.error(f"Command requires additional dependencies: {e}")
69
+ _logger.info("Install with: pip install rich")
70
+ return
71
+ return _missing_command
72
+
73
+ def _lazy_import_users():
74
+ """Lazy import user commands"""
75
+ try:
76
+ from .users import createsuperuser_command
77
+ return createsuperuser_command
78
+ except ImportError as e:
79
+ def _missing_command(*args, **kwargs):
80
+ _logger.error(f"Command requires additional dependencies: {e}")
81
+ _logger.info("Install with: pip install rich typer")
82
+ return
83
+ return _missing_command
84
+
85
+ def _lazy_import_database():
86
+ """Lazy import database commands"""
87
+ try:
88
+ from .database import db_ai_query_command, db_rollback_command, db_audit_log_command
89
+ return db_ai_query_command, db_rollback_command, db_audit_log_command
90
+ except ImportError as e:
91
+ def _missing_command(*args, **kwargs):
92
+ _logger.error(f"Database commands require additional dependencies: {e}")
93
+ _logger.info("Install with: pip install rich")
94
+ return
95
+ return _missing_command, _missing_command, _missing_command
96
+
97
+ # Lazy loading attributes
98
+ def __getattr__(name):
99
+ if name == "serve_command":
100
+ return _lazy_import_serve()[0]
101
+ elif name == "dev_command":
102
+ return _lazy_import_serve()[1]
103
+ elif name == "prod_command":
104
+ return _lazy_import_serve()[2]
105
+ elif name == "info_command":
106
+ return _lazy_import_info()[0]
107
+ elif name == "version_command":
108
+ return _lazy_import_info()[1]
109
+ elif name == "new_command":
110
+ return _lazy_import_project()
111
+ elif name == "shell_command":
112
+ return _lazy_import_shell()
113
+ elif name == "createsuperuser_command":
114
+ return _lazy_import_users()
115
+ elif name == "db_ai_query_command":
116
+ return _lazy_import_database()[0]
117
+ elif name == "db_rollback_command":
118
+ return _lazy_import_database()[1]
119
+ elif name == "db_audit_log_command":
120
+ return _lazy_import_database()[2]
121
+ else:
122
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")