django-cfg 1.3.13__py3-none-any.whl → 1.4.3__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 (446) 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/agents/examples/__init__.py +3 -0
  11. django_cfg/apps/agents/examples/simple_example.py +161 -0
  12. django_cfg/apps/api/commands/views.py +66 -83
  13. django_cfg/apps/api/health/drf_views.py +269 -0
  14. django_cfg/apps/api/health/serializers.py +45 -0
  15. django_cfg/apps/api/health/urls.py +6 -1
  16. django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
  17. django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
  18. django_cfg/apps/knowbase/admin/document_admin.py +136 -270
  19. django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
  20. django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
  21. django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
  22. django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
  23. django_cfg/apps/knowbase/config/constance_fields.py +1 -1
  24. django_cfg/apps/knowbase/config/settings.py +2 -2
  25. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  26. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  27. django_cfg/apps/knowbase/mixins/__init__.py +19 -2
  28. django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
  29. django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
  30. django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
  31. django_cfg/apps/knowbase/mixins/creator.py +10 -10
  32. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  33. django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
  34. django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
  35. django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
  36. django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
  37. django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
  38. django_cfg/apps/knowbase/mixins/service.py +2 -2
  39. django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
  40. django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
  41. django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
  42. django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
  43. django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
  44. django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
  45. django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
  46. django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
  47. django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
  48. django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
  49. django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
  50. django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
  51. django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
  52. django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
  53. django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
  54. django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
  55. django_cfg/apps/knowbase/services/embedding/models.py +18 -14
  56. django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
  57. django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
  58. django_cfg/apps/leads/tests.py +1 -1
  59. django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
  60. django_cfg/apps/payments/admin/balance_admin.py +1 -1
  61. django_cfg/apps/payments/admin/currencies_admin.py +1 -1
  62. django_cfg/apps/payments/admin/payments_admin.py +1 -1
  63. django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
  64. django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
  65. django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
  66. django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
  67. django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
  68. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
  69. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
  70. django_cfg/apps/payments/admin_interface/views/base.py +29 -2
  71. django_cfg/apps/payments/apps.py +1 -1
  72. django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
  73. django_cfg/apps/payments/config/helpers.py +3 -2
  74. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
  75. django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
  76. django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
  77. django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
  78. django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
  79. django_cfg/apps/payments/management/commands/test_providers.py +1 -1
  80. django_cfg/apps/payments/middleware/api_access.py +1 -1
  81. django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
  82. django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
  83. django_cfg/apps/payments/models/balance.py +2 -2
  84. django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
  85. django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
  86. django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
  87. django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
  88. django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
  89. django_cfg/apps/payments/models/payments.py +2 -2
  90. django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
  91. django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
  92. django_cfg/apps/payments/services/core/base.py +1 -1
  93. django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
  94. django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
  95. django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
  96. django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
  97. django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
  98. django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
  99. django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
  100. django_cfg/apps/payments/services/core/payment_service.py +124 -612
  101. django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
  102. django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
  103. django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
  104. django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
  105. django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
  106. django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
  107. django_cfg/apps/payments/services/providers/base.py +1 -1
  108. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
  109. django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
  110. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
  111. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
  112. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
  113. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
  114. django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
  115. django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
  116. django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
  117. django_cfg/apps/payments/services/providers/registry.py +1 -1
  118. django_cfg/apps/payments/services/providers/sync_service.py +1 -1
  119. django_cfg/apps/payments/signals/__init__.py +1 -1
  120. django_cfg/apps/payments/signals/api_key_signals.py +1 -1
  121. django_cfg/apps/payments/signals/balance_signals.py +1 -1
  122. django_cfg/apps/payments/signals/payment_signals.py +1 -1
  123. django_cfg/apps/payments/signals/subscription_signals.py +1 -1
  124. django_cfg/apps/payments/views/api/api_keys.py +1 -1
  125. django_cfg/apps/payments/views/api/balances.py +1 -1
  126. django_cfg/apps/payments/views/api/base.py +1 -1
  127. django_cfg/apps/payments/views/api/currencies.py +1 -1
  128. django_cfg/apps/payments/views/api/payments.py +1 -1
  129. django_cfg/apps/payments/views/api/subscriptions.py +1 -1
  130. django_cfg/apps/payments/views/api/webhooks.py +1 -1
  131. django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
  132. django_cfg/apps/payments/views/serializers/balances.py +1 -1
  133. django_cfg/apps/payments/views/serializers/currencies.py +1 -1
  134. django_cfg/apps/payments/views/serializers/payments.py +1 -1
  135. django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
  136. django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
  137. django_cfg/apps/support/admin/support_admin.py +21 -13
  138. django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
  139. django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
  140. django_cfg/apps/support/utils/support_email_service.py +1 -1
  141. django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
  142. django_cfg/apps/tasks/utils/simulator.py +1 -1
  143. django_cfg/apps/tasks/views/dashboard.py +33 -3
  144. django_cfg/apps/urls.py +5 -1
  145. django_cfg/cli/README.md +57 -471
  146. django_cfg/cli/commands/create_project.py +140 -529
  147. django_cfg/cli/main.py +13 -10
  148. django_cfg/core/__init__.py +63 -6
  149. django_cfg/core/base/__init__.py +5 -0
  150. django_cfg/core/base/config_model.py +652 -0
  151. django_cfg/core/builders/__init__.py +11 -0
  152. django_cfg/core/builders/apps_builder.py +258 -0
  153. django_cfg/core/builders/middleware_builder.py +115 -0
  154. django_cfg/core/builders/security_builder.py +96 -0
  155. django_cfg/core/config.py +20 -892
  156. django_cfg/core/constants.py +69 -0
  157. django_cfg/core/environment/__init__.py +9 -0
  158. django_cfg/core/exceptions.py +45 -298
  159. django_cfg/core/generation/__init__.py +51 -0
  160. django_cfg/core/generation/core_generators/__init__.py +0 -0
  161. django_cfg/core/generation/core_generators/settings.py +90 -0
  162. django_cfg/core/generation/core_generators/static.py +82 -0
  163. django_cfg/core/generation/core_generators/templates.py +141 -0
  164. django_cfg/core/generation/data_generators/__init__.py +15 -0
  165. django_cfg/core/generation/data_generators/cache.py +132 -0
  166. django_cfg/core/generation/data_generators/database.py +117 -0
  167. django_cfg/core/generation/generation.py +92 -0
  168. django_cfg/core/generation/integration_generators/__init__.py +21 -0
  169. django_cfg/core/generation/integration_generators/api.py +237 -0
  170. django_cfg/core/generation/integration_generators/sessions.py +65 -0
  171. django_cfg/core/generation/integration_generators/tailwind.py +54 -0
  172. django_cfg/core/generation/integration_generators/tasks.py +92 -0
  173. django_cfg/core/generation/integration_generators/third_party.py +144 -0
  174. django_cfg/core/generation/orchestrator.py +285 -0
  175. django_cfg/core/generation/protocols.py +30 -0
  176. django_cfg/core/generation/security_generators/__init__.py +0 -0
  177. django_cfg/core/generation/utility_generators/__init__.py +24 -0
  178. django_cfg/core/generation/utility_generators/email.py +58 -0
  179. django_cfg/core/generation/utility_generators/i18n.py +66 -0
  180. django_cfg/core/generation/utility_generators/limits.py +58 -0
  181. django_cfg/core/generation/utility_generators/logging.py +66 -0
  182. django_cfg/core/generation/utility_generators/security.py +101 -0
  183. django_cfg/core/generation/utils/__init__.py +0 -0
  184. django_cfg/core/generation/utils/helpers.py +32 -0
  185. django_cfg/core/integration/__init__.py +18 -25
  186. django_cfg/core/integration/display/startup.py +146 -133
  187. django_cfg/core/integration/url_integration.py +13 -2
  188. django_cfg/core/services/__init__.py +5 -0
  189. django_cfg/core/services/config_service.py +121 -0
  190. django_cfg/core/state/__init__.py +9 -0
  191. django_cfg/core/state/registry.py +84 -0
  192. django_cfg/core/types/__init__.py +15 -0
  193. django_cfg/core/types/aliases.py +15 -0
  194. django_cfg/core/types/enums.py +49 -0
  195. django_cfg/dashboard/DEBUG_README.md +105 -0
  196. django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
  197. django_cfg/dashboard/__init__.py +24 -0
  198. django_cfg/dashboard/components.py +308 -0
  199. django_cfg/dashboard/debug.py +176 -0
  200. django_cfg/dashboard/management/__init__.py +0 -0
  201. django_cfg/dashboard/management/commands/__init__.py +0 -0
  202. django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
  203. django_cfg/dashboard/sections/__init__.py +1 -0
  204. django_cfg/dashboard/sections/base.py +128 -0
  205. django_cfg/dashboard/sections/commands.py +32 -0
  206. django_cfg/dashboard/sections/overview.py +394 -0
  207. django_cfg/dashboard/sections/stats.py +48 -0
  208. django_cfg/dashboard/sections/system.py +73 -0
  209. django_cfg/management/commands/check_settings.py +6 -2
  210. django_cfg/management/commands/clear_constance.py +6 -1
  211. django_cfg/management/commands/create_token.py +5 -4
  212. django_cfg/management/commands/generate.py +5 -0
  213. django_cfg/management/commands/list_urls.py +7 -2
  214. django_cfg/management/commands/migrate_all.py +6 -2
  215. django_cfg/management/commands/migrator.py +6 -1
  216. django_cfg/management/commands/rundramatiq.py +6 -1
  217. django_cfg/management/commands/rundramatiq_simulator.py +11 -4
  218. django_cfg/management/commands/runserver_ngrok.py +9 -7
  219. django_cfg/management/commands/script.py +25 -21
  220. django_cfg/management/commands/show_config.py +6 -1
  221. django_cfg/management/commands/show_urls.py +8 -3
  222. django_cfg/management/commands/superuser.py +5 -4
  223. django_cfg/management/commands/task_clear.py +8 -3
  224. django_cfg/management/commands/task_status.py +8 -3
  225. django_cfg/management/commands/test_email.py +6 -1
  226. django_cfg/management/commands/test_telegram.py +6 -1
  227. django_cfg/management/commands/test_twilio.py +6 -1
  228. django_cfg/management/commands/tree.py +7 -4
  229. django_cfg/models/__init__.py +88 -3
  230. django_cfg/models/api/__init__.py +27 -0
  231. django_cfg/models/{api.py → api/config.py} +1 -1
  232. django_cfg/models/api/drf/__init__.py +21 -0
  233. django_cfg/models/api/drf/config.py +101 -0
  234. django_cfg/models/api/drf/redoc.py +31 -0
  235. django_cfg/models/api/drf/spectacular.py +129 -0
  236. django_cfg/models/api/drf/swagger.py +59 -0
  237. django_cfg/models/{api_keys.py → api/keys.py} +16 -6
  238. django_cfg/models/{limits.py → api/limits.py} +0 -1
  239. django_cfg/models/base/__init__.py +14 -0
  240. django_cfg/models/django/__init__.py +16 -0
  241. django_cfg/models/{constance.py → django/constance.py} +1 -1
  242. django_cfg/models/{environment.py → django/environment.py} +1 -1
  243. django_cfg/models/infrastructure/__init__.py +17 -0
  244. django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
  245. django_cfg/models/infrastructure/database/__init__.py +22 -0
  246. django_cfg/models/infrastructure/database/config.py +265 -0
  247. django_cfg/models/infrastructure/database/converters.py +91 -0
  248. django_cfg/models/infrastructure/database/parsers.py +96 -0
  249. django_cfg/models/infrastructure/database/routing.py +85 -0
  250. django_cfg/models/infrastructure/database/validators.py +170 -0
  251. django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
  252. django_cfg/models/{security.py → infrastructure/security.py} +2 -2
  253. django_cfg/models/ngrok/__init__.py +11 -0
  254. django_cfg/models/ngrok/auth.py +37 -0
  255. django_cfg/models/ngrok/config.py +77 -0
  256. django_cfg/models/ngrok/tunnel.py +35 -0
  257. django_cfg/models/payments/__init__.py +20 -0
  258. django_cfg/models/payments/api_keys.py +57 -0
  259. django_cfg/models/{payments.py → payments/config.py} +56 -154
  260. django_cfg/models/payments/providers/__init__.py +15 -0
  261. django_cfg/models/payments/providers/base.py +25 -0
  262. django_cfg/models/payments/providers/nowpayments.py +48 -0
  263. django_cfg/models/services/__init__.py +18 -0
  264. django_cfg/models/services/base.py +65 -0
  265. django_cfg/models/{email.py → services/email.py} +1 -1
  266. django_cfg/models/services/telegram.py +172 -0
  267. django_cfg/models/tasks/__init__.py +51 -0
  268. django_cfg/models/tasks/backends.py +250 -0
  269. django_cfg/models/tasks/config.py +314 -0
  270. django_cfg/models/tasks/utils.py +174 -0
  271. django_cfg/modules/base.py +18 -3
  272. django_cfg/modules/django_admin/decorators/actions.py +1 -1
  273. django_cfg/modules/django_admin/decorators/display.py +1 -1
  274. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
  275. django_cfg/modules/django_currency/examples/__init__.py +3 -0
  276. django_cfg/modules/django_currency/examples/example_database_usage.py +144 -0
  277. django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
  278. django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
  279. django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
  280. django_cfg/modules/django_drf_theme/README.md +207 -0
  281. django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
  282. django_cfg/modules/django_drf_theme/__init__.py +23 -0
  283. django_cfg/modules/django_drf_theme/apps.py +15 -0
  284. django_cfg/modules/django_drf_theme/renderers.py +58 -0
  285. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
  286. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
  287. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
  288. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
  289. django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
  290. django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
  291. django_cfg/modules/django_email/__init__.py +14 -0
  292. django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
  293. django_cfg/modules/django_email/utils.py +40 -0
  294. django_cfg/modules/django_health/__init__.py +9 -0
  295. django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
  296. django_cfg/modules/django_ipc_client/README.md +346 -0
  297. django_cfg/modules/django_ipc_client/__init__.py +51 -0
  298. django_cfg/modules/django_ipc_client/client.py +540 -0
  299. django_cfg/modules/django_ipc_client/config.py +207 -0
  300. django_cfg/modules/django_ipc_client/dashboard/README.md +517 -0
  301. django_cfg/modules/django_ipc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
  302. django_cfg/modules/django_ipc_client/dashboard/__init__.py +11 -0
  303. django_cfg/modules/django_ipc_client/dashboard/apps.py +22 -0
  304. django_cfg/modules/django_ipc_client/dashboard/monitor.py +435 -0
  305. django_cfg/modules/django_ipc_client/dashboard/static/django_ipc_dashboard/js/dashboard.js +373 -0
  306. django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/base.html +76 -0
  307. django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/dashboard.html +200 -0
  308. django_cfg/modules/django_ipc_client/dashboard/urls.py +22 -0
  309. django_cfg/modules/django_ipc_client/dashboard/urls_admin.py +9 -0
  310. django_cfg/modules/django_ipc_client/dashboard/views.py +251 -0
  311. django_cfg/modules/django_ipc_client/exceptions.py +201 -0
  312. django_cfg/modules/django_llm/llm/client.py +155 -550
  313. django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
  314. django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
  315. django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
  316. django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
  317. django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
  318. django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
  319. django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
  320. django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
  321. django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
  322. django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
  323. django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
  324. django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
  325. django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
  326. django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
  327. django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
  328. django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
  329. django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
  330. django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
  331. django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
  332. django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
  333. django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
  334. django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
  335. django_cfg/modules/django_llm/translator/translator.py +150 -603
  336. django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
  337. django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
  338. django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
  339. django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
  340. django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
  341. django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
  342. django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
  343. django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
  344. django_cfg/modules/django_logging/__init__.py +14 -0
  345. django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
  346. django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
  347. django_cfg/modules/django_ngrok/__init__.py +39 -0
  348. django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
  349. django_cfg/modules/django_rpc_old/POETRY.md +344 -0
  350. django_cfg/modules/django_rpc_old/README.md +397 -0
  351. django_cfg/modules/django_rpc_old/TESTING.md +358 -0
  352. django_cfg/modules/django_rpc_old/__init__.py +39 -0
  353. django_cfg/modules/django_rpc_old/client.py +531 -0
  354. django_cfg/modules/django_rpc_old/config.py +279 -0
  355. django_cfg/modules/django_rpc_old/exceptions.py +172 -0
  356. django_cfg/modules/django_tailwind/README.md +478 -0
  357. django_cfg/modules/django_tailwind/__init__.py +7 -0
  358. django_cfg/modules/django_tailwind/apps.py +10 -0
  359. django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
  360. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
  361. django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
  362. django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
  363. django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
  364. django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
  365. django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
  366. django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
  367. django_cfg/modules/django_tasks/__init__.py +29 -0
  368. django_cfg/modules/django_tasks/factory.py +127 -0
  369. django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
  370. django_cfg/modules/django_tasks/settings.py +107 -0
  371. django_cfg/modules/django_telegram/__init__.py +29 -0
  372. django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
  373. django_cfg/modules/django_telegram/utils.py +62 -0
  374. django_cfg/modules/django_twilio/__init__.py +54 -107
  375. django_cfg/modules/django_twilio/_imports.py +30 -0
  376. django_cfg/modules/django_twilio/base.py +192 -0
  377. django_cfg/modules/django_twilio/email_otp.py +227 -0
  378. django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
  379. django_cfg/modules/django_twilio/simple_service.py +1 -2
  380. django_cfg/modules/django_twilio/sms.py +94 -0
  381. django_cfg/modules/django_twilio/twilio_service.py +2 -3
  382. django_cfg/modules/django_twilio/unified.py +310 -0
  383. django_cfg/modules/django_twilio/utils.py +190 -0
  384. django_cfg/modules/django_twilio/whatsapp.py +137 -0
  385. django_cfg/modules/django_unfold/callbacks/base.py +198 -7
  386. django_cfg/modules/django_unfold/callbacks/main.py +102 -10
  387. django_cfg/modules/django_unfold/dashboard.py +65 -43
  388. django_cfg/modules/django_unfold/models/config.py +13 -12
  389. django_cfg/modules/django_unfold/models/navigation.py +8 -3
  390. django_cfg/modules/django_unfold/models/tabs.py +2 -2
  391. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
  392. django_cfg/registry/core.py +24 -26
  393. django_cfg/registry/modules.py +5 -2
  394. django_cfg/registry/services.py +20 -3
  395. django_cfg/registry/third_party.py +8 -8
  396. django_cfg/static/admin/css/dashboard.css +260 -0
  397. django_cfg/static/admin/js/commands.js +171 -0
  398. django_cfg/static/admin/js/dashboard.js +126 -0
  399. django_cfg/templates/admin/components/management_commands.js +375 -0
  400. django_cfg/templates/admin/components/progress_bar.html +18 -23
  401. django_cfg/templates/admin/examples/component_class_example.html +156 -0
  402. django_cfg/templates/admin/index.html +48 -20
  403. django_cfg/templates/admin/index_new.html +106 -0
  404. django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
  405. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
  406. django_cfg/templates/admin/sections/commands_section.html +626 -0
  407. django_cfg/templates/admin/sections/overview_section.html +112 -0
  408. django_cfg/templates/admin/sections/stats_section.html +35 -0
  409. django_cfg/templates/admin/sections/system_section.html +99 -0
  410. django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
  411. django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
  412. django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
  413. django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
  414. django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
  415. django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
  416. django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
  417. django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
  418. django_cfg/templatetags/django_cfg.py +7 -1
  419. django_cfg/utils/smart_defaults.py +4 -4
  420. django_cfg-1.4.3.dist-info/METADATA +533 -0
  421. {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/RECORD +432 -195
  422. django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
  423. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
  424. django_cfg/core/generation.py +0 -621
  425. django_cfg/management/commands/validate_config.py +0 -189
  426. django_cfg/models/database.py +0 -480
  427. django_cfg/models/drf.py +0 -272
  428. django_cfg/models/ngrok.py +0 -122
  429. django_cfg/models/services.py +0 -440
  430. django_cfg/models/tasks.py +0 -550
  431. django_cfg/modules/django_twilio/service.py +0 -942
  432. django_cfg/template_archive/django_sample.zip +0 -0
  433. django_cfg/templates/rest_framework/api.html +0 -12
  434. django_cfg/utils/toolkit.py +0 -703
  435. django_cfg-1.3.13.dist-info/METADATA +0 -1029
  436. /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
  437. /django_cfg/core/{environment.py → environment/detector.py} +0 -0
  438. /django_cfg/models/{cors.py → api/cors.py} +0 -0
  439. /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
  440. /django_cfg/models/{base.py → base/config.py} +0 -0
  441. /django_cfg/models/{cfg.py → base/module.py} +0 -0
  442. /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
  443. /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
  444. {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/WHEEL +0 -0
  445. {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/entry_points.txt +0 -0
  446. {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/licenses/LICENSE +0 -0
@@ -11,38 +11,38 @@ Usage:
11
11
  class MyModel(ExternalDataMixin, models.Model):
12
12
  name = models.CharField(max_length=100)
13
13
  description = models.TextField()
14
-
14
+
15
15
  class Meta:
16
16
  # Standard Django Meta options...
17
-
17
+
18
18
  class ExternalDataMeta:
19
19
  # Required: fields to watch for changes
20
20
  watch_fields = ['name', 'description']
21
-
21
+
22
22
  # Optional: similarity threshold (default: 0.5)
23
23
  similarity_threshold = 0.4
24
-
24
+
25
25
  # Optional: source type (default: ExternalDataType.MODEL)
26
26
  source_type = ExternalDataType.CUSTOM
27
-
27
+
28
28
  # Optional: enable/disable auto-sync (default: True)
29
29
  auto_sync = True
30
-
30
+
31
31
  # Optional: make public (default: False)
32
32
  is_public = False
33
-
33
+
34
34
  # Required: content generation method
35
35
  def get_external_content(self):
36
36
  return f"# {self.name}\n\n{self.description}"
37
-
37
+
38
38
  # Optional: custom title (default: str(instance))
39
39
  def get_external_title(self):
40
40
  return f"My Model: {self.name}"
41
-
41
+
42
42
  # Optional: custom description (default: auto-generated)
43
43
  def get_external_description(self):
44
44
  return f"Information about {self.name}"
45
-
45
+
46
46
  # Optional: metadata (default: basic model info)
47
47
  def get_external_metadata(self):
48
48
  return {
@@ -50,7 +50,7 @@ Usage:
50
50
  'model_id': str(self.id),
51
51
  'name': self.name,
52
52
  }
53
-
53
+
54
54
  # Optional: tags (default: [model_name.lower()])
55
55
  def get_external_tags(self):
56
56
  return ['my_model', self.name.lower()]
@@ -58,15 +58,17 @@ Usage:
58
58
 
59
59
  import logging
60
60
  import hashlib
61
- from typing import Optional, List, Dict, Any, Type
61
+ from typing import Optional, List, Dict, Any
62
62
  from django.db import models
63
63
  from django.db.models.signals import post_save, post_delete
64
- from django.dispatch import receiver
65
- from django.contrib.contenttypes.models import ContentType
66
64
 
67
65
  from ..models.external_data import ExternalData, ExternalDataType, ExternalDataStatus
68
66
  from .creator import ExternalDataCreator
69
- from .config import ExternalDataConfig
67
+ from .config import ExternalDataMetaConfig, ExternalDataMetaParser
68
+ from .generators import (
69
+ ExternalDataContentGenerator,
70
+ ExternalDataMetadataGenerator,
71
+ )
70
72
 
71
73
  logger = logging.getLogger(__name__)
72
74
 
@@ -74,23 +76,23 @@ logger = logging.getLogger(__name__)
74
76
  class ExternalDataMixin(models.Model):
75
77
  """
76
78
  Mixin that automatically integrates models with knowbase ExternalData system.
77
-
79
+
78
80
  Provides:
79
81
  - Automatic external_source_id field
80
82
  - Change tracking and vectorization
81
83
  - Simple configuration interface
82
84
  - Automatic cleanup on deletion
83
85
  """
84
-
86
+
85
87
  # Automatically added field for linking to ExternalData
86
88
  external_source_id = models.UUIDField(
87
- null=True,
88
- blank=True,
89
+ null=True,
90
+ blank=True,
89
91
  db_index=True,
90
92
  help_text="UUID of the linked ExternalData object in knowbase",
91
93
  verbose_name="External Source ID"
92
94
  )
93
-
95
+
94
96
  # Track content hash for change detection
95
97
  _external_content_hash = models.CharField(
96
98
  max_length=64,
@@ -98,27 +100,27 @@ class ExternalDataMixin(models.Model):
98
100
  help_text="SHA256 hash of content for change detection",
99
101
  verbose_name="Content Hash"
100
102
  )
101
-
103
+
102
104
  class Meta:
103
105
  abstract = True
104
-
106
+
105
107
  def __init_subclass__(cls, **kwargs):
106
108
  """Register signal handlers for each subclass."""
107
109
  super().__init_subclass__(**kwargs)
108
-
110
+
109
111
  # Register signals for this specific model class
110
112
  post_save.connect(
111
113
  cls._external_data_post_save_handler,
112
114
  sender=cls,
113
115
  dispatch_uid=f"external_data_mixin_{cls.__name__}"
114
116
  )
115
-
117
+
116
118
  post_delete.connect(
117
119
  cls._external_data_post_delete_handler,
118
120
  sender=cls,
119
121
  dispatch_uid=f"external_data_mixin_delete_{cls.__name__}"
120
122
  )
121
-
123
+
122
124
  @classmethod
123
125
  def _external_data_post_save_handler(cls, sender, instance, created, **kwargs):
124
126
  """Handle post_save signal for ExternalData integration."""
@@ -126,39 +128,39 @@ class ExternalDataMixin(models.Model):
126
128
  meta_config = cls._get_external_data_meta()
127
129
  if not meta_config or not meta_config.get('auto_sync', True):
128
130
  return
129
-
131
+
130
132
  # Check if we should process this save (only if watched fields changed)
131
133
  if not created and not cls._should_update_external_data(instance, kwargs):
132
134
  logger.debug(f"📊 No relevant field changes for {cls.__name__}: {instance}")
133
135
  return
134
-
136
+
135
137
  # Check if content changed
136
138
  current_content = cls._get_content_for_instance(instance)
137
139
  current_hash = cls._calculate_content_hash(current_content)
138
-
140
+
139
141
  if created:
140
142
  # New instance - create ExternalData
141
143
  logger.info(f"🔗 Creating ExternalData for new {cls.__name__}: {instance}")
142
144
  instance._external_content_hash = current_hash
143
145
  instance.save(update_fields=['_external_content_hash'])
144
146
  cls._create_external_data(instance)
145
-
147
+
146
148
  elif instance._external_content_hash != current_hash:
147
149
  # Content changed - update ExternalData
148
150
  logger.info(f"🔮 Content changed for {cls.__name__}: {instance}, updating ExternalData")
149
151
  instance._external_content_hash = current_hash
150
152
  instance.save(update_fields=['_external_content_hash'])
151
-
153
+
152
154
  if instance.external_source_id:
153
155
  cls._update_external_data(instance)
154
156
  else:
155
157
  cls._create_external_data(instance)
156
158
  else:
157
159
  logger.debug(f"📊 No content changes for {cls.__name__}: {instance}")
158
-
160
+
159
161
  except Exception as e:
160
162
  logger.error(f"❌ Error in ExternalData post_save handler for {cls.__name__}: {e}")
161
-
163
+
162
164
  @classmethod
163
165
  def _external_data_post_delete_handler(cls, sender, instance, **kwargs):
164
166
  """Handle post_delete signal for ExternalData cleanup."""
@@ -168,61 +170,32 @@ class ExternalDataMixin(models.Model):
168
170
  ExternalData.objects.filter(id=instance.external_source_id).delete()
169
171
  except Exception as e:
170
172
  logger.error(f"❌ Error cleaning up ExternalData for {cls.__name__}: {e}")
171
-
173
+
172
174
  @classmethod
173
175
  def _get_external_data_meta(cls) -> Dict[str, Any]:
174
176
  """Get ExternalDataMeta configuration from the model or auto-generate smart defaults."""
175
- config = {}
176
-
177
- # If ExternalDataMeta exists, use it
178
- if hasattr(cls, 'ExternalDataMeta'):
179
- meta_class = cls.ExternalDataMeta
180
- # Extract configuration from ExternalDataMeta
181
- for attr in dir(meta_class):
182
- if not attr.startswith('_'):
183
- value = getattr(meta_class, attr)
184
- if not callable(value): # Only properties, not methods
185
- config[attr] = value
186
-
187
- # Smart defaults based on model analysis
188
- if 'watch_fields' not in config:
189
- config['watch_fields'] = cls._auto_detect_watch_fields()
190
-
191
- if 'similarity_threshold' not in config:
192
- config['similarity_threshold'] = 0.5 # Balanced default
193
-
194
- if 'source_type' not in config:
195
- from ..models.external_data import ExternalDataType
196
- config['source_type'] = ExternalDataType.MODEL # Smart default
197
-
198
- if 'auto_sync' not in config:
199
- config['auto_sync'] = True # Enable by default
200
-
201
- if 'is_public' not in config:
202
- config['is_public'] = False # Private by default for security
203
-
204
- return config
205
-
177
+ return ExternalDataMetaParser.parse(cls)
178
+
206
179
  @classmethod
207
180
  def _should_update_external_data(cls, instance, save_kwargs) -> bool:
208
181
  """Check if we should update ExternalData based on changed fields."""
209
182
  meta_config = cls._get_external_data_meta()
210
183
  if not meta_config:
211
184
  return True # No config = update always
212
-
185
+
213
186
  watch_fields = meta_config.get('watch_fields', [])
214
187
  if not watch_fields:
215
188
  return True # No watch fields = update always
216
-
189
+
217
190
  # Check if update_fields was used in save()
218
191
  update_fields = save_kwargs.get('update_fields')
219
192
  if update_fields is not None:
220
193
  # Only update if any watched field was updated
221
194
  return any(field in update_fields for field in watch_fields)
222
-
195
+
223
196
  # If no update_fields specified, assume all fields might have changed
224
197
  return True
225
-
198
+
226
199
  @classmethod
227
200
  def _get_content_for_instance(cls, instance) -> str:
228
201
  """Get content string for the instance."""
@@ -231,10 +204,11 @@ class ExternalDataMixin(models.Model):
231
204
  return str(instance.get_external_content())
232
205
  except Exception as e:
233
206
  logger.warning(f"Error calling get_external_content on {cls.__name__}: {e}")
234
-
235
- # Smart auto-generation based on model fields
236
- return cls._auto_generate_content(instance)
237
-
207
+
208
+ # Use generator for auto-generation
209
+ generator = ExternalDataContentGenerator(instance)
210
+ return generator.generate_content()
211
+
238
212
  @classmethod
239
213
  def _get_title_for_instance(cls, instance) -> str:
240
214
  """Get title for the instance."""
@@ -243,10 +217,11 @@ class ExternalDataMixin(models.Model):
243
217
  return str(instance.get_external_title())
244
218
  except Exception as e:
245
219
  logger.warning(f"Error calling get_external_title on {cls.__name__}: {e}")
246
-
247
- # Smart auto-generation based on model fields
248
- return cls._auto_generate_title(instance)
249
-
220
+
221
+ # Use generator for auto-generation
222
+ generator = ExternalDataContentGenerator(instance)
223
+ return generator.generate_title()
224
+
250
225
  @classmethod
251
226
  def _get_description_for_instance(cls, instance) -> str:
252
227
  """Get description for the instance."""
@@ -255,10 +230,11 @@ class ExternalDataMixin(models.Model):
255
230
  return str(instance.get_external_description())
256
231
  except Exception as e:
257
232
  logger.warning(f"Error calling get_external_description on {cls.__name__}: {e}")
258
-
259
- # Smart auto-generation based on model fields
260
- return cls._auto_generate_description(instance)
261
-
233
+
234
+ # Use generator for auto-generation
235
+ generator = ExternalDataMetadataGenerator(instance)
236
+ return generator.generate_description()
237
+
262
238
  @classmethod
263
239
  def _get_tags_for_instance(cls, instance) -> List[str]:
264
240
  """Get tags for the instance."""
@@ -270,15 +246,16 @@ class ExternalDataMixin(models.Model):
270
246
  return [str(tags)]
271
247
  except Exception as e:
272
248
  logger.warning(f"Error calling get_external_tags on {cls.__name__}: {e}")
273
-
274
- # Smart auto-generation based on model fields
275
- return cls._auto_generate_tags(instance)
276
-
249
+
250
+ # Use generator for auto-generation
251
+ generator = ExternalDataMetadataGenerator(instance)
252
+ return generator.generate_tags()
253
+
277
254
  @classmethod
278
255
  def _calculate_content_hash(cls, content: str) -> str:
279
256
  """Calculate SHA256 hash of content."""
280
257
  return hashlib.sha256(content.encode('utf-8')).hexdigest()
281
-
258
+
282
259
  @classmethod
283
260
  def _create_external_data(cls, instance):
284
261
  """Create ExternalData for the instance."""
@@ -287,12 +264,12 @@ class ExternalDataMixin(models.Model):
287
264
  if not meta_config:
288
265
  logger.warning(f"No ExternalDataMeta found for {cls.__name__}")
289
266
  return
290
-
267
+
291
268
  # Get user (try to find from instance or use superuser)
292
269
  user = cls._get_user_for_instance(instance)
293
-
294
- # Build ExternalDataConfig
295
- external_config = ExternalDataConfig(
270
+
271
+ # Build ExternalDataMetaConfig
272
+ external_config = ExternalDataMetaConfig(
296
273
  title=cls._get_title_for_instance(instance),
297
274
  description=cls._get_description_for_instance(instance),
298
275
  source_type=meta_config.get('source_type', ExternalDataType.MODEL),
@@ -310,11 +287,11 @@ class ExternalDataMixin(models.Model):
310
287
  'watch_fields': meta_config.get('watch_fields', []),
311
288
  }
312
289
  )
313
-
290
+
314
291
  # Create ExternalData
315
292
  creator = ExternalDataCreator(user)
316
293
  result = creator.create_from_config(external_config)
317
-
294
+
318
295
  if result['success']:
319
296
  external_data = result['external_data']
320
297
  instance.external_source_id = external_data.id
@@ -322,20 +299,20 @@ class ExternalDataMixin(models.Model):
322
299
  logger.info(f"✅ Created ExternalData {external_data.id} for {cls.__name__}: {instance}")
323
300
  else:
324
301
  logger.error(f"❌ Failed to create ExternalData for {cls.__name__}: {result.get('error')}")
325
-
302
+
326
303
  except Exception as e:
327
304
  logger.error(f"❌ Error creating ExternalData for {cls.__name__}: {e}")
328
-
305
+
329
306
  @classmethod
330
307
  def _update_external_data(cls, instance):
331
308
  """Update existing ExternalData for the instance."""
332
309
  try:
333
310
  if not instance.external_source_id:
334
311
  return
335
-
312
+
336
313
  external_data = ExternalData.objects.get(id=instance.external_source_id)
337
314
  meta_config = cls._get_external_data_meta() or {}
338
-
315
+
339
316
  # Update fields using the same methods as creation
340
317
  external_data.title = cls._get_title_for_instance(instance)
341
318
  external_data.description = cls._get_description_for_instance(instance)
@@ -344,16 +321,16 @@ class ExternalDataMixin(models.Model):
344
321
  external_data.tags = cls._get_tags_for_instance(instance)
345
322
  external_data.similarity_threshold = meta_config.get('similarity_threshold', 0.5)
346
323
  external_data.status = ExternalDataStatus.PENDING # Mark for reprocessing
347
-
324
+
348
325
  external_data.save()
349
326
  logger.info(f"✅ Updated ExternalData {external_data.id} for {cls.__name__}: {instance}")
350
-
327
+
351
328
  except ExternalData.DoesNotExist:
352
329
  logger.warning(f"ExternalData {instance.external_source_id} not found, creating new one")
353
330
  cls._create_external_data(instance)
354
331
  except Exception as e:
355
332
  logger.error(f"❌ Error updating ExternalData for {cls.__name__}: {e}")
356
-
333
+
357
334
  @classmethod
358
335
  def _build_metadata(cls, instance, config: Dict[str, Any]) -> Dict[str, Any]:
359
336
  """Build metadata dictionary for ExternalData."""
@@ -365,7 +342,7 @@ class ExternalDataMixin(models.Model):
365
342
  'created_at': getattr(instance, 'created_at', None),
366
343
  'updated_at': getattr(instance, 'updated_at', None),
367
344
  }
368
-
345
+
369
346
  # Add custom metadata if method exists
370
347
  if hasattr(instance, 'get_external_metadata'):
371
348
  try:
@@ -374,14 +351,14 @@ class ExternalDataMixin(models.Model):
374
351
  metadata.update(custom_metadata)
375
352
  except Exception as e:
376
353
  logger.warning(f"Error calling get_external_metadata on {cls.__name__}: {e}")
377
-
354
+
378
355
  # Convert datetime objects to strings
379
356
  for key, value in metadata.items():
380
357
  if hasattr(value, 'isoformat'):
381
358
  metadata[key] = value.isoformat()
382
-
359
+
383
360
  return metadata
384
-
361
+
385
362
  @classmethod
386
363
  def _get_user_for_instance(cls, instance):
387
364
  """Get user for ExternalData ownership."""
@@ -392,23 +369,23 @@ class ExternalDataMixin(models.Model):
392
369
  return instance.created_by
393
370
  if hasattr(instance, 'owner'):
394
371
  return instance.owner
395
-
396
- # Fallback to superuser
372
+
373
+ # Fallback to staff user
397
374
  from django.contrib.auth import get_user_model
398
375
  User = get_user_model()
399
- superuser = User.objects.filter(is_superuser=True).first()
400
- if superuser:
401
- return superuser
402
-
376
+ staff_user = User.objects.filter(is_staff=True).first()
377
+ if staff_user:
378
+ return staff_user
379
+
403
380
  raise ValueError("No user found for ExternalData ownership")
404
-
381
+
405
382
  def regenerate_external_data(self):
406
383
  """Manually regenerate ExternalData for this instance."""
407
384
  if self.external_source_id:
408
385
  self._update_external_data(self)
409
386
  else:
410
387
  self._create_external_data(self)
411
-
388
+
412
389
  def create_external_data(self, user=None):
413
390
  """Create ExternalData for this instance if it doesn't exist."""
414
391
  if self.external_source_id:
@@ -417,7 +394,7 @@ class ExternalDataMixin(models.Model):
417
394
  'error': f'External data already exists: {self.external_source_id}',
418
395
  'external_data': None
419
396
  }
420
-
397
+
421
398
  try:
422
399
  self._create_external_data(self)
423
400
  if self.external_source_id:
@@ -438,7 +415,7 @@ class ExternalDataMixin(models.Model):
438
415
  'error': f'Error creating external data: {str(e)}',
439
416
  'external_data': None
440
417
  }
441
-
418
+
442
419
  def delete_external_data(self):
443
420
  """Manually delete ExternalData for this instance."""
444
421
  if self.external_source_id:
@@ -449,328 +426,53 @@ class ExternalDataMixin(models.Model):
449
426
  logger.info(f"🗑️ Deleted ExternalData for {self.__class__.__name__}: {self}")
450
427
  except Exception as e:
451
428
  logger.error(f"❌ Error deleting ExternalData: {e}")
452
-
429
+
453
430
  @property
454
431
  def has_external_data(self) -> bool:
455
432
  """Check if this instance has linked ExternalData."""
456
433
  return bool(self.external_source_id)
457
-
434
+
458
435
  @property
459
436
  def external_data_status(self) -> Optional[str]:
460
437
  """Get status of linked ExternalData."""
461
438
  if not self.external_source_id:
462
439
  return None
463
-
440
+
464
441
  try:
465
442
  external_data = ExternalData.objects.get(id=self.external_source_id)
466
443
  return external_data.status
467
444
  except ExternalData.DoesNotExist:
468
445
  return None
469
-
470
- # ==========================================
471
- # SMART AUTO-GENERATION METHODS
472
- # ==========================================
473
-
474
- @classmethod
475
- def _auto_generate_title(cls, instance) -> str:
476
- """Auto-generate title based on model fields."""
477
- # Try common title fields first
478
- title_fields = ['title', 'name', 'full_name', 'display_name', 'label']
479
-
480
- for field_name in title_fields:
481
- if hasattr(instance, field_name):
482
- value = getattr(instance, field_name, None)
483
- if value and str(value).strip():
484
- # Add model context for clarity
485
- model_name = cls._meta.verbose_name or cls.__name__
486
- return f"{model_name}: {value}"
487
-
488
- # Fallback: use string representation with model name
489
- model_name = cls._meta.verbose_name or cls.__name__
490
- return f"{model_name}: {instance}"
491
-
492
- @classmethod
493
- def _auto_generate_description(cls, instance) -> str:
494
- """Auto-generate description based on model fields."""
495
- model_name = cls._meta.verbose_name or cls.__name__
496
-
497
- # Try common description fields
498
- desc_fields = ['description', 'summary', 'about', 'details', 'info']
499
- for field_name in desc_fields:
500
- if hasattr(instance, field_name):
501
- value = getattr(instance, field_name, None)
502
- if value and str(value).strip():
503
- return f"{model_name} information: {value}"
504
-
505
- # Build description from key fields
506
- key_info = []
507
-
508
- # Add primary identifier
509
- if hasattr(instance, 'name') and instance.name:
510
- key_info.append(f"Name: {instance.name}")
511
- elif hasattr(instance, 'title') and instance.title:
512
- key_info.append(f"Title: {instance.title}")
513
-
514
- # Add status if available
515
- if hasattr(instance, 'is_active'):
516
- status = "Active" if instance.is_active else "Inactive"
517
- key_info.append(f"Status: {status}")
518
-
519
- # Add creation date if available
520
- if hasattr(instance, 'created_at') and instance.created_at:
521
- key_info.append(f"Created: {instance.created_at.strftime('%Y-%m-%d')}")
522
-
523
- if key_info:
524
- return f"Comprehensive information about this {model_name.lower()}. {', '.join(key_info)}."
525
-
526
- return f"Auto-generated information from {model_name} model."
527
-
528
- @classmethod
529
- def _auto_generate_tags(cls, instance) -> List[str]:
530
- """Auto-generate tags based on model fields and metadata."""
531
- tags = []
532
-
533
- # Add model-based tags
534
- tags.append(cls.__name__.lower())
535
- if cls._meta.verbose_name:
536
- tags.append(cls._meta.verbose_name.lower().replace(' ', '_'))
537
-
538
- # Add app label
539
- tags.append(cls._meta.app_label)
540
-
541
- # Add field-based tags
542
- tag_fields = ['category', 'type', 'kind', 'status', 'brand', 'model']
543
- for field_name in tag_fields:
544
- if hasattr(instance, field_name):
545
- value = getattr(instance, field_name, None)
546
- if value:
547
- # Handle foreign key relationships
548
- if hasattr(value, 'name'):
549
- tags.append(str(value.name).lower())
550
- elif hasattr(value, 'code'):
551
- tags.append(str(value.code).lower())
552
- else:
553
- tags.append(str(value).lower())
554
-
555
- # Add boolean field tags
556
- bool_fields = ['is_active', 'is_public', 'is_featured', 'is_published']
557
- for field_name in bool_fields:
558
- if hasattr(instance, field_name):
559
- value = getattr(instance, field_name, None)
560
- if value is True:
561
- tags.append(field_name.replace('is_', ''))
562
-
563
- # Clean and deduplicate tags
564
- clean_tags = []
565
- for tag in tags:
566
- clean_tag = str(tag).lower().strip().replace(' ', '_')
567
- if clean_tag and clean_tag not in clean_tags:
568
- clean_tags.append(clean_tag)
569
-
570
- return clean_tags[:10] # Limit to 10 tags
571
-
572
- @classmethod
573
- def _auto_generate_content(cls, instance) -> str:
574
- """Auto-generate comprehensive content based on model fields."""
575
- content_parts = []
576
-
577
- # Header with title
578
- title = cls._auto_generate_title(instance)
579
- content_parts.append(f"# {title}")
580
- content_parts.append("")
581
-
582
- # Basic Information section
583
- content_parts.append("## Basic Information")
584
-
585
- # Add key fields
586
- key_fields = cls._get_content_fields(instance)
587
- for field_name, field_value, field_label in key_fields:
588
- if field_value is not None and str(field_value).strip():
589
- content_parts.append(f"- **{field_label}**: {field_value}")
590
-
591
- content_parts.append("")
592
-
593
- # Add relationships section if any
594
- relationships = cls._get_relationship_info(instance)
595
- if relationships:
596
- content_parts.append("## Related Information")
597
- for rel_name, rel_info in relationships.items():
598
- content_parts.append(f"- **{rel_name}**: {rel_info}")
599
- content_parts.append("")
600
-
601
- # Add statistics if available
602
- stats = cls._get_statistics_info(instance)
603
- if stats:
604
- content_parts.append("## Statistics")
605
- for stat_name, stat_value in stats.items():
606
- content_parts.append(f"- **{stat_name}**: {stat_value}")
607
- content_parts.append("")
608
-
609
- # Add metadata section
610
- content_parts.append("## Technical Information")
611
- content_parts.append(f"This data is automatically synchronized from the {cls.__name__} model using ExternalDataMixin.")
612
- content_parts.append(f"")
613
- content_parts.append(f"**Model**: {cls._meta.label}")
614
- content_parts.append(f"**ID**: {instance.pk}")
615
- if hasattr(instance, 'created_at') and instance.created_at:
616
- content_parts.append(f"**Created**: {instance.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
617
- if hasattr(instance, 'updated_at') and instance.updated_at:
618
- content_parts.append(f"**Updated**: {instance.updated_at.strftime('%Y-%m-%d %H:%M:%S')}")
619
-
620
- return "\n".join(content_parts)
621
-
622
- @classmethod
623
- def _get_content_fields(cls, instance) -> List[tuple]:
624
- """Get fields to include in content generation."""
625
- fields_info = []
626
-
627
- # Define field priority and labels
628
- priority_fields = {
629
- 'name': 'Name',
630
- 'title': 'Title',
631
- 'code': 'Code',
632
- 'description': 'Description',
633
- 'summary': 'Summary',
634
- 'body_type': 'Body Type',
635
- 'segment': 'Segment',
636
- 'category': 'Category',
637
- 'type': 'Type',
638
- 'status': 'Status',
639
- 'is_active': 'Active',
640
- 'is_public': 'Public',
641
- 'price': 'Price',
642
- 'year': 'Year',
643
- 'fuel_type': 'Fuel Type',
644
- }
645
-
646
- # Add priority fields first
647
- for field_name, field_label in priority_fields.items():
648
- if hasattr(instance, field_name):
649
- value = getattr(instance, field_name, None)
650
- if value is not None:
651
- # Format boolean fields
652
- if isinstance(value, bool):
653
- value = "Yes" if value else "No"
654
- # Format choice fields
655
- elif hasattr(instance, f'get_{field_name}_display'):
656
- display_value = getattr(instance, f'get_{field_name}_display')()
657
- if display_value:
658
- value = display_value
659
- # Format foreign key relationships
660
- elif hasattr(value, '__str__'):
661
- value = str(value)
662
-
663
- fields_info.append((field_name, value, field_label))
664
-
665
- return fields_info
666
-
667
- @classmethod
668
- def _get_relationship_info(cls, instance) -> Dict[str, str]:
669
- """Get relationship information for content."""
670
- relationships = {}
671
-
672
- # Common relationship field names
673
- rel_fields = ['brand', 'category', 'parent', 'owner', 'user', 'created_by']
674
-
675
- for field_name in rel_fields:
676
- if hasattr(instance, field_name):
677
- value = getattr(instance, field_name, None)
678
- if value:
679
- relationships[field_name.replace('_', ' ').title()] = str(value)
680
-
681
- return relationships
682
-
683
- @classmethod
684
- def _get_statistics_info(cls, instance) -> Dict[str, Any]:
685
- """Get statistics information for content."""
686
- stats = {}
687
-
688
- # Common statistics field names
689
- stat_fields = ['total_vehicles', 'total_models', 'total_items', 'count', 'views', 'likes']
690
-
691
- for field_name in stat_fields:
692
- if hasattr(instance, field_name):
693
- value = getattr(instance, field_name, None)
694
- if value is not None and (isinstance(value, (int, float)) and value > 0):
695
- label = field_name.replace('_', ' ').title()
696
- if isinstance(value, float):
697
- stats[label] = f"{value:,.2f}"
698
- else:
699
- stats[label] = f"{value:,}"
700
-
701
- return stats
702
-
703
- @classmethod
704
- def _auto_detect_watch_fields(cls) -> List[str]:
705
- """Auto-detect important fields to watch for changes."""
706
- watch_fields = []
707
-
708
- # Get all model fields
709
- for field in cls._meta.get_fields():
710
- if hasattr(field, 'name') and not field.name.startswith('_'):
711
- field_name = field.name
712
-
713
- # Skip auto-generated and system fields
714
- skip_fields = {
715
- 'id', 'pk', 'created_at', 'updated_at', 'external_source_id',
716
- '_external_content_hash', 'slug'
717
- }
718
- if field_name in skip_fields:
719
- continue
720
-
721
- # Skip reverse foreign keys and many-to-many
722
- if hasattr(field, 'related_model') and field.many_to_many:
723
- continue
724
- if hasattr(field, 'remote_field') and field.remote_field and hasattr(field.remote_field, 'related_name'):
725
- continue
726
-
727
- # Include important field types
728
- if hasattr(field, '__class__'):
729
- field_type = field.__class__.__name__
730
- important_types = {
731
- 'CharField', 'TextField', 'BooleanField', 'IntegerField',
732
- 'PositiveIntegerField', 'ForeignKey', 'DecimalField', 'FloatField'
733
- }
734
- if field_type in important_types:
735
- watch_fields.append(field_name)
736
-
737
- # If no fields detected, watch all non-system fields
738
- if not watch_fields:
739
- for field in cls._meta.get_fields():
740
- if hasattr(field, 'name') and not field.name.startswith('_') and field.name not in {'id', 'pk'}:
741
- watch_fields.append(field.name)
742
-
743
- return watch_fields[:10] # Limit to prevent too many triggers
744
-
446
+
745
447
  # ==========================================
746
448
  # MANAGER-LEVEL METHODS (CLASS METHODS)
747
449
  # ==========================================
748
-
450
+
749
451
  @classmethod
750
452
  def with_external_data(cls):
751
453
  """Return queryset of instances that have external data."""
752
454
  return cls.objects.filter(external_source_id__isnull=False)
753
-
455
+
754
456
  @classmethod
755
457
  def without_external_data(cls):
756
458
  """Return queryset of instances that don't have external data."""
757
459
  return cls.objects.filter(external_source_id__isnull=True)
758
-
460
+
759
461
  @classmethod
760
462
  def sync_all_external_data(cls, limit=None):
761
463
  """Sync external data for all instances that have it."""
762
464
  instances_with_data = cls.with_external_data()
763
-
465
+
764
466
  if limit:
765
467
  instances_with_data = instances_with_data[:limit]
766
-
468
+
767
469
  results = {
768
470
  'total_processed': 0,
769
471
  'successful_updates': 0,
770
472
  'failed_updates': 0,
771
473
  'errors': []
772
474
  }
773
-
475
+
774
476
  for instance in instances_with_data:
775
477
  try:
776
478
  instance.regenerate_external_data()
@@ -779,24 +481,24 @@ class ExternalDataMixin(models.Model):
779
481
  except Exception as e:
780
482
  results['failed_updates'] += 1
781
483
  results['errors'].append(f"{instance}: {str(e)}")
782
-
484
+
783
485
  return results
784
-
486
+
785
487
  @classmethod
786
488
  def create_external_data_for_all(cls, limit=None):
787
489
  """Create external data for all instances that don't have it."""
788
490
  instances_without_data = cls.without_external_data()
789
-
491
+
790
492
  if limit:
791
493
  instances_without_data = instances_without_data[:limit]
792
-
494
+
793
495
  results = {
794
496
  'total_processed': 0,
795
497
  'successful_creates': 0,
796
498
  'failed_creates': 0,
797
499
  'errors': []
798
500
  }
799
-
501
+
800
502
  for instance in instances_without_data:
801
503
  try:
802
504
  result = instance.create_external_data()
@@ -809,5 +511,5 @@ class ExternalDataMixin(models.Model):
809
511
  except Exception as e:
810
512
  results['failed_creates'] += 1
811
513
  results['errors'].append(f"{instance}: {str(e)}")
812
-
514
+
813
515
  return results