django-cfg 1.3.5__py3-none-any.whl → 1.3.9__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 (252) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/apps/urls.py +1 -2
  97. django_cfg/config.py +1 -1
  98. django_cfg/core/config.py +10 -5
  99. django_cfg/core/generation.py +1 -1
  100. django_cfg/management/commands/__init__.py +13 -1
  101. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  102. django_cfg/management/commands/app_agent_generate.py +342 -0
  103. django_cfg/management/commands/app_agent_info.py +308 -0
  104. django_cfg/management/commands/migrate_all.py +9 -3
  105. django_cfg/management/commands/migrator.py +11 -6
  106. django_cfg/management/commands/rundramatiq.py +3 -2
  107. django_cfg/middleware/__init__.py +0 -2
  108. django_cfg/models/api_keys.py +115 -0
  109. django_cfg/modules/django_admin/__init__.py +64 -0
  110. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  111. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  112. django_cfg/modules/django_admin/decorators/display.py +106 -0
  113. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  114. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  115. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  116. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  117. django_cfg/modules/django_admin/models/__init__.py +20 -0
  118. django_cfg/modules/django_admin/models/action_models.py +33 -0
  119. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  120. django_cfg/modules/django_admin/models/base.py +26 -0
  121. django_cfg/modules/django_admin/models/display_models.py +31 -0
  122. django_cfg/modules/django_admin/utils/badges.py +159 -0
  123. django_cfg/modules/django_admin/utils/displays.py +247 -0
  124. django_cfg/modules/django_app_agent/__init__.py +87 -0
  125. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  126. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  127. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  128. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  129. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  130. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  135. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  136. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  137. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  138. django_cfg/modules/django_app_agent/core/config.py +300 -0
  139. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  140. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  141. django_cfg/modules/django_app_agent/models/base.py +283 -0
  142. django_cfg/modules/django_app_agent/models/context.py +496 -0
  143. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  144. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  145. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  146. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  147. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  153. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  154. django_cfg/modules/django_app_agent/services/base.py +437 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  160. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  165. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  171. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  172. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  188. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  195. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  196. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  197. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  198. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  199. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  200. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  201. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  202. django_cfg/modules/django_currency/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  204. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  205. django_cfg/modules/django_currency/core/converter.py +12 -12
  206. django_cfg/modules/django_currency/database/__init__.py +2 -2
  207. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  208. django_cfg/modules/django_llm/llm/client.py +10 -2
  209. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  210. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  211. django_cfg/modules/django_unfold/dashboard.py +14 -13
  212. django_cfg/modules/django_unfold/models/config.py +1 -1
  213. django_cfg/registry/core.py +3 -0
  214. django_cfg/registry/third_party.py +2 -2
  215. django_cfg/template_archive/django_sample.zip +0 -0
  216. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  217. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/RECORD +224 -118
  218. django_cfg/apps/accounts/admin/activity.py +0 -96
  219. django_cfg/apps/accounts/admin/group.py +0 -17
  220. django_cfg/apps/accounts/admin/otp.py +0 -59
  221. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  222. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  223. django_cfg/apps/accounts/admin/user.py +0 -300
  224. django_cfg/apps/agents/core/agent.py +0 -281
  225. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  226. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  227. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  228. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  229. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  230. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  231. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  232. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  236. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  237. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  239. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  242. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  243. django_cfg/apps/tasks/admin.py +0 -320
  244. django_cfg/middleware/static_nocache.py +0 -55
  245. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  249. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  250. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  251. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  252. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -125,6 +125,11 @@ class Command(BaseCommand):
125
125
  except Exception as e:
126
126
  self.stdout.write(self.style.WARNING(f"⚠️ Warning creating migrations: {e}"))
127
127
 
128
+ def _raise_system_exit(self, message):
129
+ self.stdout.write(self.style.ERROR(f"❌ {message}"))
130
+ logger.error(message)
131
+ # raise SystemExit(1)
132
+
128
133
  def migrate_database(self, db_name):
129
134
  """Migrate specific database"""
130
135
  try:
@@ -154,12 +159,12 @@ class Command(BaseCommand):
154
159
  self.stdout.write(f" 📦 Migrating {app}...")
155
160
  call_command("migrate", app, database=db_name, verbosity=1)
