django-cfg 1.3.13__py3-none-any.whl → 1.4.0__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 (438) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/user_admin.py +39 -16
  3. django_cfg/apps/accounts/serializers/profile.py +1 -1
  4. django_cfg/apps/accounts/services/otp_service.py +18 -11
  5. django_cfg/apps/accounts/signals.py +15 -24
  6. django_cfg/apps/accounts/utils/notifications.py +217 -358
  7. django_cfg/apps/accounts/views/otp.py +2 -2
  8. django_cfg/apps/accounts/views/webhook.py +1 -1
  9. django_cfg/apps/agents/core/django_agent.py +1 -1
  10. django_cfg/apps/api/commands/views.py +66 -83
  11. django_cfg/apps/api/health/drf_views.py +269 -0
  12. django_cfg/apps/api/health/serializers.py +45 -0
  13. django_cfg/apps/api/health/urls.py +6 -1
  14. django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
  15. django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
  16. django_cfg/apps/knowbase/admin/document_admin.py +136 -270
  17. django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
  18. django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
  19. django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
  20. django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
  21. django_cfg/apps/knowbase/config/constance_fields.py +1 -1
  22. django_cfg/apps/knowbase/config/settings.py +2 -2
  23. django_cfg/apps/knowbase/mixins/__init__.py +19 -2
  24. django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
  25. django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
  26. django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
  27. django_cfg/apps/knowbase/mixins/creator.py +10 -10
  28. django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
  29. django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
  30. django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
  31. django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
  32. django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
  33. django_cfg/apps/knowbase/mixins/service.py +2 -2
  34. django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
  35. django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
  36. django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
  37. django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
  38. django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
  39. django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
  40. django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
  41. django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
  42. django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
  43. django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
  44. django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
  45. django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
  46. django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
  47. django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
  48. django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
  49. django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
  50. django_cfg/apps/knowbase/services/embedding/models.py +18 -14
  51. django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
  52. django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
  53. django_cfg/apps/leads/tests.py +1 -1
  54. django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
  55. django_cfg/apps/payments/admin/balance_admin.py +1 -1
  56. django_cfg/apps/payments/admin/currencies_admin.py +1 -1
  57. django_cfg/apps/payments/admin/payments_admin.py +1 -1
  58. django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
  59. django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
  60. django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
  61. django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
  62. django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
  63. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
  64. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
  65. django_cfg/apps/payments/admin_interface/views/base.py +29 -2
  66. django_cfg/apps/payments/apps.py +1 -1
  67. django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
  68. django_cfg/apps/payments/config/helpers.py +3 -2
  69. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
  70. django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
  71. django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
  72. django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
  73. django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
  74. django_cfg/apps/payments/management/commands/test_providers.py +1 -1
  75. django_cfg/apps/payments/middleware/api_access.py +1 -1
  76. django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
  77. django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
  78. django_cfg/apps/payments/models/balance.py +2 -2
  79. django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
  80. django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
  81. django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
  82. django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
  83. django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
  84. django_cfg/apps/payments/models/payments.py +2 -2
  85. django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
  86. django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
  87. django_cfg/apps/payments/services/core/base.py +1 -1
  88. django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
  89. django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
  90. django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
  91. django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
  92. django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
  93. django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
  94. django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
  95. django_cfg/apps/payments/services/core/payment_service.py +124 -612
  96. django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
  97. django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
  98. django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
  99. django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
  100. django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
  101. django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
  102. django_cfg/apps/payments/services/providers/base.py +1 -1
  103. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
  104. django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
  105. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
  106. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
  107. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
  108. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
  109. django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
  110. django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
  111. django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
  112. django_cfg/apps/payments/services/providers/registry.py +1 -1
  113. django_cfg/apps/payments/services/providers/sync_service.py +1 -1
  114. django_cfg/apps/payments/signals/__init__.py +1 -1
  115. django_cfg/apps/payments/signals/api_key_signals.py +1 -1
  116. django_cfg/apps/payments/signals/balance_signals.py +1 -1
  117. django_cfg/apps/payments/signals/payment_signals.py +1 -1
  118. django_cfg/apps/payments/signals/subscription_signals.py +1 -1
  119. django_cfg/apps/payments/views/api/api_keys.py +1 -1
  120. django_cfg/apps/payments/views/api/balances.py +1 -1
  121. django_cfg/apps/payments/views/api/base.py +1 -1
  122. django_cfg/apps/payments/views/api/currencies.py +1 -1
  123. django_cfg/apps/payments/views/api/payments.py +1 -1
  124. django_cfg/apps/payments/views/api/subscriptions.py +1 -1
  125. django_cfg/apps/payments/views/api/webhooks.py +1 -1
  126. django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
  127. django_cfg/apps/payments/views/serializers/balances.py +1 -1
  128. django_cfg/apps/payments/views/serializers/currencies.py +1 -1
  129. django_cfg/apps/payments/views/serializers/payments.py +1 -1
  130. django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
  131. django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
  132. django_cfg/apps/support/admin/support_admin.py +21 -13
  133. django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
  134. django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
  135. django_cfg/apps/support/utils/support_email_service.py +1 -1
  136. django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
  137. django_cfg/apps/tasks/utils/simulator.py +1 -1
  138. django_cfg/apps/tasks/views/dashboard.py +33 -3
  139. django_cfg/apps/urls.py +5 -1
  140. django_cfg/cli/README.md +57 -471
  141. django_cfg/cli/commands/create_project.py +140 -529
  142. django_cfg/cli/main.py +13 -10
  143. django_cfg/core/__init__.py +63 -6
  144. django_cfg/core/base/__init__.py +5 -0
  145. django_cfg/core/base/config_model.py +652 -0
  146. django_cfg/core/builders/__init__.py +11 -0
  147. django_cfg/core/builders/apps_builder.py +258 -0
  148. django_cfg/core/builders/middleware_builder.py +115 -0
  149. django_cfg/core/builders/security_builder.py +96 -0
  150. django_cfg/core/config.py +20 -892
  151. django_cfg/core/constants.py +69 -0
  152. django_cfg/core/environment/__init__.py +9 -0
  153. django_cfg/core/exceptions.py +45 -298
  154. django_cfg/core/generation/__init__.py +51 -0
  155. django_cfg/core/generation/core_generators/__init__.py +0 -0
  156. django_cfg/core/generation/core_generators/settings.py +90 -0
  157. django_cfg/core/generation/core_generators/static.py +82 -0
  158. django_cfg/core/generation/core_generators/templates.py +141 -0
  159. django_cfg/core/generation/data_generators/__init__.py +15 -0
  160. django_cfg/core/generation/data_generators/cache.py +132 -0
  161. django_cfg/core/generation/data_generators/database.py +117 -0
  162. django_cfg/core/generation/generation.py +92 -0
  163. django_cfg/core/generation/integration_generators/__init__.py +21 -0
  164. django_cfg/core/generation/integration_generators/api.py +237 -0
  165. django_cfg/core/generation/integration_generators/sessions.py +65 -0
  166. django_cfg/core/generation/integration_generators/tailwind.py +54 -0
  167. django_cfg/core/generation/integration_generators/tasks.py +92 -0
  168. django_cfg/core/generation/integration_generators/third_party.py +144 -0
  169. django_cfg/core/generation/orchestrator.py +285 -0
  170. django_cfg/core/generation/protocols.py +30 -0
  171. django_cfg/core/generation/security_generators/__init__.py +0 -0
  172. django_cfg/core/generation/utility_generators/__init__.py +24 -0
  173. django_cfg/core/generation/utility_generators/email.py +58 -0
  174. django_cfg/core/generation/utility_generators/i18n.py +66 -0
  175. django_cfg/core/generation/utility_generators/limits.py +58 -0
  176. django_cfg/core/generation/utility_generators/logging.py +66 -0
  177. django_cfg/core/generation/utility_generators/security.py +101 -0
  178. django_cfg/core/generation/utils/__init__.py +0 -0
  179. django_cfg/core/generation/utils/helpers.py +32 -0
  180. django_cfg/core/integration/__init__.py +18 -25
  181. django_cfg/core/integration/display/startup.py +146 -133
  182. django_cfg/core/integration/url_integration.py +13 -2
  183. django_cfg/core/services/__init__.py +5 -0
  184. django_cfg/core/services/config_service.py +121 -0
  185. django_cfg/core/state/__init__.py +9 -0
  186. django_cfg/core/state/registry.py +84 -0
  187. django_cfg/core/types/__init__.py +15 -0
  188. django_cfg/core/types/aliases.py +15 -0
  189. django_cfg/core/types/enums.py +49 -0
  190. django_cfg/dashboard/DEBUG_README.md +105 -0
  191. django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
  192. django_cfg/dashboard/__init__.py +24 -0
  193. django_cfg/dashboard/components.py +308 -0
  194. django_cfg/dashboard/debug.py +176 -0
  195. django_cfg/dashboard/management/__init__.py +0 -0
  196. django_cfg/dashboard/management/commands/__init__.py +0 -0
  197. django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
  198. django_cfg/dashboard/sections/__init__.py +1 -0
  199. django_cfg/dashboard/sections/base.py +128 -0
  200. django_cfg/dashboard/sections/commands.py +32 -0
  201. django_cfg/dashboard/sections/overview.py +394 -0
  202. django_cfg/dashboard/sections/stats.py +48 -0
  203. django_cfg/dashboard/sections/system.py +73 -0
  204. django_cfg/management/commands/check_settings.py +6 -2
  205. django_cfg/management/commands/clear_constance.py +6 -1
  206. django_cfg/management/commands/create_token.py +5 -4
  207. django_cfg/management/commands/generate.py +5 -0
  208. django_cfg/management/commands/list_urls.py +7 -2
  209. django_cfg/management/commands/migrate_all.py +6 -2
  210. django_cfg/management/commands/migrator.py +6 -1
  211. django_cfg/management/commands/rundramatiq.py +6 -1
  212. django_cfg/management/commands/rundramatiq_simulator.py +11 -4
  213. django_cfg/management/commands/runserver_ngrok.py +9 -7
  214. django_cfg/management/commands/script.py +25 -21
  215. django_cfg/management/commands/show_config.py +6 -1
  216. django_cfg/management/commands/show_urls.py +8 -3
  217. django_cfg/management/commands/superuser.py +5 -4
  218. django_cfg/management/commands/task_clear.py +8 -3
  219. django_cfg/management/commands/task_status.py +8 -3
  220. django_cfg/management/commands/test_email.py +6 -1
  221. django_cfg/management/commands/test_telegram.py +6 -1
  222. django_cfg/management/commands/test_twilio.py +6 -1
  223. django_cfg/management/commands/tree.py +7 -4
  224. django_cfg/models/__init__.py +88 -3
  225. django_cfg/models/api/__init__.py +27 -0
  226. django_cfg/models/{api.py → api/config.py} +1 -1
  227. django_cfg/models/api/drf/__init__.py +21 -0
  228. django_cfg/models/api/drf/config.py +101 -0
  229. django_cfg/models/api/drf/redoc.py +31 -0
  230. django_cfg/models/api/drf/spectacular.py +129 -0
  231. django_cfg/models/api/drf/swagger.py +59 -0
  232. django_cfg/models/{api_keys.py → api/keys.py} +16 -6
  233. django_cfg/models/{limits.py → api/limits.py} +0 -1
  234. django_cfg/models/base/__init__.py +14 -0
  235. django_cfg/models/django/__init__.py +16 -0
  236. django_cfg/models/{constance.py → django/constance.py} +1 -1
  237. django_cfg/models/{environment.py → django/environment.py} +1 -1
  238. django_cfg/models/infrastructure/__init__.py +17 -0
  239. django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
  240. django_cfg/models/infrastructure/database/__init__.py +22 -0
  241. django_cfg/models/infrastructure/database/config.py +265 -0
  242. django_cfg/models/infrastructure/database/converters.py +91 -0
  243. django_cfg/models/infrastructure/database/parsers.py +96 -0
  244. django_cfg/models/infrastructure/database/routing.py +85 -0
  245. django_cfg/models/infrastructure/database/validators.py +170 -0
  246. django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
  247. django_cfg/models/{security.py → infrastructure/security.py} +2 -2
  248. django_cfg/models/ngrok/__init__.py +11 -0
  249. django_cfg/models/ngrok/auth.py +37 -0
  250. django_cfg/models/ngrok/config.py +77 -0
  251. django_cfg/models/ngrok/tunnel.py +35 -0
  252. django_cfg/models/payments/__init__.py +20 -0
  253. django_cfg/models/payments/api_keys.py +57 -0
  254. django_cfg/models/{payments.py → payments/config.py} +56 -154
  255. django_cfg/models/payments/providers/__init__.py +15 -0
  256. django_cfg/models/payments/providers/base.py +25 -0
  257. django_cfg/models/payments/providers/nowpayments.py +48 -0
  258. django_cfg/models/services/__init__.py +18 -0
  259. django_cfg/models/services/base.py +65 -0
  260. django_cfg/models/{email.py → services/email.py} +1 -1
  261. django_cfg/models/services/telegram.py +172 -0
  262. django_cfg/models/tasks/__init__.py +51 -0
  263. django_cfg/models/tasks/backends.py +250 -0
  264. django_cfg/models/tasks/config.py +314 -0
  265. django_cfg/models/tasks/utils.py +174 -0
  266. django_cfg/modules/base.py +18 -3
  267. django_cfg/modules/django_admin/decorators/actions.py +1 -1
  268. django_cfg/modules/django_admin/decorators/display.py +1 -1
  269. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
  270. django_cfg/modules/django_cfg_rpc_client/README.md +346 -0
  271. django_cfg/modules/django_cfg_rpc_client/__init__.py +51 -0
  272. django_cfg/modules/django_cfg_rpc_client/client.py +540 -0
  273. django_cfg/modules/django_cfg_rpc_client/config.py +207 -0
  274. django_cfg/modules/django_cfg_rpc_client/dashboard/README.md +517 -0
  275. django_cfg/modules/django_cfg_rpc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
  276. django_cfg/modules/django_cfg_rpc_client/dashboard/__init__.py +11 -0
  277. django_cfg/modules/django_cfg_rpc_client/dashboard/apps.py +22 -0
  278. django_cfg/modules/django_cfg_rpc_client/dashboard/monitor.py +435 -0
  279. django_cfg/modules/django_cfg_rpc_client/dashboard/static/django_cfg_rpc_dashboard/js/dashboard.js +373 -0
  280. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/base.html +76 -0
  281. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/dashboard.html +200 -0
  282. django_cfg/modules/django_cfg_rpc_client/dashboard/urls.py +22 -0
  283. django_cfg/modules/django_cfg_rpc_client/dashboard/urls_admin.py +9 -0
  284. django_cfg/modules/django_cfg_rpc_client/dashboard/views.py +251 -0
  285. django_cfg/modules/django_cfg_rpc_client/exceptions.py +201 -0
  286. django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
  287. django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
  288. django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
  289. django_cfg/modules/django_drf_theme/README.md +207 -0
  290. django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
  291. django_cfg/modules/django_drf_theme/__init__.py +23 -0
  292. django_cfg/modules/django_drf_theme/apps.py +15 -0
  293. django_cfg/modules/django_drf_theme/renderers.py +58 -0
  294. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
  295. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
  296. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
  297. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
  298. django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
  299. django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
  300. django_cfg/modules/django_email/__init__.py +14 -0
  301. django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
  302. django_cfg/modules/django_email/utils.py +40 -0
  303. django_cfg/modules/django_health/__init__.py +9 -0
  304. django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
  305. django_cfg/modules/django_llm/llm/client.py +155 -550
  306. django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
  307. django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
  308. django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
  309. django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
  310. django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
  311. django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
  312. django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
  313. django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
  314. django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
  315. django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
  316. django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
  317. django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
  318. django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
  319. django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
  320. django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
  321. django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
  322. django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
  323. django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
  324. django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
  325. django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
  326. django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
  327. django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
  328. django_cfg/modules/django_llm/translator/translator.py +150 -603
  329. django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
  330. django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
  331. django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
  332. django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
  333. django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
  334. django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
  335. django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
  336. django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
  337. django_cfg/modules/django_logging/__init__.py +14 -0
  338. django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
  339. django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
  340. django_cfg/modules/django_ngrok/__init__.py +39 -0
  341. django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
  342. django_cfg/modules/django_rpc_old/POETRY.md +344 -0
  343. django_cfg/modules/django_rpc_old/README.md +397 -0
  344. django_cfg/modules/django_rpc_old/TESTING.md +358 -0
  345. django_cfg/modules/django_rpc_old/__init__.py +39 -0
  346. django_cfg/modules/django_rpc_old/client.py +531 -0
  347. django_cfg/modules/django_rpc_old/config.py +279 -0
  348. django_cfg/modules/django_rpc_old/exceptions.py +172 -0
  349. django_cfg/modules/django_tailwind/README.md +478 -0
  350. django_cfg/modules/django_tailwind/__init__.py +7 -0
  351. django_cfg/modules/django_tailwind/apps.py +10 -0
  352. django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
  353. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
  354. django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
  355. django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
  356. django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
  357. django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
  358. django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
  359. django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
  360. django_cfg/modules/django_tasks/__init__.py +29 -0
  361. django_cfg/modules/django_tasks/factory.py +127 -0
  362. django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
  363. django_cfg/modules/django_tasks/settings.py +107 -0
  364. django_cfg/modules/django_telegram/__init__.py +29 -0
  365. django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
  366. django_cfg/modules/django_telegram/utils.py +62 -0
  367. django_cfg/modules/django_twilio/__init__.py +54 -107
  368. django_cfg/modules/django_twilio/_imports.py +30 -0
  369. django_cfg/modules/django_twilio/base.py +192 -0
  370. django_cfg/modules/django_twilio/email_otp.py +227 -0
  371. django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
  372. django_cfg/modules/django_twilio/simple_service.py +1 -2
  373. django_cfg/modules/django_twilio/sms.py +94 -0
  374. django_cfg/modules/django_twilio/twilio_service.py +2 -3
  375. django_cfg/modules/django_twilio/unified.py +310 -0
  376. django_cfg/modules/django_twilio/utils.py +190 -0
  377. django_cfg/modules/django_twilio/whatsapp.py +137 -0
  378. django_cfg/modules/django_unfold/callbacks/base.py +198 -7
  379. django_cfg/modules/django_unfold/callbacks/main.py +102 -10
  380. django_cfg/modules/django_unfold/dashboard.py +65 -43
  381. django_cfg/modules/django_unfold/models/config.py +13 -12
  382. django_cfg/modules/django_unfold/models/navigation.py +8 -3
  383. django_cfg/modules/django_unfold/models/tabs.py +2 -2
  384. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
  385. django_cfg/registry/core.py +24 -26
  386. django_cfg/registry/modules.py +5 -2
  387. django_cfg/registry/services.py +20 -3
  388. django_cfg/registry/third_party.py +8 -8
  389. django_cfg/static/admin/css/dashboard.css +260 -0
  390. django_cfg/static/admin/js/commands.js +171 -0
  391. django_cfg/static/admin/js/dashboard.js +126 -0
  392. django_cfg/templates/admin/components/management_commands.js +375 -0
  393. django_cfg/templates/admin/components/progress_bar.html +18 -23
  394. django_cfg/templates/admin/index.html +48 -20
  395. django_cfg/templates/admin/index_new.html +106 -0
  396. django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
  397. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
  398. django_cfg/templates/admin/sections/commands_section.html +626 -0
  399. django_cfg/templates/admin/sections/overview_section.html +112 -0
  400. django_cfg/templates/admin/sections/stats_section.html +35 -0
  401. django_cfg/templates/admin/sections/system_section.html +99 -0
  402. django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
  403. django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
  404. django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
  405. django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
  406. django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
  407. django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
  408. django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
  409. django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
  410. django_cfg/templatetags/django_cfg.py +7 -1
  411. django_cfg/utils/smart_defaults.py +4 -4
  412. django_cfg-1.4.0.dist-info/METADATA +920 -0
  413. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/RECORD +424 -195
  414. django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
  415. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
  416. django_cfg/core/generation.py +0 -621
  417. django_cfg/management/commands/validate_config.py +0 -189
  418. django_cfg/models/database.py +0 -480
  419. django_cfg/models/drf.py +0 -272
  420. django_cfg/models/ngrok.py +0 -122
  421. django_cfg/models/services.py +0 -440
  422. django_cfg/models/tasks.py +0 -550
  423. django_cfg/modules/django_twilio/service.py +0 -942
  424. django_cfg/template_archive/django_sample.zip +0 -0
  425. django_cfg/templates/rest_framework/api.html +0 -12
  426. django_cfg/utils/toolkit.py +0 -703
  427. django_cfg-1.3.13.dist-info/METADATA +0 -1029
  428. /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
  429. /django_cfg/core/{environment.py → environment/detector.py} +0 -0
  430. /django_cfg/models/{cors.py → api/cors.py} +0 -0
  431. /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
  432. /django_cfg/models/{base.py → base/config.py} +0 -0
  433. /django_cfg/models/{cfg.py → base/module.py} +0 -0
  434. /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
  435. /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
  436. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/WHEEL +0 -0
  437. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/entry_points.txt +0 -0
  438. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,265 @@
