django-cfg 1.3.11__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 (439) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/inlines.py +11 -5
  3. django_cfg/apps/accounts/admin/user_admin.py +39 -16
  4. django_cfg/apps/accounts/serializers/profile.py +1 -1
  5. django_cfg/apps/accounts/services/otp_service.py +18 -11
  6. django_cfg/apps/accounts/signals.py +15 -24
  7. django_cfg/apps/accounts/utils/notifications.py +217 -358
  8. django_cfg/apps/accounts/views/otp.py +2 -2
  9. django_cfg/apps/accounts/views/webhook.py +1 -1
  10. django_cfg/apps/agents/core/django_agent.py +1 -1
  11. django_cfg/apps/api/commands/views.py +66 -83
  12. django_cfg/apps/api/health/drf_views.py +269 -0
  13. django_cfg/apps/api/health/serializers.py +45 -0
  14. django_cfg/apps/api/health/urls.py +6 -1
  15. django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
  16. django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
  17. django_cfg/apps/knowbase/admin/document_admin.py +136 -270
  18. django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
  19. django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
  20. django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
  21. django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
  22. django_cfg/apps/knowbase/config/constance_fields.py +1 -1
  23. django_cfg/apps/knowbase/config/settings.py +2 -2
  24. django_cfg/apps/knowbase/mixins/__init__.py +19 -2
  25. django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
  26. django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
  27. django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
  28. django_cfg/apps/knowbase/mixins/creator.py +10 -10
  29. django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
  30. django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
  31. django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
  32. django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
  33. django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
  34. django_cfg/apps/knowbase/mixins/service.py +2 -2
  35. django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
  36. django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
  37. django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
  38. django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
  39. django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
  40. django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
  41. django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
  42. django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
  43. django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
  44. django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
  45. django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
  46. django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
  47. django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
  48. django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
  49. django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
  50. django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
  51. django_cfg/apps/knowbase/services/embedding/models.py +18 -14
  52. django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
  53. django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
  54. django_cfg/apps/leads/tests.py +1 -1
  55. django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
  56. django_cfg/apps/payments/admin/balance_admin.py +1 -1
  57. django_cfg/apps/payments/admin/currencies_admin.py +1 -1
  58. django_cfg/apps/payments/admin/payments_admin.py +1 -1
  59. django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
  60. django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
  61. django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
  62. django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
  63. django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
  64. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
  65. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
  66. django_cfg/apps/payments/admin_interface/views/base.py +29 -2
  67. django_cfg/apps/payments/apps.py +1 -1
  68. django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
  69. django_cfg/apps/payments/config/helpers.py +3 -2
  70. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
  71. django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
  72. django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
  73. django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
  74. django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
  75. django_cfg/apps/payments/management/commands/test_providers.py +1 -1
  76. django_cfg/apps/payments/middleware/api_access.py +1 -1
  77. django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
  78. django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
  79. django_cfg/apps/payments/models/balance.py +2 -2
  80. django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
  81. django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
  82. django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
  83. django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
  84. django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
  85. django_cfg/apps/payments/models/payments.py +2 -2
  86. django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
  87. django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
  88. django_cfg/apps/payments/services/core/base.py +1 -1
  89. django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
  90. django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
  91. django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
  92. django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
  93. django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
  94. django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
  95. django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
  96. django_cfg/apps/payments/services/core/payment_service.py +124 -612
  97. django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
  98. django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
  99. django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
  100. django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
  101. django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
  102. django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
  103. django_cfg/apps/payments/services/providers/base.py +1 -1
  104. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
  105. django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
  106. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
  107. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
  108. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
  109. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
  110. django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
  111. django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
  112. django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
  113. django_cfg/apps/payments/services/providers/registry.py +1 -1
  114. django_cfg/apps/payments/services/providers/sync_service.py +1 -1
  115. django_cfg/apps/payments/signals/__init__.py +1 -1
  116. django_cfg/apps/payments/signals/api_key_signals.py +1 -1
  117. django_cfg/apps/payments/signals/balance_signals.py +1 -1
  118. django_cfg/apps/payments/signals/payment_signals.py +1 -1
  119. django_cfg/apps/payments/signals/subscription_signals.py +1 -1
  120. django_cfg/apps/payments/views/api/api_keys.py +1 -1
  121. django_cfg/apps/payments/views/api/balances.py +1 -1
  122. django_cfg/apps/payments/views/api/base.py +1 -1
  123. django_cfg/apps/payments/views/api/currencies.py +1 -1
  124. django_cfg/apps/payments/views/api/payments.py +1 -1
  125. django_cfg/apps/payments/views/api/subscriptions.py +1 -1
  126. django_cfg/apps/payments/views/api/webhooks.py +1 -1
  127. django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
  128. django_cfg/apps/payments/views/serializers/balances.py +1 -1
  129. django_cfg/apps/payments/views/serializers/currencies.py +1 -1
  130. django_cfg/apps/payments/views/serializers/payments.py +1 -1
  131. django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
  132. django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
  133. django_cfg/apps/support/admin/support_admin.py +21 -13
  134. django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
  135. django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
  136. django_cfg/apps/support/utils/support_email_service.py +1 -1
  137. django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
  138. django_cfg/apps/tasks/utils/simulator.py +1 -1
  139. django_cfg/apps/tasks/views/dashboard.py +33 -3
  140. django_cfg/apps/urls.py +5 -1
  141. django_cfg/cli/README.md +57 -471
  142. django_cfg/cli/commands/create_project.py +140 -529
  143. django_cfg/cli/main.py +13 -10
  144. django_cfg/core/__init__.py +63 -6
  145. django_cfg/core/base/__init__.py +5 -0
  146. django_cfg/core/base/config_model.py +652 -0
  147. django_cfg/core/builders/__init__.py +11 -0
  148. django_cfg/core/builders/apps_builder.py +258 -0
  149. django_cfg/core/builders/middleware_builder.py +115 -0
  150. django_cfg/core/builders/security_builder.py +96 -0
  151. django_cfg/core/config.py +20 -892
  152. django_cfg/core/constants.py +69 -0
  153. django_cfg/core/environment/__init__.py +9 -0
  154. django_cfg/core/exceptions.py +45 -298
  155. django_cfg/core/generation/__init__.py +51 -0
  156. django_cfg/core/generation/core_generators/__init__.py +0 -0
  157. django_cfg/core/generation/core_generators/settings.py +90 -0
  158. django_cfg/core/generation/core_generators/static.py +82 -0
  159. django_cfg/core/generation/core_generators/templates.py +141 -0
  160. django_cfg/core/generation/data_generators/__init__.py +15 -0
  161. django_cfg/core/generation/data_generators/cache.py +132 -0
  162. django_cfg/core/generation/data_generators/database.py +117 -0
  163. django_cfg/core/generation/generation.py +92 -0
  164. django_cfg/core/generation/integration_generators/__init__.py +21 -0
  165. django_cfg/core/generation/integration_generators/api.py +237 -0
  166. django_cfg/core/generation/integration_generators/sessions.py +65 -0
  167. django_cfg/core/generation/integration_generators/tailwind.py +54 -0
  168. django_cfg/core/generation/integration_generators/tasks.py +92 -0
  169. django_cfg/core/generation/integration_generators/third_party.py +144 -0
  170. django_cfg/core/generation/orchestrator.py +285 -0
  171. django_cfg/core/generation/protocols.py +30 -0
  172. django_cfg/core/generation/security_generators/__init__.py +0 -0
  173. django_cfg/core/generation/utility_generators/__init__.py +24 -0
  174. django_cfg/core/generation/utility_generators/email.py +58 -0
  175. django_cfg/core/generation/utility_generators/i18n.py +66 -0
  176. django_cfg/core/generation/utility_generators/limits.py +58 -0
  177. django_cfg/core/generation/utility_generators/logging.py +66 -0
  178. django_cfg/core/generation/utility_generators/security.py +101 -0
  179. django_cfg/core/generation/utils/__init__.py +0 -0
  180. django_cfg/core/generation/utils/helpers.py +32 -0
  181. django_cfg/core/integration/__init__.py +18 -25
  182. django_cfg/core/integration/display/startup.py +146 -133
  183. django_cfg/core/integration/url_integration.py +13 -2
  184. django_cfg/core/services/__init__.py +5 -0
  185. django_cfg/core/services/config_service.py +121 -0
  186. django_cfg/core/state/__init__.py +9 -0
  187. django_cfg/core/state/registry.py +84 -0
  188. django_cfg/core/types/__init__.py +15 -0
  189. django_cfg/core/types/aliases.py +15 -0
  190. django_cfg/core/types/enums.py +49 -0
  191. django_cfg/dashboard/DEBUG_README.md +105 -0
  192. django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
  193. django_cfg/dashboard/__init__.py +24 -0
  194. django_cfg/dashboard/components.py +308 -0
  195. django_cfg/dashboard/debug.py +176 -0
  196. django_cfg/dashboard/management/__init__.py +0 -0
  197. django_cfg/dashboard/management/commands/__init__.py +0 -0
  198. django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
  199. django_cfg/dashboard/sections/__init__.py +1 -0
  200. django_cfg/dashboard/sections/base.py +128 -0
  201. django_cfg/dashboard/sections/commands.py +32 -0
  202. django_cfg/dashboard/sections/overview.py +394 -0
  203. django_cfg/dashboard/sections/stats.py +48 -0
  204. django_cfg/dashboard/sections/system.py +73 -0
  205. django_cfg/management/commands/check_settings.py +6 -2
  206. django_cfg/management/commands/clear_constance.py +6 -1
  207. django_cfg/management/commands/create_token.py +5 -4
  208. django_cfg/management/commands/generate.py +5 -0
  209. django_cfg/management/commands/list_urls.py +7 -2
  210. django_cfg/management/commands/migrate_all.py +6 -2
  211. django_cfg/management/commands/migrator.py +6 -1
  212. django_cfg/management/commands/rundramatiq.py +6 -1
  213. django_cfg/management/commands/rundramatiq_simulator.py +11 -4
  214. django_cfg/management/commands/runserver_ngrok.py +9 -7
  215. django_cfg/management/commands/script.py +25 -21
  216. django_cfg/management/commands/show_config.py +6 -1
  217. django_cfg/management/commands/show_urls.py +8 -3
  218. django_cfg/management/commands/superuser.py +5 -4
  219. django_cfg/management/commands/task_clear.py +8 -3
  220. django_cfg/management/commands/task_status.py +8 -3
  221. django_cfg/management/commands/test_email.py +6 -1
  222. django_cfg/management/commands/test_telegram.py +6 -1
  223. django_cfg/management/commands/test_twilio.py +6 -1
  224. django_cfg/management/commands/tree.py +7 -4
  225. django_cfg/models/__init__.py +88 -3
  226. django_cfg/models/api/__init__.py +27 -0
  227. django_cfg/models/{api.py → api/config.py} +1 -1
  228. django_cfg/models/api/drf/__init__.py +21 -0
  229. django_cfg/models/api/drf/config.py +101 -0
  230. django_cfg/models/api/drf/redoc.py +31 -0
  231. django_cfg/models/api/drf/spectacular.py +129 -0
  232. django_cfg/models/api/drf/swagger.py +59 -0
  233. django_cfg/models/{api_keys.py → api/keys.py} +16 -6
  234. django_cfg/models/{limits.py → api/limits.py} +0 -1
  235. django_cfg/models/base/__init__.py +14 -0
  236. django_cfg/models/django/__init__.py +16 -0
  237. django_cfg/models/{constance.py → django/constance.py} +1 -1
  238. django_cfg/models/{environment.py → django/environment.py} +1 -1
  239. django_cfg/models/infrastructure/__init__.py +17 -0
  240. django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
  241. django_cfg/models/infrastructure/database/__init__.py +22 -0
  242. django_cfg/models/infrastructure/database/config.py +265 -0
  243. django_cfg/models/infrastructure/database/converters.py +91 -0
  244. django_cfg/models/infrastructure/database/parsers.py +96 -0
  245. django_cfg/models/infrastructure/database/routing.py +85 -0
  246. django_cfg/models/infrastructure/database/validators.py +170 -0
  247. django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
  248. django_cfg/models/{security.py → infrastructure/security.py} +2 -2
  249. django_cfg/models/ngrok/__init__.py +11 -0
  250. django_cfg/models/ngrok/auth.py +37 -0
  251. django_cfg/models/ngrok/config.py +77 -0
  252. django_cfg/models/ngrok/tunnel.py +35 -0
  253. django_cfg/models/payments/__init__.py +20 -0
  254. django_cfg/models/payments/api_keys.py +57 -0
  255. django_cfg/models/{payments.py → payments/config.py} +56 -154
  256. django_cfg/models/payments/providers/__init__.py +15 -0
  257. django_cfg/models/payments/providers/base.py +25 -0
  258. django_cfg/models/payments/providers/nowpayments.py +48 -0
  259. django_cfg/models/services/__init__.py +18 -0
  260. django_cfg/models/services/base.py +65 -0
  261. django_cfg/models/{email.py → services/email.py} +1 -1
  262. django_cfg/models/services/telegram.py +172 -0
  263. django_cfg/models/tasks/__init__.py +51 -0
  264. django_cfg/models/tasks/backends.py +250 -0
  265. django_cfg/models/tasks/config.py +314 -0
  266. django_cfg/models/tasks/utils.py +174 -0
  267. django_cfg/modules/base.py +18 -3
  268. django_cfg/modules/django_admin/decorators/actions.py +1 -1
  269. django_cfg/modules/django_admin/decorators/display.py +1 -1
  270. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
  271. django_cfg/modules/django_cfg_rpc_client/README.md +346 -0
  272. django_cfg/modules/django_cfg_rpc_client/__init__.py +51 -0
  273. django_cfg/modules/django_cfg_rpc_client/client.py +540 -0
  274. django_cfg/modules/django_cfg_rpc_client/config.py +207 -0
  275. django_cfg/modules/django_cfg_rpc_client/dashboard/README.md +517 -0
  276. django_cfg/modules/django_cfg_rpc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
  277. django_cfg/modules/django_cfg_rpc_client/dashboard/__init__.py +11 -0
  278. django_cfg/modules/django_cfg_rpc_client/dashboard/apps.py +22 -0
  279. django_cfg/modules/django_cfg_rpc_client/dashboard/monitor.py +435 -0
  280. django_cfg/modules/django_cfg_rpc_client/dashboard/static/django_cfg_rpc_dashboard/js/dashboard.js +373 -0
  281. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/base.html +76 -0
  282. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/dashboard.html +200 -0
  283. django_cfg/modules/django_cfg_rpc_client/dashboard/urls.py +22 -0
  284. django_cfg/modules/django_cfg_rpc_client/dashboard/urls_admin.py +9 -0
  285. django_cfg/modules/django_cfg_rpc_client/dashboard/views.py +251 -0
  286. django_cfg/modules/django_cfg_rpc_client/exceptions.py +201 -0
  287. django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
  288. django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
  289. django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
  290. django_cfg/modules/django_drf_theme/README.md +207 -0
  291. django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
  292. django_cfg/modules/django_drf_theme/__init__.py +23 -0
  293. django_cfg/modules/django_drf_theme/apps.py +15 -0
  294. django_cfg/modules/django_drf_theme/renderers.py +58 -0
  295. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
  296. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
  297. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
  298. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
  299. django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
  300. django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
  301. django_cfg/modules/django_email/__init__.py +14 -0
  302. django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
  303. django_cfg/modules/django_email/utils.py +40 -0
  304. django_cfg/modules/django_health/__init__.py +9 -0
  305. django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
  306. django_cfg/modules/django_llm/llm/client.py +155 -550
  307. django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
  308. django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
  309. django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
  310. django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
  311. django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
  312. django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
  313. django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
  314. django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
  315. django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
  316. django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
  317. django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
  318. django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
  319. django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
  320. django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
  321. django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
  322. django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
  323. django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
  324. django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
  325. django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
  326. django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
  327. django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
  328. django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
  329. django_cfg/modules/django_llm/translator/translator.py +150 -603
  330. django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
  331. django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
  332. django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
  333. django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
  334. django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
  335. django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
  336. django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
  337. django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
  338. django_cfg/modules/django_logging/__init__.py +14 -0
  339. django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
  340. django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
  341. django_cfg/modules/django_ngrok/__init__.py +39 -0
  342. django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
  343. django_cfg/modules/django_rpc_old/POETRY.md +344 -0
  344. django_cfg/modules/django_rpc_old/README.md +397 -0
  345. django_cfg/modules/django_rpc_old/TESTING.md +358 -0
  346. django_cfg/modules/django_rpc_old/__init__.py +39 -0
  347. django_cfg/modules/django_rpc_old/client.py +531 -0
  348. django_cfg/modules/django_rpc_old/config.py +279 -0
  349. django_cfg/modules/django_rpc_old/exceptions.py +172 -0
  350. django_cfg/modules/django_tailwind/README.md +478 -0
  351. django_cfg/modules/django_tailwind/__init__.py +7 -0
  352. django_cfg/modules/django_tailwind/apps.py +10 -0
  353. django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
  354. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
  355. django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
  356. django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
  357. django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
  358. django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
  359. django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
  360. django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
  361. django_cfg/modules/django_tasks/__init__.py +29 -0
  362. django_cfg/modules/django_tasks/factory.py +127 -0
  363. django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
  364. django_cfg/modules/django_tasks/settings.py +107 -0
  365. django_cfg/modules/django_telegram/__init__.py +29 -0
  366. django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
  367. django_cfg/modules/django_telegram/utils.py +62 -0
  368. django_cfg/modules/django_twilio/__init__.py +54 -107
  369. django_cfg/modules/django_twilio/_imports.py +30 -0
  370. django_cfg/modules/django_twilio/base.py +192 -0
  371. django_cfg/modules/django_twilio/email_otp.py +227 -0
  372. django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
  373. django_cfg/modules/django_twilio/simple_service.py +1 -2
  374. django_cfg/modules/django_twilio/sms.py +94 -0
  375. django_cfg/modules/django_twilio/twilio_service.py +2 -3
  376. django_cfg/modules/django_twilio/unified.py +310 -0
  377. django_cfg/modules/django_twilio/utils.py +190 -0
  378. django_cfg/modules/django_twilio/whatsapp.py +137 -0
  379. django_cfg/modules/django_unfold/callbacks/base.py +198 -7
  380. django_cfg/modules/django_unfold/callbacks/main.py +102 -10
  381. django_cfg/modules/django_unfold/dashboard.py +65 -43
  382. django_cfg/modules/django_unfold/models/config.py +13 -12
  383. django_cfg/modules/django_unfold/models/navigation.py +8 -3
  384. django_cfg/modules/django_unfold/models/tabs.py +2 -2
  385. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
  386. django_cfg/registry/core.py +24 -26
  387. django_cfg/registry/modules.py +5 -2
  388. django_cfg/registry/services.py +20 -3
  389. django_cfg/registry/third_party.py +8 -8
  390. django_cfg/static/admin/css/dashboard.css +260 -0
  391. django_cfg/static/admin/js/commands.js +171 -0
  392. django_cfg/static/admin/js/dashboard.js +126 -0
  393. django_cfg/templates/admin/components/management_commands.js +375 -0
  394. django_cfg/templates/admin/components/progress_bar.html +18 -23
  395. django_cfg/templates/admin/index.html +48 -20
  396. django_cfg/templates/admin/index_new.html +106 -0
  397. django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
  398. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
  399. django_cfg/templates/admin/sections/commands_section.html +626 -0
  400. django_cfg/templates/admin/sections/overview_section.html +112 -0
  401. django_cfg/templates/admin/sections/stats_section.html +35 -0
  402. django_cfg/templates/admin/sections/system_section.html +99 -0
  403. django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
  404. django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
  405. django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
  406. django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
  407. django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
  408. django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
  409. django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
  410. django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
  411. django_cfg/templatetags/django_cfg.py +7 -1
  412. django_cfg/utils/smart_defaults.py +4 -4
  413. django_cfg-1.4.0.dist-info/METADATA +920 -0
  414. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/RECORD +425 -196
  415. django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
  416. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
  417. django_cfg/core/generation.py +0 -621
  418. django_cfg/management/commands/validate_config.py +0 -189
  419. django_cfg/models/database.py +0 -480
  420. django_cfg/models/drf.py +0 -272
  421. django_cfg/models/ngrok.py +0 -122
  422. django_cfg/models/services.py +0 -440
  423. django_cfg/models/tasks.py +0 -550
  424. django_cfg/modules/django_twilio/service.py +0 -942
  425. django_cfg/template_archive/django_sample.zip +0 -0
  426. django_cfg/templates/rest_framework/api.html +0 -12
  427. django_cfg/utils/toolkit.py +0 -703
  428. django_cfg-1.3.11.dist-info/METADATA +0 -1029
  429. /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
  430. /django_cfg/core/{environment.py → environment/detector.py} +0 -0
  431. /django_cfg/models/{cors.py → api/cors.py} +0 -0
  432. /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
  433. /django_cfg/models/{base.py → base/config.py} +0 -0
  434. /django_cfg/models/{cfg.py → base/module.py} +0 -0
  435. /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
  436. /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
  437. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/WHEEL +0 -0
  438. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/entry_points.txt +0 -0
  439. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,540 @@