156
161
  except Exception as e:
157
- self.stdout.write(self.style.WARNING(f" ⚠️ Warning migrating {app}: {e}"))
162
+ self._raise_system_exit(f"Migration failed for {app} on {db_name}: {e}")
158
163
 
159
164
  self.stdout.write(self.style.SUCCESS(f"✅ {db_name} migration completed!"))
160
165
 
161
166
  except Exception as e:
162
- self.stdout.write(self.style.ERROR(f"Error migrating {db_name}: {e}"))
167
+ self._raise_system_exit(f"Error migrating {db_name}: {e}")
163
168
 
164
169
  def migrate_constance_if_needed(self):
165
170
  """Always migrate constance app if it's installed"""
@@ -173,12 +178,12 @@ class Command(BaseCommand):
173
178
  call_command("migrate", "constance", database="default", verbosity=1)
174
179
  self.stdout.write(self.style.SUCCESS("✅ Constance migration completed!"))
175
180
  except Exception as e:
176
- self.stdout.write(self.style.WARNING(f"⚠️ Constance migration warning: {e}"))
181
+ self._raise_system_exit(f"Constance migration failed: {e}")
177
182
  else:
178
183
  self.stdout.write(self.style.WARNING("⚠️ Constance not found in INSTALLED_APPS"))
179
184
 
180
185
  except Exception as e:
181
- self.stdout.write(self.style.WARNING(f"⚠️ Could not migrate constance: {e}"))
186
+ self._raise_system_exit(f"Could not migrate constance: {e}")
182
187
 
183
188
  def migrate_app(self, app_name):
184
189
  """Migrate specific app across all databases"""
@@ -192,7 +197,7 @@ class Command(BaseCommand):
192
197
  try:
193
198
  call_command("migrate", app_name, database=db_name, verbosity=1)
194
199
  except Exception as e:
195
- self.stdout.write(self.style.WARNING(f" ⚠️ Warning: {e}"))
200
+ self._raise_system_exit(f"Migration failed for {app_name} on {db_name}: {e}")
196
201
 
197
202
  def show_database_status(self):
198
203
  """Show status of all databases and their apps"""
@@ -265,7 +270,7 @@ class Command(BaseCommand):
265
270
  self.stdout.write(f"📦 Installed Apps: {len(settings.INSTALLED_APPS)}")
266
271
 
267
272
  except Exception as e:
268
- self.stdout.write(self.style.ERROR(f"Error getting Django config info: {e}"))
273
+ self._raise_system_exit(f"Error getting Django config info: {e}")
269
274
 
270
275
  def get_apps_for_database(self, db_name: str):
271
276
  """Get apps for specific database with smart logic for default"""
@@ -16,8 +16,6 @@ from django.conf import settings
16
16
  from django.core.management.base import BaseCommand
17
17
  from django.utils.module_loading import module_has_submodule
18
18
  from django_cfg.modules.django_logger import get_logger
19
-
20
-
21
19
  from django_cfg.modules.django_tasks import get_task_service
22
20
 
23
21
 
@@ -111,8 +109,10 @@ class Command(BaseCommand):
111
109
  # If dry run, show command and exit
112
110
  if dry_run:
113
111
  executable_name = "dramatiq"