1
+ """
2
+ Database configuration model for django_cfg.
3
+
4
+ Type-safe database connection configuration with validation.
5
+ """
6
+
7
+ from typing import Dict, List, Optional, Any, Literal
8
+ from pydantic import BaseModel, Field, field_validator, model_validator, PrivateAttr
9
+
10
+ from . import validators
11
+ from . import converters
12
+ from . import routing
13
+ from .parsers import detect_engine_from_url
14
+
15
+
16
+ class DatabaseConfig(BaseModel):
17
+ """
18
+ Type-safe database connection configuration.
19
+
20
+ Supports both individual connection parameters and connection strings.
21
+ Automatically validates connection parameters and provides helpful error messages.
22
+ """
23
+
24
+ model_config = {
25
+ "str_strip_whitespace": True,
26
+ "validate_assignment": True,
27
+ "extra": "forbid", # Prevent typos in field names
28
+ }
29
+
30
+ # Core connection parameters
31
+ engine: Optional[str] = Field(
32
+ default=None,
33
+ description="Django database engine (e.g., 'django.db.backends.postgresql'). If not provided, will be auto-detected from database URL scheme.",
34
+ min_length=1,
35
+ )
36
+
37
+ name: str = Field(
38
+ ...,
39
+ description="Database name or connection string",
40
+ min_length=1,
41
+ )
42
+
43
+ user: Optional[str] = Field(
44
+ default=None,
45
+ description="Database username",
46
+ )
47
+
48
+ password: Optional[str] = Field(
49
+ default=None,
50
+ description="Database password",
51
+ repr=False, # Don't show in repr for security
52
+ )
53
+
54
+ host: str = Field(
55
+ default="localhost",
56
+ description="Database host",
57
+ min_length=1,
58
+ )
59
+
60
+ port: int = Field(
61
+ default=5432,
62
+ description="Database port",
63
+ ge=1,
64
+ le=65535,
65
+ )
66
+
67
+ # Connection options
68
+ connect_timeout: int = Field(
69
+ default=10,
70
+ description="Connection timeout in seconds",
71
+ ge=1,
72
+ le=300, # Max 5 minutes
73
+ )
74
+
75
+ sslmode: str = Field(
76
+ default="prefer",
77
+ description="SSL mode for connection",
78
+ )
79
+
80
+ # Additional database options
81
+ options: Dict[str, Any] = Field(
82
+ default_factory=dict,
83
+ description="Additional database-specific options",
84
+ )
85
+
86
+ # Database routing configuration
87
+ apps: List[str] = Field(
88
+ default_factory=list,
89
+ description="Django app labels that should use this database",
90
+ )
91
+
92
+ operations: List[Literal["read", "write", "migrate"]] = Field(
93
+ default_factory=lambda: ["read", "write", "migrate"],
94
+ description="Allowed operations for this database",
95
+ min_length=1,
96
+ )
97
+
98
+ migrate_to: Optional[str] = Field(
99
+ default=None,
100
+ description="Override database alias for migrations (if different from this database)",
101
+ )
102
+
103
+ routing_description: str = Field(
104
+ default="",
105
+ description="Human-readable description of the routing rule",
106
+ )
107
+
108
+ # Internal fields for parsed connection strings
109
+ _is_connection_string: bool = PrivateAttr(default=False)
110
+ _parsed_components: Optional[Dict[str, Any]] = PrivateAttr(default=None)
111
+
112
+ # Validators
113
+ @field_validator("engine")
114
+ @classmethod
115
+ def validate_engine(cls, v: Optional[str]) -> Optional[str]:
116
+ """Validate Django database engine format."""
117
+ return validators.validate_engine(v)
118
+
119
+ @field_validator("name")
120
+ @classmethod
121
+ def validate_name(cls, v: str) -> str:
122
+ """Validate database name or parse connection string."""
123
+ return validators.validate_name(v)
124
+
125
+ @field_validator("sslmode")
126
+ @classmethod
127
+ def validate_sslmode(cls, v: str) -> str:
128
+ """Validate SSL mode values."""
129
+ return validators.validate_sslmode(v)
130
+
131
+ @field_validator("apps")
132
+ @classmethod
133
+ def validate_apps(cls, v: List[str]) -> List[str]:
134
+ """Validate app labels format."""
135
+ return validators.validate_apps(v)
136
+
137
+ @field_validator("operations")
138
+ @classmethod
139
+ def validate_operations(cls, v: List[str]) -> List[str]:
140
+ """Validate operations list."""
141
+ return validators.validate_operations(v)
142
+
143
+ @model_validator(mode="before")
144
+ @classmethod
145
+ def validate_connection_consistency(cls, values: Dict[str, Any]) -> Dict[str, Any]:
146
+ """Validate connection parameter consistency and auto-detect engine."""
147
+ return validators.validate_connection_consistency(values)
148
+
149
+ @model_validator(mode="after")
150
+ def validate_connection_after(self) -> "DatabaseConfig":
151
+ """Validate connection after model creation."""
152
+ return validators.validate_connection_after(self)
153
+
154
+ # Converters
155
+ def to_django_config(self) -> Dict[str, Any]:
156
+ """
157
+ Convert to Django database configuration format.
158
+
159
+ Returns:
160
+ Django-compatible database configuration dictionary
161
+
162
+ Raises:
163
+ DatabaseError: If configuration cannot be converted
164
+ """
165
+ return converters.to_django_config(self)
166
+
167
+ # Routing methods
168
+ def matches_app(self, app_label: str) -> bool:
169
+ """
170
+ Check if this database should be used for the given app.
171
+
172
+ Args:
173
+ app_label: Django app label to check
174
+
175
+ Returns:
176
+ True if this database should be used for the app
177
+ """
178
+ return routing.matches_app(self, app_label)
179
+
180
+ def allows_operation(self, operation: str) -> bool:
181
+ """
182
+ Check if this database allows the given operation.
183
+
184
+ Args:
185
+ operation: Operation to check ('read', 'write', 'migrate')
186
+
187
+ Returns:
188
+ True if operation is allowed
189
+ """
190
+ return routing.allows_operation(self, operation)
191
+
192
+ def get_migration_database(self) -> Optional[str]:
193
+ """
194
+ Get the database alias to use for migrations.
195
+
196
+ Returns:
197
+ Database alias for migrations, or None to use this database
198
+ """
199
+ return routing.get_migration_database(self)
200
+
201
+ def has_routing_rules(self) -> bool:
202
+ """
203
+ Check if this database has any routing rules configured.
204
+
205
+ Returns:
206
+ True if apps list is not empty
207
+ """
208
+ return routing.has_routing_rules(self)
209
+
210
+ def test_connection(self) -> bool:
211
+ """
212
+ Test database connection (placeholder for future implementation).
213
+
214
+ Returns:
215
+ True if connection successful, False otherwise
216
+ """
217
+ return routing.test_connection(self)
218
+
219
+ # Factory methods
220
+ @classmethod
221
+ def from_url(
222
+ cls,
223
+ url: str,
224
+ *,
225
+ apps: Optional[List[str]] = None,
226
+ operations: Optional[List[Literal["read", "write", "migrate"]]] = None,
227
+ routing_description: str = "",
228
+ **kwargs
229
+ ) -> "DatabaseConfig":
230
+ """
231
+ Create DatabaseConfig from URL with automatic engine detection.
232
+
233
+ Args:
234
+ url: Database URL (e.g., 'postgresql://user:pass@host:port/db')
235
+ apps: Django app labels that should use this database
236
+ operations: Allowed operations for this database
237
+ routing_description: Human-readable description of the routing rule
238
+ **kwargs: Additional parameters to override defaults
239
+
240
+ Returns:
241
+ DatabaseConfig instance with auto-detected engine
242
+
243
+ Example:
244
+ # Simple SQLite database
245
+ db = DatabaseConfig.from_url("sqlite:///db.sqlite3")
246
+
247
+ # PostgreSQL with routing
248
+ blog_db = DatabaseConfig.from_url(
249
+ "postgresql://user:pass@localhost:5432/blog",
250
+ apps=["apps.blog"],
251
+ routing_description="Blog posts and comments"
252
+ )
253
+ """
254
+ return cls(
255
+ name=url,
256
+ apps=apps or [],
257
+ operations=operations or ["read", "write", "migrate"],
258
+ routing_description=routing_description,
259
+ **kwargs
260
+ )
261
+
262
+
263
+ __all__ = [
264
+ "DatabaseConfig",
265
+ ]
@@ -0,0 +1,91 @@
1
+ """
2
+ Database configuration converters.
3
+
4
+ Convert DatabaseConfig to Django settings format.
5
+ """
6
+
7
+ from typing import Dict, Any
8
+
9
+
10
+ def to_django_config(config: "DatabaseConfig") -> Dict[str, Any]: # type: ignore
11
+ """
12
+ Convert DatabaseConfig to Django database configuration format.
13
+
14
+ Args:
15
+ config: DatabaseConfig instance
16
+
17
+ Returns:
18
+ Django-compatible database configuration dictionary
19
+
20
+ Raises:
21
+ DatabaseError: If configuration cannot be converted
22
+ """
23
+ try:
24
+ # Base configuration
25
+ django_config = {
26
+ "ENGINE": config.engine,
27
+ "OPTIONS": {**config.options},
28
+ }
29
+
30
+ # Add database-specific options
31
+ if config.engine == "django.db.backends.postgresql":
32
+ # PostgreSQL supports connect_timeout and sslmode
33
+ django_config["OPTIONS"]["connect_timeout"] = config.connect_timeout
34
+ django_config["OPTIONS"]["sslmode"] = config.sslmode
35
+ elif config.engine == "django.db.backends.mysql":
36
+ # MySQL supports connect_timeout but not sslmode
37
+ django_config["OPTIONS"]["connect_timeout"] = config.connect_timeout
38
+ # SQLite doesn't support connect_timeout or sslmode, so we skip them
39
+
40
+ # Handle connection string vs individual parameters
41
+ if config._is_connection_string:
42
+ # For connection strings, use the full string as NAME
43
+ django_config["NAME"] = config.name
44
+
45
+ # Add parsed components if available
46
+ if config._parsed_components:
47
+ parsed = config._parsed_components
48
+ if parsed.get("database"):
49
+ django_config["NAME"] = parsed["database"]
50
+ if parsed.get("user"):
51
+ django_config["USER"] = parsed["user"]
52
+ if parsed.get("password"):
53
+ django_config["PASSWORD"] = parsed["password"]
54
+ if parsed.get("host"):
55
+ django_config["HOST"] = parsed["host"]
56
+ if parsed.get("port"):
57
+ django_config["PORT"] = parsed["port"]
58
+ if parsed.get("options"):
59
+ django_config["OPTIONS"].update(parsed["options"])
60
+ else:
61
+ # Individual parameters
62
+ django_config["NAME"] = config.name
63
+
64
+ if config.user:
65
+ django_config["USER"] = config.user
66
+
67
+ if config.password:
68
+ django_config["PASSWORD"] = config.password
69
+
70
+ if config.host:
71
+ django_config["HOST"] = config.host
72
+
73
+ if config.port:
74
+ django_config["PORT"] = config.port
75
+
76
+ return django_config
77
+
78
+ except Exception as e:
79
+ # Import here to avoid circular dependency
80
+ from django_cfg.core.exceptions import DatabaseError
81
+
82
+ raise DatabaseError(
83
+ f"Failed to convert database configuration: {e}",
84
+ database_alias=getattr(config, "_alias", "unknown"),
85
+ context={"config": config.model_dump()}
86
+ ) from e
87
+
88
+
89
+ __all__ = [
90
+ "to_django_config",
91
+ ]
@@ -0,0 +1,96 @@
1
+ """
2
+ Database connection string parsers.
3
+
4
+ Utilities for parsing and detecting database engines from URLs.
5
+ """
6
+
7
+ from typing import Dict, Any
8
+ from urllib.parse import urlparse
9
+
10
+
11
+ def detect_engine_from_url(url: str) -> str:
12
+ """
13
+ Automatically detect Django database engine from URL scheme.
14
+
15
+ Args:
16
+ url: Database URL (e.g., 'postgresql://...', 'sqlite:///...')
17
+
18
+ Returns:
19
+ Django database engine string
20
+
21
+ Raises:
22
+ ValueError: If URL scheme is not supported
23
+ """
24
+ if "://" not in url:
25
+ # Assume SQLite for file paths without scheme
26
+ return "django.db.backends.sqlite3"
27
+
28
+ scheme = url.split("://")[0].lower()
29
+
30
+ # Map URL schemes to Django engines
31
+ scheme_to_engine = {
32
+ "postgresql": "django.db.backends.postgresql",
33
+ "postgres": "django.db.backends.postgresql",
34
+ "mysql": "django.db.backends.mysql",
35
+ "sqlite": "django.db.backends.sqlite3",
36
+ "sqlite3": "django.db.backends.sqlite3",
37
+ "oracle": "django.db.backends.oracle",
38
+ }
39
+
40
+ if scheme in scheme_to_engine:
41
+ return scheme_to_engine[scheme]
42
+
43
+ raise ValueError(
44
+ f"Unsupported database scheme '{scheme}'. "
45
+ f"Supported schemes: {', '.join(scheme_to_engine.keys())}"
46
+ )
47
+
48
+
49
+ def parse_connection_string(connection_string: str) -> Dict[str, Any]:
50
+ """
51
+ Parse database connection string into components.
52
+
53
+ Args:
54
+ connection_string: Database URL to parse
55
+
56
+ Returns:
57
+ Dictionary with parsed components (scheme, user, password, host, port, database, options)
58
+
59
+ Raises:
60
+ DatabaseError: If connection string cannot be parsed
61
+ """
62
+ try:
63
+ parsed = urlparse(connection_string)
64
+
65
+ components = {
66
+ "scheme": parsed.scheme,
67
+ "user": parsed.username,
68
+ "password": parsed.password,
69
+ "host": parsed.hostname,
70
+ "port": parsed.port,
71
+ "database": parsed.path.lstrip("/") if parsed.path else None,
72
+ }
73
+
74
+ # Parse query parameters as options
75
+ if parsed.query:
76
+ from urllib.parse import parse_qs
77
+
78
+ query_params = parse_qs(parsed.query)
79
+ components["options"] = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()}
80
+
81
+ return components
82
+
83
+ except Exception as e:
84
+ # Import here to avoid circular dependency
85
+ from django_cfg.core.exceptions import DatabaseError
86
+
87
+ raise DatabaseError(
88
+ f"Failed to parse connection string: {e}",
89
+ context={"connection_string": connection_string}
90
+ ) from e
91
+
92
+
93
+ __all__ = [
94
+ "detect_engine_from_url",
95
+ "parse_connection_string",
96
+ ]
@@ -0,0 +1,85 @@
1
+ """
2
+ Database routing utilities.
3
+
4
+ Methods for database routing and operation checks.
5
+ """
6
+
7
+ from typing import Optional
8
+
9
+
10
+ def matches_app(config: "DatabaseConfig", app_label: str) -> bool: # type: ignore
11
+ """
12
+ Check if this database should be used for the given app.
13
+
14
+ Args:
15
+ config: DatabaseConfig instance
16
+ app_label: Django app label to check
17
+
18
+ Returns:
19
+ True if this database should be used for the app
20
+ """
21
+ return app_label in config.apps
22
+
23
+
24
+ def allows_operation(config: "DatabaseConfig", operation: str) -> bool: # type: ignore
25
+ """
26
+ Check if this database allows the given operation.
27
+
28
+ Args:
29
+ config: DatabaseConfig instance
30
+ operation: Operation to check ('read', 'write', 'migrate')
31
+
32
+ Returns:
33
+ True if operation is allowed
34
+ """
35
+ return operation in config.operations
36
+
37
+
38
+ def get_migration_database(config: "DatabaseConfig") -> Optional[str]: # type: ignore
39
+ """
40
+ Get the database alias to use for migrations.
41
+
42
+ Args:
43
+ config: DatabaseConfig instance
44
+
45
+ Returns:
46
+ Database alias for migrations, or None to use this database
47
+ """
48
+ return config.migrate_to
49
+
50
+
51
+ def has_routing_rules(config: "DatabaseConfig") -> bool: # type: ignore
52
+ """
53
+ Check if this database has any routing rules configured.
54
+
55
+ Args:
56
+ config: DatabaseConfig instance
57
+
58
+ Returns:
59
+ True if apps list is not empty
60
+ """
61
+ return bool(config.apps)
62
+
63
+
64
+ def test_connection(config: "DatabaseConfig") -> bool: # type: ignore
65
+ """
66
+ Test database connection (placeholder for future implementation).
67
+
68
+ Args:
69
+ config: DatabaseConfig instance
70
+
71
+ Returns:
72
+ True if connection successful, False otherwise
73
+ """
74
+ # TODO: Implement actual connection testing
75
+ # This would require Django to be available and configured
76
+ return True
77
+
78
+
79
+ __all__ = [
80
+ "matches_app",
81
+ "allows_operation",
82
+ "get_migration_database",
83
+ "has_routing_rules",
84
+ "test_connection",
85
+ ]
@@ -0,0 +1,170 @@
1
+ """
2
+ Database configuration validators.
3
+
4
+ Field validators for DatabaseConfig model.
5
+ """
6
+
7
+ from typing import Dict, List, Any, Optional
8
+ from pathlib import Path
9
+ from urllib.parse import urlparse
10
+
11
+ from .parsers import detect_engine_from_url, parse_connection_string
12
+
13
+
14
+ def validate_engine(v: Optional[str]) -> Optional[str]:
15
+ """Validate Django database engine format."""
16
+ if v is None:
17
+ return v
18
+
19
+ if not v.startswith("django.db.backends."):
20
+ raise ValueError(
21
+ f"Invalid database engine '{v}'. "
22
+ "Must start with 'django.db.backends.'"
23
+ )
24
+
25
+ # Common engines validation
26
+ valid_engines = {
27
+ "django.db.backends.postgresql",
28
+ "django.db.backends.mysql",
29
+ "django.db.backends.sqlite3",
30
+ "django.db.backends.oracle",
31
+ }
32
+
33
+ if v not in valid_engines and not v.startswith("django.db.backends."):
34
+ # Allow custom backends but warn about common typos
35
+ common_typos = {
36
+ "postgresql": "django.db.backends.postgresql",
37
+ "postgres": "django.db.backends.postgresql",
38
+ "mysql": "django.db.backends.mysql",
39
+ "sqlite": "django.db.backends.sqlite3",
40
+ "sqlite3": "django.db.backends.sqlite3",
41
+ }
42
+
43
+ if v in common_typos:
44
+ raise ValueError(
45
+ f"Invalid engine '{v}'. Did you mean '{common_typos[v]}'?"
46
+ )
47
+
48
+ return v
49
+
50
+
51
+ def validate_name(v: str) -> str:
52
+ """Validate database name or parse connection string."""
53
+ # Check if it's a connection string
54
+ if "://" in v:
55
+ try:
56
+ parsed = urlparse(v)
57
+ if not parsed.scheme:
58
+ raise ValueError("Invalid connection string format")
59
+ return v
60
+ except Exception as e:
61
+ raise ValueError(f"Invalid connection string: {e}") from e
62
+
63
+ # Regular database name validation
64
+ if v in [":memory:", ""]:
65
+ return v # Special cases for SQLite
66
+
67
+ # Check for path-like names (SQLite files)
68
+ if "/" in v or "\\" in v or v.endswith(".db") or v.endswith(".sqlite3"):
69
+ path = Path(v)
70
+ if path.is_absolute() or v.startswith("./") or v.startswith("../"):
71
+ return v # Valid file path
72
+
73
+ return v
74
+
75
+
76
+ def validate_sslmode(v: str) -> str:
77
+ """Validate SSL mode values."""
78
+ valid_modes = {"disable", "allow", "prefer", "require", "verify-ca", "verify-full"}
79
+
80
+ if v not in valid_modes:
81
+ raise ValueError(
82
+ f"Invalid SSL mode '{v}'. "
83
+ f"Valid options: {', '.join(sorted(valid_modes))}"
84
+ )
85
+
86
+ return v
87
+
88
+
89
+ def validate_connection_consistency(values: Dict[str, Any]) -> Dict[str, Any]:
90
+ """Validate connection parameter consistency and auto-detect engine."""
91
+ # Auto-detect engine if not provided
92
+ if values.get("engine") is None and values.get("name"):
93
+ values["engine"] = detect_engine_from_url(values["name"])
94
+
95
+ return values
96
+
97
+
98
+ def validate_connection_after(config: "DatabaseConfig") -> "DatabaseConfig": # type: ignore
99
+ """Validate connection after model creation."""
100
+ # Parse connection string if present
101
+ if "://" in config.name:
102
+ object.__setattr__(config, '_is_connection_string', True)
103
+ parsed = parse_connection_string(config.name)
104
+ object.__setattr__(config, '_parsed_components', parsed)
105
+
106
+ # Override individual parameters with parsed values if not explicitly set
107
+ if parsed and not config.user and parsed.get("user"):
108
+ object.__setattr__(config, 'user', parsed["user"])
109
+ if parsed and not config.password and parsed.get("password"):
110
+ object.__setattr__(config, 'password', parsed["password"])
111
+ if parsed and config.host == "localhost" and parsed.get("host"):
112
+ object.__setattr__(config, 'host', parsed["host"])
113
+ if parsed and config.port == 5432 and parsed.get("port"):
114
+ object.__setattr__(config, 'port', parsed["port"])
115
+
116
+ # Validate SQLite-specific constraints
117
+ if config.engine == "django.db.backends.sqlite3":
118
+ if config.name not in [":memory:", ""] and not (
119
+ config.name.endswith((".db", ".sqlite", ".sqlite3"))
120
+ or "/" in config.name
121
+ or "\\" in config.name
122
+ ):
123
+ raise ValueError(
124
+ "SQLite database name must be ':memory:', a file path, "
125
+ "or end with .db, .sqlite, or .sqlite3"
126
+ )
127
+
128
+ # Validate PostgreSQL-specific constraints
129
+ elif config.engine == "django.db.backends.postgresql":
130
+ if not config._is_connection_string and not config.name:
131
+ raise ValueError("PostgreSQL database name is required")
132
+
133
+ return config
134
+
135
+
136
+ def validate_apps(v: List[str]) -> List[str]:
137
+ """Validate app labels format."""
138
+ for app in v:
139
+ if not app or not app.replace("_", "").replace(".", "").isalnum():
140
+ raise ValueError(
141
+ f"Invalid app label '{app}'. "
142
+ "App labels must contain only letters, numbers, dots, and underscores"
143
+ )
144
+ return v
145
+
146
+
147
+ def validate_operations(v: List[str]) -> List[str]:
148
+ """Validate operations list."""
149
+ if not v:
150
+ raise ValueError("At least one operation must be specified")
151
+
152
+ valid_ops = {"read", "write", "migrate"}
153
+ for op in v:
154
+ if op not in valid_ops:
155
+ raise ValueError(
156
+ f"Invalid operation '{op}'. "
157
+ f"Valid operations: {', '.join(sorted(valid_ops))}"
158
+ )
159
+ return v
160
+
161
+
162
+ __all__ = [
163
+ "validate_engine",
164
+ "validate_name",
165
+ "validate_sslmode",
166
+ "validate_connection_consistency",
167
+ "validate_connection_after",
168
+ "validate_apps",
169
+ "validate_operations",
170
+ ]