1
+ """
2
+ Django-CFG RPC Client.
3
+
4
+ Synchronous RPC client enabling Django applications to communicate
5
+ with django-cfg-rpc WebSocket servers via Redis.
6
+
7
+ Works with or without django-cfg-rpc installed:
8
+ - With django-cfg-rpc: Full type safety with Pydantic models
9
+ - Without django-cfg-rpc: Basic dict-based communication
10
+ """
11
+
12
+ import redis
13
+ import json
14
+ import logging
15
+ from uuid import uuid4
16
+ from typing import Optional, TypeVar, Type, Any, Dict
17
+
18
+ from .exceptions import (
19
+ RPCTimeoutError,
20
+ RPCRemoteError,
21
+ RPCConnectionError,
22
+ RPCConfigurationError,
23
+ HAS_DJANGO_CFG_RPC,
24
+ )
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ # Try to import Pydantic from django-cfg-rpc
29
+ if HAS_DJANGO_CFG_RPC:
30
+ try:
31
+ from pydantic import BaseModel
32
+ TParams = TypeVar("TParams", bound=BaseModel)
33
+ TResult = TypeVar("TResult", bound=BaseModel)
34
+ except ImportError:
35
+ # Fallback if pydantic not available
36
+ BaseModel = dict # type: ignore
37
+ TParams = TypeVar("TParams")
38
+ TResult = TypeVar("TResult")
39
+ else:
40
+ BaseModel = dict # type: ignore
41
+ TParams = TypeVar("TParams")
42
+ TResult = TypeVar("TResult")
43
+
44
+
45
+ class DjangoCfgRPCClient:
46
+ """
47
+ Synchronous RPC client for Django to communicate with django-cfg-rpc servers.
48
+
49
+ Features:
50
+ - Uses Redis Streams for reliable request delivery
51
+ - Uses Redis Lists for fast response retrieval
52
+ - Blocks synchronously using BLPOP (no async/await)
53
+ - Handles correlation IDs automatically
54
+ - Type-safe API with Pydantic models (if django-cfg-rpc installed)
55
+ - Connection pooling for performance
56
+ - Automatic cleanup of ephemeral keys
57
+
58
+ Example:
59
+ >>> from django_cfg.modules.django_cfg_rpc_client import get_rpc_client
60
+ >>>
61
+ >>> # With django-cfg-rpc models
62
+ >>> from django_cfg_rpc.models import NotificationRequest, NotificationResponse
63
+ >>> rpc = get_rpc_client()
64
+ >>> result: NotificationResponse = rpc.call(
65
+ ... method="send_notification",
66
+ ... params=NotificationRequest(user_id="123", type="info",
67
+ ... title="Hello", message="World"),
68
+ ... result_model=NotificationResponse
69
+ ... )
70
+ >>>
71
+ >>> # Without django-cfg-rpc (dict-based)
72
+ >>> result = rpc.call_dict(
73
+ ... method="send_notification",
74
+ ... params={"user_id": "123", "type": "info",
75
+ ... "title": "Hello", "message": "World"}
76
+ ... )
77
+ """
78
+
79
+ def __init__(
80
+ self,
81
+ redis_url: Optional[str] = None,
82
+ timeout: int = 30,
83
+ request_stream: str = "stream:requests",
84
+ consumer_group: str = "rpc_group",
85
+ stream_maxlen: int = 10000,
86
+ response_key_prefix: str = "list:response:",
87
+ response_key_ttl: int = 60,
88
+ max_connections: int = 50,
89
+ log_calls: bool = False,
90
+ ):
91
+ """
92
+ Initialize RPC client.
93
+
94
+ Args:
95
+ redis_url: Redis connection URL
96
+ timeout: Default timeout for RPC calls (seconds)
97
+ request_stream: Redis Stream name for requests
98
+ consumer_group: Consumer group name
99
+ stream_maxlen: Maximum stream length
100
+ response_key_prefix: Prefix for response list keys
101
+ response_key_ttl: Response key TTL (seconds)
102
+ max_connections: Maximum Redis connections in pool
103
+ log_calls: Log all RPC calls (verbose)
104
+ """
105
+ self.redis_url = redis_url or self._get_redis_url_from_settings()
106
+ self.default_timeout = timeout
107
+ self.request_stream = request_stream
108
+ self.consumer_group = consumer_group
109
+ self.stream_maxlen = stream_maxlen
110
+ self.response_key_prefix = response_key_prefix
111
+ self.response_key_ttl = response_key_ttl
112
+ self.log_calls = log_calls
113
+
114
+ # Create Redis connection pool
115
+ try:
116
+ self._pool = redis.ConnectionPool.from_url(
117
+ self.redis_url,
118
+ max_connections=max_connections,
119
+ decode_responses=False, # We handle JSON ourselves
120
+ socket_keepalive=True,
121
+ )
122
+ self._redis = redis.Redis(connection_pool=self._pool)
123
+
124
+ # Test connection
125
+ self._redis.ping()
126
+
127
+ logger.info(f"Django-CFG RPC Client initialized: {self.redis_url}")
128
+
129
+ except redis.ConnectionError as e:
130
+ raise RPCConnectionError(
131
+ f"Failed to connect to Redis: {e}",
132
+ redis_url=self.redis_url,
133
+ )
134
+ except Exception as e:
135
+ raise RPCConnectionError(
136
+ f"Failed to initialize RPC client: {e}",
137
+ redis_url=self.redis_url,
138
+ )
139
+
140
+ def _get_redis_url_from_settings(self) -> str:
141
+ """
142
+ Get Redis URL from Django settings.
143
+
144
+ Returns:
145
+ Redis URL string
146
+
147
+ Raises:
148
+ RPCConfigurationError: If settings not configured
149
+ """
150
+ try:
151
+ from django.conf import settings
152
+
153
+ if not hasattr(settings, "DJANGO_CFG_RPC"):
154
+ raise RPCConfigurationError(
155
+ "DJANGO_CFG_RPC not found in Django settings. "
156
+ "Configure DjangoCfgRPCConfig in django-cfg.",
157
+ config_key="DJANGO_CFG_RPC",
158
+ )
159
+
160
+ redis_url = settings.DJANGO_CFG_RPC.get("REDIS_URL")
161
+ if not redis_url:
162
+ raise RPCConfigurationError(
163
+ "REDIS_URL not found in DJANGO_CFG_RPC settings",
164
+ config_key="DJANGO_CFG_RPC.REDIS_URL",
165
+ )
166
+
167
+ return redis_url
168
+
169
+ except ImportError:
170
+ raise RPCConfigurationError(
171
+ "Django not installed. Provide redis_url explicitly or configure Django."
172
+ )
173
+
174
+ def call(
175
+ self,
176
+ method: str,
177
+ params: Any,
178
+ result_model: Optional[Type[TResult]] = None,
179
+ timeout: Optional[int] = None,
180
+ ) -> Any:
181
+ """
182
+ Make synchronous RPC call to django-cfg-rpc server.
183
+
184
+ Args:
185
+ method: RPC method name
186
+ params: Pydantic model or dict with parameters
187
+ result_model: Expected result model class (optional)
188
+ timeout: Optional timeout override (seconds)
189
+
190
+ Returns:
191
+ Pydantic result model instance (if result_model provided) or dict
192
+
193
+ Raises:
194
+ RPCTimeoutError: If timeout exceeded
195
+ RPCRemoteError: If remote execution failed
196
+ ValidationError: If response doesn't match result_model
197
+
198
+ Example:
199
+ >>> from django_cfg_rpc.models import NotificationRequest, NotificationResponse
200
+ >>> result = rpc.call(
201
+ ... method="send_notification",
202
+ ... params=NotificationRequest(user_id="123", type="info",
203
+ ... title="Hello", message="World"),
204
+ ... result_model=NotificationResponse,
205
+ ... timeout=10
206
+ ... )
207
+ >>> print(result.delivered) # True/False
208
+ """
209
+ timeout = timeout or self.default_timeout
210
+
211
+ # Generate correlation ID
212
+ cid = str(uuid4())
213
+ reply_key = f"{self.response_key_prefix}{cid}"
214
+
215
+ # Serialize params
216
+ if HAS_DJANGO_CFG_RPC and hasattr(params, "model_dump_json"):
217
+ params_json = params.model_dump_json()
218
+ elif HAS_DJANGO_CFG_RPC and hasattr(params, "model_dump"):
219
+ params_json = json.dumps(params.model_dump())
220
+ elif isinstance(params, dict):
221
+ params_json = json.dumps(params)
222
+ else:
223
+ params_json = json.dumps({"data": params})
224
+
225
+ # Build RPC request payload
226
+ request_payload = {
227
+ "type": "rpc",
228
+ "method": method,
229
+ "params": json.loads(params_json), # Embedded as dict
230
+ "correlation_id": cid,
231
+ "timeout": timeout,
232
+ }
233
+
234
+ if self.log_calls:
235
+ logger.debug(f"RPC call: {method} (cid={cid})")
236
+
237
+ try:
238
+ # Send request to Redis Stream
239
+ message_id = self._redis.xadd(
240
+ self.request_stream,
241
+ {"payload": json.dumps(request_payload)},
242
+ maxlen=self.stream_maxlen,
243
+ approximate=True,
244
+ )
245
+
246
+ if self.log_calls:
247
+ logger.debug(f"Request sent to stream: {message_id}")
248
+
249
+ # Block waiting for response (BLPOP)
250
+ response_data = self._redis.blpop(reply_key, timeout)
251
+
252
+ if response_data is None:
253
+ # Timeout occurred
254
+ logger.warning(f"RPC timeout: {method} (cid={cid}, timeout={timeout}s)")
255
+ raise RPCTimeoutError(
256
+ f"RPC call '{method}' timed out after {timeout}s",
257
+ method=method,
258
+ timeout_seconds=timeout,
259
+ )
260
+
261
+ # Unpack BLPOP result: (key, value)
262
+ _, response_json = response_data
263
+
264
+ # Parse response
265
+ response_dict = json.loads(response_json)
266
+
267
+ if self.log_calls:
268
+ logger.debug(f"RPC response received: {method}")
269
+
270
+ # Check response type
271
+ if response_dict.get("type") == "error":
272
+ # Error response
273
+ error_data = response_dict.get("error", {})
274
+ raise RPCRemoteError(error_data)
275
+
276
+ # Extract result
277
+ result_data = response_dict.get("result")
278
+
279
+ if result_data is None:
280
+ raise RPCRemoteError({
281
+ "code": "internal_error",
282
+ "message": "Response has no result field",
283
+ })
284
+
285
+ # Deserialize result if model provided
286
+ if result_model and HAS_DJANGO_CFG_RPC:
287
+ try:
288
+ return result_model(**result_data)
289
+ except Exception as e:
290
+ logger.error(f"Failed to deserialize result: {e}")
291
+ # Return raw dict as fallback
292
+ return result_data
293
+ else:
294
+ return result_data
295
+
296
+ finally:
297
+ # Always cleanup response key
298
+ try:
299
+ self._redis.delete(reply_key)
300
+ except Exception as e:
301
+ logger.error(f"Failed to cleanup response key {reply_key}: {e}")
302
+
303
+ def call_dict(
304
+ self,
305
+ method: str,
306
+ params: Dict[str, Any],
307
+ timeout: Optional[int] = None,
308
+ ) -> Dict[str, Any]:
309
+ """
310
+ Make RPC call with dict params (no Pydantic).
311
+
312
+ Args:
313
+ method: RPC method name
314
+ params: Dictionary with parameters
315
+ timeout: Optional timeout override (seconds)
316
+
317
+ Returns:
318
+ Dictionary with result
319
+
320
+ Example:
321
+ >>> result = rpc.call_dict(
322
+ ... method="send_notification",
323
+ ... params={"user_id": "123", "type": "info",
324
+ ... "title": "Hello", "message": "World"}
325
+ ... )
326
+ >>> print(result["delivered"])
327
+ """
328
+ return self.call(method=method, params=params, result_model=None, timeout=timeout)
329
+
330
+ def fire_and_forget(self, method: str, params: Any) -> str:
331
+ """
332
+ Send RPC request without waiting for response.
333
+
334
+ Useful for notifications where result doesn't matter.
335
+ Returns immediately after sending to Redis Stream.
336
+
337
+ Args:
338
+ method: RPC method name
339
+ params: Pydantic model or dict with parameters
340
+
341
+ Returns:
342
+ Message ID from Redis Stream
343
+
344
+ Example:
345
+ >>> rpc.fire_and_forget(
346
+ ... method="log_event",
347
+ ... params={"event": "user_login", "user_id": "123"}
348
+ ... )
349
+ """
350
+ cid = str(uuid4())
351
+
352
+ # Serialize params
353
+ if HAS_DJANGO_CFG_RPC and hasattr(params, "model_dump_json"):
354
+ params_json = params.model_dump_json()
355
+ elif HAS_DJANGO_CFG_RPC and hasattr(params, "model_dump"):
356
+ params_json = json.dumps(params.model_dump())
357
+ elif isinstance(params, dict):
358
+ params_json = json.dumps(params)
359
+ else:
360
+ params_json = json.dumps({"data": params})
361
+
362
+ request_payload = {
363
+ "type": "rpc",
364
+ "method": method,
365
+ "params": json.loads(params_json),
366
+ "correlation_id": cid,
367
+ "timeout": 0, # Indicates fire-and-forget
368
+ }
369
+
370
+ message_id = self._redis.xadd(
371
+ self.request_stream,
372
+ {"payload": json.dumps(request_payload)},
373
+ maxlen=self.stream_maxlen,
374
+ approximate=True,
375
+ )
376
+
377
+ if self.log_calls:
378
+ logger.debug(f"Fire-and-forget: {method} (mid={message_id})")
379
+
380
+ return message_id.decode() if isinstance(message_id, bytes) else str(message_id)
381
+
382
+ def health_check(self, timeout: int = 5) -> bool:
383
+ """
384
+ Check if RPC system is healthy.
385
+
386
+ Attempts to ping Redis.
387
+
388
+ Args:
389
+ timeout: Health check timeout (seconds)
390
+
391
+ Returns:
392
+ True if healthy, False otherwise
393
+
394
+ Example:
395
+ >>> if rpc.health_check():
396
+ ... print("RPC system healthy")
397
+ ... else:
398
+ ... print("RPC system unhealthy")
399
+ """
400
+ try:
401
+ # Try to ping Redis
402
+ ping_result = self._redis.ping()
403
+ if not ping_result:
404
+ logger.error("Health check failed: Redis ping returned False")
405
+ return False
406
+
407
+ return True
408
+
409
+ except Exception as e:
410
+ logger.error(f"Health check failed: {e}")
411
+ return False
412
+
413
+ def get_connection_info(self) -> dict:
414
+ """
415
+ Get connection information.
416
+
417
+ Returns:
418
+ Dictionary with connection details
419
+
420
+ Example:
421
+ >>> info = rpc.get_connection_info()
422
+ >>> print(info["redis_url"])
423
+ >>> print(info["request_stream"])
424
+ """
425
+ return {
426
+ "redis_url": self.redis_url,
427
+ "pool_size": self._pool.max_connections if self._pool else 0,
428
+ "request_stream": self.request_stream,
429
+ "consumer_group": self.consumer_group,
430
+ "default_timeout": self.default_timeout,
431
+ "has_django_cfg_rpc": HAS_DJANGO_CFG_RPC,
432
+ }
433
+
434
+ def close(self):
435
+ """
436
+ Close Redis connection pool.
437
+
438
+ Call this when shutting down application to clean up resources.
439
+
440
+ Example:
441
+ >>> rpc.close()
442
+ """
443
+ if self._pool:
444
+ self._pool.disconnect()
445
+ logger.info("RPC client closed")
446
+
447
+ def __enter__(self):
448
+ """Context manager entry."""
449
+ return self
450
+
451
+ def __exit__(self, exc_type, exc_val, exc_tb):
452
+ """Context manager exit."""
453
+ self.close()
454
+
455
+
456
+ # ==================== Singleton Pattern ====================
457
+
458
+ _rpc_client: Optional[DjangoCfgRPCClient] = None
459
+ _rpc_client_lock = None
460
+
461
+
462
+ def get_rpc_client(force_new: bool = False) -> DjangoCfgRPCClient:
463
+ """
464
+ Get global RPC client instance (singleton).
465
+
466
+ Creates client from Django settings on first call.
467
+ Subsequent calls return the same instance (thread-safe).
468
+
469
+ Args:
470
+ force_new: Force create new instance (for testing)
471
+
472
+ Returns:
473
+ DjangoCfgRPCClient instance
474
+
475
+ Example:
476
+ >>> from django_cfg.modules.django_cfg_rpc_client import get_rpc_client
477
+ >>> rpc = get_rpc_client()
478
+ >>> result = rpc.call(...)
479
+ """
480
+ global _rpc_client, _rpc_client_lock
481
+
482
+ if force_new:
483
+ return _create_client_from_settings()
484
+
485
+ if _rpc_client is None:
486
+ # Thread-safe singleton creation
487
+ import threading
488
+
489
+ if _rpc_client_lock is None:
490
+ _rpc_client_lock = threading.Lock()
491
+
492
+ with _rpc_client_lock:
493
+ if _rpc_client is None:
494
+ _rpc_client = _create_client_from_settings()
495
+
496
+ return _rpc_client
497
+
498
+
499
+ def _create_client_from_settings() -> DjangoCfgRPCClient:
500
+ """
501
+ Create RPC client from Django settings.
502
+
503
+ Returns:
504
+ DjangoCfgRPCClient instance
505
+
506
+ Raises:
507
+ RPCConfigurationError: If settings not configured
508
+ """
509
+ try:
510
+ from django.conf import settings
511
+
512
+ if not hasattr(settings, "DJANGO_CFG_RPC"):
513
+ raise RPCConfigurationError(
514
+ "DJANGO_CFG_RPC not found in Django settings"
515
+ )
516
+
517
+ rpc_settings = settings.DJANGO_CFG_RPC
518
+
519
+ return DjangoCfgRPCClient(
520
+ redis_url=rpc_settings.get("REDIS_URL"),
521
+ timeout=rpc_settings.get("RPC_TIMEOUT", 30),
522
+ request_stream=rpc_settings.get("REQUEST_STREAM", "stream:requests"),
523
+ consumer_group=rpc_settings.get("CONSUMER_GROUP", "rpc_group"),
524
+ stream_maxlen=rpc_settings.get("STREAM_MAXLEN", 10000),
525
+ response_key_prefix=rpc_settings.get("RESPONSE_KEY_PREFIX", "list:response:"),
526
+ response_key_ttl=rpc_settings.get("RESPONSE_KEY_TTL", 60),
527
+ max_connections=rpc_settings.get("REDIS_MAX_CONNECTIONS", 50),
528
+ log_calls=rpc_settings.get("LOG_RPC_CALLS", False),
529
+ )
530
+
531
+ except ImportError:
532
+ raise RPCConfigurationError(
533
+ "Django not installed. Cannot create client from settings."
534
+ )
535
+
536
+
537
+ __all__ = [
538
+ "DjangoCfgRPCClient",
539
+ "get_rpc_client",
540
+ ]