112
+
114
113
  process_args = [
115
114
  executable_name,
115
+ "django_cfg.modules.dramatiq_setup", # Broker module
116
116
  "--processes", str(processes),
117
117
  "--threads", str(threads),
118
118
  "--worker-shutdown-timeout", str(worker_shutdown_timeout),
@@ -150,6 +150,7 @@ class Command(BaseCommand):
150
150
  # Build process arguments exactly like django_dramatiq
151
151
  process_args = [
152
152
  executable_name,
153
+ "django_cfg.modules.dramatiq_setup", # Broker module
153
154
  "--processes", str(processes),
154
155
  "--threads", str(threads),
155
156
  "--worker-shutdown-timeout", str(worker_shutdown_timeout),
@@ -5,9 +5,7 @@ Provides middleware components for Django CFG applications.
5
5
  """
6
6
 
7
7
  from .user_activity import UserActivityMiddleware
8
- from .static_nocache import StaticNoCacheMiddleware
9
8
 
10
9
  __all__ = [
11
10
  'UserActivityMiddleware',
12
- 'StaticNoCacheMiddleware',
13
11
  ]
@@ -0,0 +1,115 @@
1
+ """
2
+ API Keys configuration models for django-cfg.
3
+
4
+ Simple model for OpenAI and OpenRouter API keys.
5
+ Following CRITICAL_REQUIREMENTS.md:
6
+ - No raw Dict/Any usage - everything through Pydantic models
7
+ - Proper type annotations for all fields
8
+ - No mutable default arguments
9
+ """
10
+
11
+ from typing import Optional
12
+ from pydantic import BaseModel, Field, field_validator, SecretStr
13
+
14
+
15
+ class ApiKeys(BaseModel):
16
+ """
17
+ API keys configuration for LLM services.
18
+
19
+ Simple model for storing OpenAI and OpenRouter API keys.
20
+
21
+ Example:
22
+ ```python
23
+ api_keys = ApiKeys(
24
+ openai="${OPENAI_API_KEY}",
25
+ openrouter="${OPENROUTER_API_KEY}"
26
+ )
27
+ ```
28
+ """
29
+
30
+ model_config = {
31
+ "validate_assignment": True,
32
+ "extra": "forbid",
33
+ "str_strip_whitespace": True,
34
+ "validate_default": True,
35
+ }
36
+
37
+ # === LLM Provider Keys ===
38
+ openai: Optional[SecretStr] = Field(
39
+ default=None,
40
+ description="OpenAI API key for GPT models and embeddings"
41
+ )
42
+
43
+ openrouter: Optional[SecretStr] = Field(
44
+ default=None,
45
+ description="OpenRouter API key for access to multiple LLM providers"
46
+ )
47
+
48
+ @field_validator("openai")
49
+ @classmethod
50
+ def validate_openai_key(cls, v: Optional[SecretStr]) -> Optional[SecretStr]:
51
+ """Validate OpenAI API key format."""
52
+ if v is None:
53
+ return v
54
+
55
+ key_str = v.get_secret_value()
56
+ if not key_str.startswith(("sk-", "sk-proj-")):
57
+ raise ValueError("OpenAI API key must start with 'sk-' or 'sk-proj-'")
58
+
59
+ if len(key_str) < 20:
60
+ raise ValueError("OpenAI API key appears to be too short")
61
+
62
+ return v
63
+
64
+ @field_validator("openrouter")
65
+ @classmethod
66
+ def validate_openrouter_key(cls, v: Optional[SecretStr]) -> Optional[SecretStr]:
67
+ """Validate OpenRouter API key format."""
68
+ if v is None:
69
+ return v
70
+
71
+ key_str = v.get_secret_value()
72
+ if not key_str.startswith(("sk-or-", "sk-proj-")):
73
+ raise ValueError("OpenRouter API key must start with 'sk-or-' or 'sk-proj-'")
74
+
75
+ if len(key_str) < 20:
76
+ raise ValueError("OpenRouter API key appears to be too short")
77
+
78
+ return v
79
+
80
+ def get_openai_key(self) -> Optional[str]:
81
+ """Get OpenAI API key as string."""
82
+ return self.openai.get_secret_value() if self.openai else None
83
+
84
+ def get_openrouter_key(self) -> Optional[str]:
85
+ """Get OpenRouter API key as string."""
86
+ return self.openrouter.get_secret_value() if self.openrouter else None
87
+
88
+ def has_openai(self) -> bool:
89
+ """Check if OpenAI key is configured."""
90
+ return self.openai is not None
91
+
92
+ def has_openrouter(self) -> bool:
93
+ """Check if OpenRouter key is configured."""
94
+ return self.openrouter is not None
95
+
96
+ def get_preferred_provider(self) -> Optional[str]:
97
+ """
98
+ Get preferred provider based on availability.
99
+
100
+ Priority: OpenRouter (default) > OpenAI
101
+
102
+ Returns:
103
+ "openrouter" or "openai" or None
104
+ """
105
+ if self.has_openrouter():
106
+ return "openrouter"
107
+ elif self.has_openai():
108
+ return "openai"
109
+ return None
110
+
111
+
112
+ # Export the main class
113
+ __all__ = [
114
+ "ApiKeys",
115
+ ]
@@ -0,0 +1,64 @@
1
+ """
2
+ Django Admin Utilities - Universal HTML Builder System
3
+
4
+ Clean, type-safe admin utilities with no HTML duplication.
5
+ """
6
+
7
+ # Core utilities
8
+ from .utils.displays import UserDisplay, MoneyDisplay, StatusDisplay, DateTimeDisplay
9
+ from .utils.badges import StatusBadge, ProgressBadge, CounterBadge
10
+
11
+ # Icons
12
+ from .icons import Icons, IconCategories
13
+
14
+ # Admin mixins
15
+ from .mixins.display_mixin import DisplayMixin
16
+ from .mixins.optimization_mixin import OptimizedModelAdmin
17
+ from .mixins.standalone_actions_mixin import StandaloneActionsMixin, standalone_action
18
+
19
+ # Configuration models
20
+ from .models.display_models import UserDisplayConfig, MoneyDisplayConfig, DateTimeDisplayConfig
21
+ from .models.badge_models import BadgeConfig, BadgeVariant, StatusBadgeConfig
22
+ from .models.action_models import ActionVariant, ActionConfig
23
+
24
+ # Decorators
25
+ from .decorators import display, action
26
+
27
+ __version__ = "1.0.0"
28
+
29
+ __all__ = [
30
+ # Display utilities
31
+ "UserDisplay",
32
+ "MoneyDisplay",
33
+ "StatusDisplay",
34
+ "DateTimeDisplay",
35
+
36
+ # Badge utilities
37
+ "StatusBadge",
38
+ "ProgressBadge",
39
+ "CounterBadge",
40
+
41
+ # Icons
42
+ "Icons",
43
+ "IconCategories",
44
+
45
+ # Admin mixins
46
+ "OptimizedModelAdmin",
47
+ "DisplayMixin",
48
+ "StandaloneActionsMixin",
49
+ "standalone_action",
50
+
51
+ # Configuration models
52
+ "UserDisplayConfig",
53
+ "MoneyDisplayConfig",
54
+ "DateTimeDisplayConfig",
55
+ "BadgeConfig",
56
+ "BadgeVariant",
57
+ "StatusBadgeConfig",
58
+ "ActionVariant",
59
+ "ActionConfig",
60
+
61
+ # Decorators
62
+ "display",
63
+ "action",
64
+ ]
@@ -0,0 +1,13 @@
1
+ """
2
+ Django Admin Decorators - Wrappers for Unfold decorators.
3
+
4
+ Provides consistent, type-safe decorators with our admin utilities integration.
5
+ """
6
+
7
+ from .display import display
8
+ from .actions import action
9
+
10
+ __all__ = [
11
+ 'display',
12
+ 'action',
13
+ ]
@@ -0,0 +1,106 @@
1
+ """
2
+ Action decorator wrapper for Unfold integration.
3
+
4
+ Provides type-safe action decorators with consistent styling.
5
+ """
6
+
7
+ from typing import Optional, Callable, Any
8
+ from functools import wraps
9
+ from unfold.decorators import action as unfold_action
10
+ from unfold.enums import ActionVariant as UnfoldActionVariant
11
+
12
+ from django_cfg.modules.django_logger import get_logger
13
+ from ..models.action_models import ActionVariant
14
+
15
+
16
+ logger = get_logger("django_admin.decorators.actions")
17
+
18
+ def action(
19
+ description: str,
20
+ variant: Optional[ActionVariant] = None,
21
+ icon: Optional[str] = None,
22
+ permissions: Optional[list] = None,
23
+ url_path: Optional[str] = None,
24
+ attrs: Optional[dict] = None
25
+ ) -> Callable:
26
+ """
27
+ Enhanced action decorator with Django Admin Utilities integration.
28
+
29
+ Args:
30
+ description: Action description shown in admin
31
+ variant: Action style variant (ActionVariant enum)
32
+ icon: Material icon name
33
+ permissions: Required permissions list
34
+ url_path: URL path for standalone action buttons (creates separate button)
35
+ attrs: Additional attributes for the action
36
+
37
+ Usage:
38
+ # Bulk action (works on selected items)
39
+ @action(description="Activate items", variant=ActionVariant.SUCCESS)
40
+ def activate_items(self, request, queryset):
41
+ updated = queryset.update(is_active=True)
42
+ self.message_user(request, f"Activated {updated} items.")
43
+
44
+ # Standalone action button (url_path creates separate button)
45
+ @action(
46
+ description="Update Rates",
47
+ variant=ActionVariant.SUCCESS,
48
+ url_path="update-rates",
49
+ icon="sync"
50
+ )
51
+ def update_rates(self, request):
52
+ # Standalone action logic (no queryset parameter)
53
+ pass
54
+ """
55
+ def decorator(func: Callable) -> Callable:
56
+ @wraps(func)
57
+ def wrapper(self, request: Any, *args, **kwargs) -> Any:
58
+ try:
59
+ # For url_path actions, there's no queryset parameter
60
+ if url_path:
61
+ return func(self, request, *args, **kwargs)
62
+ else:
63
+ # For bulk actions, pass queryset as second parameter
64
+ queryset = args[0] if args else kwargs.get('queryset')
65
+ return func(self, request, queryset, *args[1:], **kwargs)
66
+ except Exception as e:
67
+ # Log error and show user message
68
+ logger.error(f"Error in action {func.__name__}: {e}")
69
+
70
+ self.message_user(
71
+ request,
72
+ f"Error executing action: {str(e)}",
73
+ level='ERROR'
74
+ )
75
+
76
+ # Convert our ActionVariant to Unfold ActionVariant
77
+ action_variant = None
78
+ if variant:
79
+ # Direct mapping since values are the same
80
+ unfold_variant_mapping = {
81
+ ActionVariant.DEFAULT: UnfoldActionVariant.DEFAULT,
82
+ ActionVariant.PRIMARY: UnfoldActionVariant.PRIMARY,
83
+ ActionVariant.SUCCESS: UnfoldActionVariant.SUCCESS,
84
+ ActionVariant.INFO: UnfoldActionVariant.INFO,
85
+ ActionVariant.WARNING: UnfoldActionVariant.WARNING,
86
+ ActionVariant.DANGER: UnfoldActionVariant.DANGER,
87
+ }
88
+ action_variant = unfold_variant_mapping.get(variant, UnfoldActionVariant.DEFAULT)
89
+
90
+ # Build decorator kwargs
91
+ decorator_kwargs = {'description': description}
92
+ if action_variant:
93
+ decorator_kwargs['variant'] = action_variant
94
+ if icon:
95
+ decorator_kwargs['icon'] = icon
96
+ if permissions:
97
+ decorator_kwargs['permissions'] = permissions
98
+ if url_path:
99
+ decorator_kwargs['url_path'] = url_path
100
+ if attrs:
101
+ decorator_kwargs['attrs'] = attrs
102
+
103
+ # Apply Unfold decorator
104
+ return unfold_action(**decorator_kwargs)(wrapper)
105
+
106
+ return decorator
@@ -0,0 +1,106 @@
1
+ """
2
+ Display decorator wrapper for Unfold integration.
3
+
4
+ Provides type-safe display decorators with our admin utilities.
5
+ """
6
+
7
+ from typing import Optional, Callable, Any, Union
8
+ from functools import wraps
9
+ from django.utils.safestring import SafeString, mark_safe
10
+ from unfold.decorators import display as unfold_display
11
+ from django_cfg.modules.django_logger import get_logger
12
+
13
+ logger = get_logger('django_admin.decorators.display')
14
+
15
+ def display(
16
+ function: Optional[Callable] = None,
17
+ *,
18
+ boolean: Optional[bool] = None,
19
+ image: Optional[bool] = None,
20
+ ordering: Optional[str] = None,
21
+ description: Optional[str] = None,
22
+ empty_value: Optional[str] = None,
23
+ dropdown: Optional[bool] = None,
24
+ label: Union[bool, str, dict, None] = None,
25
+ header: Optional[bool] = None
26
+ ) -> Callable:
27
+ """
28
+ Enhanced display decorator with Django Admin Utilities integration.
29
+
30
+ This decorator wraps unfold.decorators.display with additional features:
31
+ - Automatic HTML safety detection and marking
32
+ - Empty value handling with customizable fallback
33
+ - Error handling with logging
34
+
35
+ Args:
36
+ function: Function to decorate (for direct usage)
37
+ boolean: Show as boolean icon (True/False icons)
38
+ image: Show as image thumbnail
39
+ ordering: Field name for sorting
40
+ description: Column header text
41
+ empty_value: Default value for empty/None fields
42
+ dropdown: Show as dropdown menu
43
+ label: Show as label badge (bool, str, or dict for styling)
44
+ header: Show as header with avatar
45
+
46
+ Usage:
47
+ @display(description="User", header=True)
48
+ def user_display(self, obj):
49
+ return self.display_user_with_avatar(obj, 'user')
50
+
51
+ @display(description="Status", label=True)
52
+ def status_display(self, obj):
53
+ return self.display_status_auto(obj, 'status')
54
+
55
+ @display(description="Has Embedding", boolean=True)
56
+ def has_embedding_display(self, obj):
57
+ return obj.has_embedding
58
+
59
+ @display(description="Avatar", image=True)
60
+ def avatar_display(self, obj):
61
+ return obj.avatar.url if obj.avatar else None
62
+ """
63
+ def decorator(func: Callable) -> Callable:
64
+ @wraps(func)
65
+ def wrapper(self, obj: Any) -> Any:
66
+ try:
67
+ result = func(self, obj)
68
+
69
+ # Handle empty values (use our default if unfold's empty_value is None)
70
+ if result is None or result == "":
71
+ return empty_value if empty_value is not None else "—"
72
+
73
+ # Auto-mark HTML as safe if it contains HTML tags
74
+ if isinstance(result, str) and ('<' in result and '>' in result):
75
+ return mark_safe(result)
76
+
77
+ # SafeString is already safe
78
+ if isinstance(result, SafeString):
79
+ return result
80
+
81
+ return result
82
+ except Exception as e:
83
+ # Log error and return safe fallback
84
+ logger.error(f"Error in display method {func.__name__}: {e}")
85
+ return empty_value if empty_value is not None else "—"
86
+
87
+ # Apply Unfold decorator with all parameters
88
+ return unfold_display(
89
+ function=None, # We handle the function ourselves
90
+ boolean=boolean,
91
+ image=image,
92
+ ordering=ordering,
93
+ description=description,
94
+ empty_value=empty_value,
95
+ dropdown=dropdown,
96
+ label=label,
97
+ header=header
98
+ )(wrapper)
99
+
100
+ # Support both @display and @display(...) usage
101
+ if function is not None:
102
+ # Direct usage: @display
103
+ return decorator(function)
104
+ else:
105
+ # Parametrized usage: @display(...)
106
+ return decorator
@@ -0,0 +1,14 @@
1
+ """
2
+ Admin mixins for easy integration.
3
+ """
4
+
5
+ from .display_mixin import DisplayMixin
6
+ from .optimization_mixin import OptimizedModelAdmin
7
+ from .standalone_actions_mixin import StandaloneActionsMixin, standalone_action
8
+
9
+ __all__ = [
10
+ "DisplayMixin",
11
+ "OptimizedModelAdmin",
12
+ "StandaloneActionsMixin",
13
+ "standalone_action",
14
+ ]
@@ -0,0 +1,81 @@
1
+ """
2
+ Display mixin for convenient wrapper methods.
3
+ """
4
+
5
+ from typing import Optional, Any
6
+ from django.utils.safestring import SafeString
7
+
8
+ from ..utils.displays import UserDisplay, MoneyDisplay, StatusDisplay, DateTimeDisplay
9
+ from ..utils.badges import StatusBadge, CounterBadge
10
+ from ..models.display_models import UserDisplayConfig, MoneyDisplayConfig, DateTimeDisplayConfig
11
+ from ..models.badge_models import StatusBadgeConfig
12
+
13
+
14
+ class DisplayMixin:
15
+ """Mixin for Django ModelAdmin classes with convenient display methods."""
16
+
17
+ def display_user_with_avatar(self, obj: Any, user_field: str = 'user',
18
+ config: Optional[UserDisplayConfig] = None) -> list:
19
+ """Display user with avatar for @display(header=True)."""
20
+ user = getattr(obj, user_field, None)
21
+ return UserDisplay.with_avatar(user, config)
22
+
23
+ def display_user_simple(self, obj: Any, user_field: str = 'user',
24
+ config: Optional[UserDisplayConfig] = None) -> SafeString:
25
+ """Simple user display."""
26
+ user = getattr(obj, user_field, None)
27
+ return UserDisplay.simple(user, config)
28
+
29
+ def display_money_amount(self, obj: Any, amount_field: str,
30
+ config: Optional[MoneyDisplayConfig] = None) -> SafeString:
31
+ """Display money amount."""
32
+ amount = getattr(obj, amount_field, None)
33
+ return MoneyDisplay.amount(amount, config)
34
+
35
+ def display_money_breakdown(self, obj: Any, main_field: str, breakdown_fields: dict,
36
+ config: Optional[MoneyDisplayConfig] = None) -> SafeString:
37
+ """Display money with breakdown."""
38
+ main_amount = getattr(obj, main_field, 0)
39
+
40
+ breakdown_items = []
41
+ for label, field_name in breakdown_fields.items():
42
+ amount = getattr(obj, field_name, 0)
43
+ breakdown_items.append({
44
+ 'label': label,
45
+ 'amount': amount,
46
+ 'color': 'warning' if amount > 0 else 'secondary'
47
+ })
48
+
49
+ return MoneyDisplay.with_breakdown(main_amount, breakdown_items, config)
50
+
51
+ def display_status_auto(self, obj: Any, status_field: str = 'status',
52
+ config: Optional[StatusBadgeConfig] = None) -> SafeString:
53
+ """Display status with auto color mapping."""
54
+ status = getattr(obj, status_field, '')
55
+ return StatusBadge.auto(status, config)
56
+
57
+ def display_datetime_relative(self, obj: Any, datetime_field: str,
58
+ config: Optional[DateTimeDisplayConfig] = None) -> SafeString:
59
+ """Display datetime with relative time."""
60
+ dt = getattr(obj, datetime_field, None)
61
+ return DateTimeDisplay.relative(dt, config)
62
+
63
+ def display_datetime_compact(self, obj: Any, datetime_field: str,
64
+ config: Optional[DateTimeDisplayConfig] = None) -> SafeString:
65
+ """Display datetime compact."""
66
+ dt = getattr(obj, datetime_field, None)
67
+ return DateTimeDisplay.compact(dt, config)
68
+
69
+ def display_count_simple(self, obj: Any, count_field: str, label: str = None) -> SafeString:
70
+ """Display count as badge."""
71
+ count = getattr(obj, count_field, 0)
72
+ return CounterBadge.simple(count, label)
73
+
74
+ def display_related_count(self, obj: Any, related_name: str, label: str = None) -> SafeString:
75
+ """Display count of related objects."""
76
+ try:
77
+ related_manager = getattr(obj, related_name)
78
+ count = related_manager.count()
79
+ return CounterBadge.simple(count, label)
80
+ except AttributeError:
81
+ return CounterBadge.simple(0, label)
@@ -0,0 +1,41 @@
1
+ """
2
+ Query optimization mixin for performance.
3
+ """
4
+
5
+ import logging
6
+ from typing import List, Dict, Any
7
+ from django.contrib import admin
8
+ from django.db.models import QuerySet
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class OptimizedModelAdmin(admin.ModelAdmin):
14
+ """Optimized ModelAdmin with automatic query optimization."""
15
+
16
+ # Performance settings
17
+ list_per_page = 50
18
+ show_full_result_count = False
19
+
20
+ # Fields for optimization - override in subclasses
21
+ select_related_fields: List[str] = []
22
+ prefetch_related_fields: List[str] = []
23
+ annotations: Dict[str, Any] = {}
24
+
25
+ def get_queryset(self, request) -> QuerySet:
26
+ """Optimize queryset with select_related and prefetch_related."""
27
+ qs = super().get_queryset(request)
28
+
29
+ if self.select_related_fields:
30
+ qs = qs.select_related(*self.select_related_fields)
31
+ logger.debug(f"Applied select_related: {self.select_related_fields}")
32
+
33
+ if self.prefetch_related_fields:
34
+ qs = qs.prefetch_related(*self.prefetch_related_fields)
35
+ logger.debug(f"Applied prefetch_related: {self.prefetch_related_fields}")
36
+
37
+ if self.annotations:
38
+ qs = qs.annotate(**self.annotations)
39
+ logger.debug(f"Applied annotations: {list(self.annotations.keys())}")
40
+
41
+ return qs