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
@@ -5,23 +5,17 @@ Enhanced document management with Material Icons and optimized queries.
5
5
  """
6
6
 
7
7
  from django.contrib import admin, messages
8
- from django.urls import reverse
9
- from django.utils.safestring import mark_safe
10
8
  from django.db import models, IntegrityError
11
- from django.db.models import Count, Sum, Avg, Q
12
9
  from django.db.models.fields.json import JSONField
13
10
  from django_json_widget.widgets import JSONEditorWidget
14
11
  from unfold.admin import ModelAdmin, TabularInline
15
12
  from unfold.contrib.filters.admin import AutocompleteSelectFilter, AutocompleteSelectMultipleFilter
16
13
  from unfold.contrib.forms.widgets import WysiwygWidget
17
- from django_cfg import ImportExportModelAdmin, ExportMixin, ImportForm, ExportForm
14
+ from django_cfg import ImportExportModelAdmin, ImportForm, ExportForm
18
15
 
19
16
  from django_cfg.modules.django_admin import (
20
17
  OptimizedModelAdmin,
21
18
  DisplayMixin,
22
- MoneyDisplayConfig,
23
- StatusBadgeConfig,
24
- DateTimeDisplayConfig,
25
19
  Icons,
26
20
  ActionVariant,
27
21
  display,
@@ -30,11 +24,19 @@ from django_cfg.modules.django_admin import (
30
24
  from django_cfg.modules.django_admin.utils.badges import StatusBadge
31
25
 
32
26
  from ..models import Document, DocumentChunk, DocumentCategory
27
+ from .helpers import (
28
+ DocumentDisplayHelpers,
29
+ DocumentStatistics,
30
+ ChunkStatistics,
31
+ CategoryStatistics,
32
+ DocumentAdminConfigs
33
+ )
34
+ from .actions import VisibilityActions
33
35
 
34
36
 
35
37
  class DocumentChunkInline(TabularInline):
36
38
  """Inline for document chunks with Unfold styling."""
37
-
39
+
38
40
  model = DocumentChunk
39
41
  verbose_name = "Document Chunk"
40
42
  verbose_name_plural = "📄 Document Chunks (Read-only)"
@@ -42,40 +44,38 @@ class DocumentChunkInline(TabularInline):
42
44
  max_num = 0
43
45
  can_delete = False
44
46
  show_change_link = True
45
-
47
+
46
48
  def has_add_permission(self, request, obj=None):
47
49
  return False
48
-
50
+
49
51
  def has_change_permission(self, request, obj=None):
50
52
  return False
51
-
53
+
52
54
  def has_delete_permission(self, request, obj=None):
53
55
  return False
54
-
56
+
55
57
  fields = [
56
- 'short_uuid', 'chunk_index', 'content_preview_inline', 'token_count',
58
+ 'short_uuid', 'chunk_index', 'content_preview_inline', 'token_count',
57
59
  'has_embedding_inline', 'embedding_cost'
58
60
  ]
59
61
  readonly_fields = [
60
62
  'short_uuid', 'chunk_index', 'content_preview_inline', 'token_count', 'character_count',
61
63
  'has_embedding_inline', 'embedding_cost', 'created_at'
62
64
  ]
63
-
65
+
64
66
  hide_title = False
65
67
  classes = ['collapse']
66
-
68
+
67
69
  @display(description="Content Preview")
68
70
  def content_preview_inline(self, obj):
69
71
  """Shortened content preview for inline display."""
70
- if not obj.content:
71
- return "—"
72
- return obj.content[:100] + "..." if len(obj.content) > 100 else obj.content
73
-
72
+ return DocumentDisplayHelpers.display_content_preview(obj, max_length=100)
73
+
74
74
  @display(description="Has Embedding", boolean=True)
75
75
  def has_embedding_inline(self, obj):
76
76
  """Check if chunk has embedding vector for inline."""
77
77
  return obj.embedding is not None and len(obj.embedding) > 0
78
-
78
+
79
79
  def get_queryset(self, request):
80
80
  """Optimize queryset for inline display."""
81
81
  return super().get_queryset(request).select_related('document', 'user')
@@ -84,17 +84,17 @@ class DocumentChunkInline(TabularInline):
84
84
  @admin.register(Document)
85
85
  class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportModelAdmin):
86
86
  """Admin interface for Document model using Django Admin Utilities."""
87
-
87
+
88
88
  # Performance optimization
89
89
  select_related_fields = ['user']
90
-
90
+
91
91
  # Import/Export configuration
92
92
  import_form_class = ImportForm
93
93
  export_form_class = ExportForm
94
-
94
+
95
95
  list_display = [
96
- 'title_display', 'categories_display', 'user_display',
97
- 'visibility_display', 'status_display', 'chunks_count_display',
96
+ 'title_display', 'categories_display', 'user_display',
97
+ 'visibility_display', 'status_display', 'chunks_count_display',
98
98
  'vectorization_progress', 'tokens_display', 'cost_display', 'created_at_display'
99
99
  ]
100
100
  list_display_links = ['title_display']
@@ -113,7 +113,7 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
113
113
  'processing_error', 'processing_duration', 'processing_status',
114
114
  'total_cost_usd', 'created_at', 'updated_at', 'duplicate_check'
115
115
  ]
116
-
116
+
117
117
  fieldsets = (
118
118
  ('📄 Basic Information', {
119
119
  'fields': ('id', 'title', 'user', 'categories', 'is_public', 'file_type', 'file_size'),
@@ -125,7 +125,7 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
125
125
  }),
126
126
  ('⚙️ Processing Status', {
127
127
  'fields': (
128
- 'processing_status', 'processing_started_at',
128
+ 'processing_status', 'processing_started_at',
129
129
  'processing_completed_at', 'processing_error'
130
130
  ),
131
131
  'classes': ('tab',)
@@ -145,38 +145,39 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
145
145
  })
146
146
  )
147
147
  filter_horizontal = ['categories']
148
-
148
+
149
149
  # Unfold configuration
150
150
  compressed_fields = True
151
151
  warn_unsaved_form = True
152
-
152
+
153
153
  # Form field overrides
154
154
  formfield_overrides = {
155
155
  models.TextField: {"widget": WysiwygWidget},
156
156
  JSONField: {"widget": JSONEditorWidget}
157
157
  }
158
-
158
+
159
159
  actions = ['reprocess_documents', 'mark_as_public', 'mark_as_private']
160
-
160
+
161
161
  def get_queryset(self, request):
162
162
  """Optimize queryset with select_related and prefetch_related."""
163
163
  queryset = Document.objects.all_users().select_related('user').prefetch_related('categories')
164
-
165
- if not request.user.is_superuser:
164
+
165
+ # Staff users see all documents, regular users see only their own
166
+ if not request.user.is_staff:
166
167
  queryset = queryset.filter(user=request.user)
167
-
168
+
168
169
  return queryset
169
-
170
+
170
171
  def save_model(self, request, obj, form, change):
171
172
  """Automatically set user to current user when creating new documents."""
172
173
  if not change:
173
174
  obj.user = request.user
174
-
175
+
175
176
  is_duplicate, existing_doc = Document.objects.check_duplicate_before_save(
176
- user=obj.user,
177
+ user=obj.user,
177
178
  content=obj.content
178
179
  )
179
-
180
+
180
181
  if is_duplicate and existing_doc:
181
182
  messages.error(
182
183
  request,
@@ -185,7 +186,7 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
185
186
  f'Please modify the content or update the existing document.'
186
187
  )
187
188
  return
188
-
189
+
189
190
  try:
190
191
  super().save_model(request, obj, form, change)
191
192
  except IntegrityError as e:
@@ -198,72 +199,54 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
198
199
  else:
199
200
  messages.error(request, f'Database error: {str(e)}')
200
201
  raise
201
-
202
+
202
203
  @display(description="Document Title", ordering="title")
203
204
  def title_display(self, obj):
204
205
  """Display document title with truncation."""
205
206
  title = obj.title or "Untitled Document"
206
207
  if len(title) > 50:
207
208
  title = title[:47] + "..."
208
-
209
- config = StatusBadgeConfig(show_icons=True, icon=Icons.DESCRIPTION)
209
+
210
210
  return StatusBadge.create(
211
211
  text=title,
212
212
  variant="primary",
213
- config=config
213
+ config=DocumentAdminConfigs.DOCUMENT_TITLE
214
214
  )
215
-
216
- @display(description="User")
215
+
217
216
  def user_display(self, obj):
218
- """User display."""
219
- if not obj.user:
220
- return "—"
221
- return self.display_user_simple(obj.user)
222
-
223
- @display(description="Visibility")
217
+ """User display (delegates to helper)."""
218
+ return DocumentDisplayHelpers.display_user(obj, self)
219
+
224
220
  def visibility_display(self, obj):
225
- """Display visibility status."""
226
- if obj.is_public:
227
- config = StatusBadgeConfig(show_icons=True, icon=Icons.PUBLIC)
228
- return StatusBadge.create(text="Public", variant="success", config=config)
229
- else:
230
- config = StatusBadgeConfig(show_icons=True, icon=Icons.LOCK)
231
- return StatusBadge.create(text="Private", variant="danger", config=config)
232
-
221
+ """Visibility display (delegates to helper)."""
222
+ return DocumentDisplayHelpers.display_visibility(obj)
223
+
233
224
  @display(description="Status")
234
225
  def status_display(self, obj):
235
226
  """Display processing status."""
236
- status_config = StatusBadgeConfig(
237
- custom_mappings={
238
- 'pending': 'warning',
239
- 'processing': 'info',
240
- 'completed': 'success',
241
- 'failed': 'danger',
242
- 'cancelled': 'secondary'
243
- },
244
- show_icons=True,
245
- icon=Icons.CHECK_CIRCLE if obj.processing_status == 'completed' else Icons.ERROR if obj.processing_status == 'failed' else Icons.SCHEDULE
246
- )
227
+ icon = DocumentAdminConfigs.get_processing_status_icon(obj.processing_status)
228
+ status_config = DocumentAdminConfigs.PROCESSING_STATUS
229
+ status_config.icon = icon
247
230
  return self.display_status_auto(obj, 'processing_status', status_config)
248
-
231
+
249
232
  @display(description="Categories")
250
233
  def categories_display(self, obj):
251
234
  """Display categories count."""
252
235
  categories = obj.categories.all()
253
-
236
+
254
237
  if not categories:
255
238
  return "No categories"
256
-
239
+
257
240
  public_count = sum(1 for cat in categories if cat.is_public)
258
241
  private_count = len(categories) - public_count
259
-
242
+
260
243
  if private_count == 0:
261
244
  return f"{len(categories)} public"
262
245
  elif public_count == 0:
263
246
  return f"{len(categories)} private"
264
247
  else:
265
248
  return f"{public_count} public, {private_count} private"
266
-
249
+
267
250
  @display(description="Chunks", ordering="chunks_count")
268
251
  def chunks_count_display(self, obj):
269
252
  """Display chunks count."""
@@ -271,43 +254,31 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
271
254
  if count > 0:
272
255
  return f"{count} chunks"
273
256
  return "0 chunks"
274
-
275
- @display(description="Tokens", ordering="total_tokens")
257
+
276
258
  def tokens_display(self, obj):
277
- """Display token count with formatting."""
278
- tokens = obj.total_tokens
279
- if tokens > 1000:
280
- return f"{tokens/1000:.1f}K"
281
- return str(tokens)
259
+ """Token count display (delegates to helper)."""
260
+ return DocumentDisplayHelpers.display_token_count(obj, 'total_tokens')
282
261
 
283
- @display(description="Cost (USD)", ordering="total_cost_usd")
284
262
  def cost_display(self, obj):
285
- """Display cost with currency formatting."""
286
- config = MoneyDisplayConfig(
287
- currency="USD",
288
- decimal_places=6,
289
- show_sign=False
290
- )
291
- return self.display_money_amount(obj, 'total_cost_usd', config)
292
-
263
+ """Cost display (delegates to helper)."""
264
+ return DocumentDisplayHelpers.display_cost_usd(obj, self, 'total_cost_usd')
265
+
293
266
  @display(description="Vectorization")
294
267
  def vectorization_progress(self, obj):
295
268
  """Display vectorization progress."""
296
269
  return Document.objects.get_vectorization_status_display(obj)
297
-
298
- @display(description="Created")
270
+
299
271
  def created_at_display(self, obj):
300
- """Created time with relative display."""
301
- config = DateTimeDisplayConfig(show_relative=True)
302
- return self.display_datetime_relative(obj, 'created_at', config)
303
-
272
+ """Created time display (delegates to helper)."""
273
+ return DocumentDisplayHelpers.display_created_at(obj, self)
274
+
304
275
  @display(description="Processing Duration")
305
276
  def processing_duration_display(self, obj):
306
277
  """Display processing duration in readable format."""
307
278
  duration = obj.processing_duration
308
279
  if duration is None:
309
280
  return "N/A"
310
-
281
+
311
282
  if duration < 60:
312
283
  return f"{duration:.1f}s"
313
284
  elif duration < 3600:
@@ -316,83 +287,54 @@ class DocumentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportM
316
287
  else:
317
288
  hours = duration / 3600
318
289
  return f"{hours:.1f}h"
319
-
290
+
320
291
  @display(description="Duplicate Check")
321
292
  def duplicate_check(self, obj):
322
293
  """Check for duplicate documents with same content."""
323
294
  duplicate_info = Document.objects.get_duplicate_info(obj)
324
-
295
+
325
296
  if isinstance(duplicate_info, str):
326
297
  if "No duplicates found" in duplicate_info:
327
298
  return "✓ No duplicates found"
328
299
  return duplicate_info
329
-
300
+
330
301
  duplicates_data = duplicate_info['duplicates']
331
302
  count = duplicate_info['count']
332
-
303
+
333
304
  duplicate_names = [dup.title for dup in duplicates_data[:3]]
334
305
  result = f"⚠️ Found {count} duplicate(s): " + ", ".join(duplicate_names)
335
306
  if count > 3:
336
307
  result += f" and {count - 3} more"
337
-
308
+
338
309
  return result
339
-
310
+
340
311
  @action(description="Reprocess documents", variant=ActionVariant.INFO)
341
312
  def reprocess_documents(self, request, queryset):
342
313
  """Reprocess selected documents."""
343
314
  count = queryset.count()
344
315
  messages.info(request, f"Reprocessing functionality not implemented yet. {count} documents selected.")
345
-
346
- @action(description="Mark as public", variant=ActionVariant.SUCCESS)
347
- def mark_as_public(self, request, queryset):
348
- """Mark selected documents as public."""
349
- updated = queryset.update(is_public=True)
350
- messages.success(request, f"Marked {updated} documents as public.")
351
-
352
- @action(description="Mark as private", variant=ActionVariant.WARNING)
353
- def mark_as_private(self, request, queryset):
354
- """Mark selected documents as private."""
355
- updated = queryset.update(is_public=False)
356
- messages.warning(request, f"Marked {updated} documents as private.")
357
-
316
+
317
+ # Visibility actions (delegate to shared actions)
318
+ mark_as_public = VisibilityActions.mark_as_public
319
+ mark_as_private = VisibilityActions.mark_as_private
320
+
358
321
  def changelist_view(self, request, extra_context=None):
359
322
  """Add summary statistics to changelist."""
360
323
  extra_context = extra_context or {}
361
-
362
324
  queryset = self.get_queryset(request)
363
- stats = queryset.aggregate(
364
- total_documents=Count('id'),
365
- total_chunks=Sum('chunks_count'),
366
- total_tokens=Sum('total_tokens'),
367
- total_cost=Sum('total_cost_usd')
368
- )
369
-
370
- status_counts = dict(
371
- queryset.values_list('processing_status').annotate(
372
- count=Count('id')
373
- )
374
- )
375
-
376
- extra_context['summary_stats'] = {
377
- 'total_documents': stats['total_documents'] or 0,
378
- 'total_chunks': stats['total_chunks'] or 0,
379
- 'total_tokens': stats['total_tokens'] or 0,
380
- 'total_cost': f"${(stats['total_cost'] or 0):.6f}",
381
- 'status_counts': status_counts
382
- }
383
-
325
+ extra_context['summary_stats'] = DocumentStatistics.get_document_stats(queryset)
384
326
  return super().changelist_view(request, extra_context)
385
327
 
386
328
 
387
329
  @admin.register(DocumentChunk)
388
330
  class DocumentChunkAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
389
331
  """Admin interface for DocumentChunk model using Django Admin Utilities."""
390
-
332
+
391
333
  # Performance optimization
392
334
  select_related_fields = ['document', 'user']
393
-
335
+
394
336
  list_display = [
395
- 'chunk_display', 'document_display', 'user_display', 'token_count_display',
337
+ 'chunk_display', 'document_display', 'user_display', 'token_count_display',
396
338
  'embedding_status', 'embedding_cost_display', 'created_at_display'
397
339
  ]
398
340
  list_display_links = ['chunk_display']
@@ -407,7 +349,7 @@ class DocumentChunkAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
407
349
  'id', 'embedding_info', 'token_count', 'character_count',
408
350
  'embedding_cost', 'created_at', 'updated_at', 'content_preview'
409
351
  ]
410
-
352
+
411
353
  fieldsets = (
412
354
  ('📄 Basic Information', {
413
355
  'fields': ('id', 'document', 'user', 'chunk_index'),
@@ -435,138 +377,91 @@ class DocumentChunkAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
435
377
  'classes': ('tab', 'collapse')
436
378
  })
437
379
  )
438
-
380
+
439
381
  # Unfold configuration
440
382
  compressed_fields = True
441
383
  warn_unsaved_form = True
442
-
384
+
443
385
  # Form field overrides
444
386
  formfield_overrides = {
445
387
  JSONField: {"widget": JSONEditorWidget}
446
388
  }
447
-
389
+
448
390
  actions = ['regenerate_embeddings', 'clear_embeddings']
449
-
391
+
450
392
  @display(description="Chunk", ordering="chunk_index")
451
393
  def chunk_display(self, obj):
452
394
  """Display chunk identifier."""
453
- config = StatusBadgeConfig(show_icons=True, icon=Icons.ARTICLE)
454
395
  return StatusBadge.create(
455
396
  text=f"Chunk {obj.chunk_index + 1}",
456
397
  variant="info",
457
- config=config
398
+ config=DocumentAdminConfigs.CHUNK
458
399
  )
459
-
400
+
460
401
  @display(description="Document", ordering="document__title")
461
402
  def document_display(self, obj):
462
403
  """Display document title."""
463
404
  return obj.document.title
464
-
465
- @display(description="User")
405
+
466
406
  def user_display(self, obj):
467
- """User display."""
468
- if not obj.user:
469
- return "—"
470
- return self.display_user_simple(obj.user)
471
-
472
- @display(description="Tokens", ordering="token_count")
407
+ """User display (delegates to helper)."""
408
+ return DocumentDisplayHelpers.display_user(obj, self)
409
+
473
410
  def token_count_display(self, obj):
474
- """Display token count with formatting."""
475
- tokens = obj.token_count
476
- if tokens > 1000:
477
- return f"{tokens/1000:.1f}K"
478
- return str(tokens)
479
-
480
- @display(description="Embedding")
411
+ """Token count display (delegates to helper)."""
412
+ return DocumentDisplayHelpers.display_token_count(obj, 'token_count')
413
+
481
414
  def embedding_status(self, obj):
482
- """Display embedding status."""
483
- has_embedding = obj.embedding is not None and len(obj.embedding) > 0
484
- if has_embedding:
485
- config = StatusBadgeConfig(show_icons=True, icon=Icons.CHECK_CIRCLE)
486
- return StatusBadge.create(text="✓ Vectorized", variant="success", config=config)
487
- else:
488
- config = StatusBadgeConfig(show_icons=True, icon=Icons.ERROR)
489
- return StatusBadge.create(text="✗ Not vectorized", variant="danger", config=config)
490
-
491
- @display(description="Cost (USD)", ordering="embedding_cost")
415
+ """Embedding status display (delegates to helper)."""
416
+ return DocumentDisplayHelpers.display_embedding_status(obj)
417
+
492
418
  def embedding_cost_display(self, obj):
493
- """Display embedding cost with currency formatting."""
494
- config = MoneyDisplayConfig(
495
- currency="USD",
496
- decimal_places=6,
497
- show_sign=False
498
- )
499
- return self.display_money_amount(obj, 'embedding_cost', config)
500
-
501
- @display(description="Created")
419
+ """Embedding cost display (delegates to helper)."""
420
+ return DocumentDisplayHelpers.display_cost_usd(obj, self, 'embedding_cost')
421
+
502
422
  def created_at_display(self, obj):
503
- """Created time with relative display."""
504
- config = DateTimeDisplayConfig(show_relative=True)
505
- return self.display_datetime_relative(obj, 'created_at', config)
506
-
507
- @display(description="Content Preview")
423
+ """Created time display (delegates to helper)."""
424
+ return DocumentDisplayHelpers.display_created_at(obj, self)
425
+
508
426
  def content_preview(self, obj):
509
- """Display content preview with truncation."""
510
- return obj.content[:200] + "..." if len(obj.content) > 200 else obj.content
511
-
427
+ """Content preview display (delegates to helper)."""
428
+ return DocumentDisplayHelpers.display_content_preview(obj, max_length=200)
429
+
512
430
  @display(description="Embedding Info")
513
431
  def embedding_info(self, obj):
514
432
  """Display embedding information safely."""
515
433
  if obj.embedding is not None and len(obj.embedding) > 0:
516
434
  return f"✓ Vector ({len(obj.embedding)} dimensions)"
517
435
  return "✗ No embedding"
518
-
436
+
519
437
  @action(description="Regenerate embeddings", variant=ActionVariant.INFO)
520
438
  def regenerate_embeddings(self, request, queryset):
521
439
  """Regenerate embeddings for selected chunks."""
522
440
  count = queryset.count()
523
441
  messages.info(request, f"Regenerate embeddings functionality not implemented yet. {count} chunks selected.")
524
-
442
+
525
443
  @action(description="Clear embeddings", variant=ActionVariant.WARNING)
526
444
  def clear_embeddings(self, request, queryset):
527
445
  """Clear embeddings for selected chunks."""
528
446
  updated = queryset.update(embedding=None)
529
447
  messages.warning(request, f"Cleared embeddings for {updated} chunks.")
530
-
448
+
531
449
  def changelist_view(self, request, extra_context=None):
532
450
  """Add chunk statistics to changelist."""
533
451
  extra_context = extra_context or {}
534
-
535
452
  queryset = self.get_queryset(request)
536
- stats = queryset.aggregate(
537
- total_chunks=Count('id'),
538
- total_tokens=Sum('token_count'),
539
- total_characters=Sum('character_count'),
540
- total_embedding_cost=Sum('embedding_cost'),
541
- avg_tokens_per_chunk=Avg('token_count')
542
- )
543
-
544
- model_counts = dict(
545
- queryset.values_list('embedding_model').annotate(
546
- count=Count('id')
547
- )
548
- )
549
-
550
- extra_context['chunk_stats'] = {
551
- 'total_chunks': stats['total_chunks'] or 0,
552
- 'total_tokens': stats['total_tokens'] or 0,
553
- 'total_characters': stats['total_characters'] or 0,
554
- 'total_embedding_cost': f"${(stats['total_embedding_cost'] or 0):.6f}",
555
- 'avg_tokens_per_chunk': f"{(stats['avg_tokens_per_chunk'] or 0):.0f}",
556
- 'model_counts': model_counts
557
- }
558
-
453
+ extra_context['chunk_stats'] = ChunkStatistics.get_chunk_stats(queryset)
559
454
  return super().changelist_view(request, extra_context)
560
455
 
561
456
 
562
457
  @admin.register(DocumentCategory)
563
458
  class DocumentCategoryAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportModelAdmin):
564
459
  """Admin interface for DocumentCategory model using Django Admin Utilities."""
565
-
460
+
566
461
  # Import/Export configuration
567
462
  import_form_class = ImportForm
568
463
  export_form_class = ExportForm
569
-
464
+
570
465
  list_display = [
571
466
  'short_uuid', 'name_display', 'visibility_display', 'document_count', 'created_at_display'
572
467
  ]
@@ -575,7 +470,7 @@ class DocumentCategoryAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, Impor
575
470
  list_filter = ['is_public', 'created_at']
576
471
  search_fields = ['name', 'description']
577
472
  readonly_fields = ['id', 'created_at', 'updated_at']
578
-
473
+
579
474
  fieldsets = (
580
475
  ('📁 Basic Information', {
581
476
  'fields': ('id', 'name', 'description', 'is_public'),
@@ -586,81 +481,52 @@ class DocumentCategoryAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, Impor
586
481
  'classes': ('tab', 'collapse')
587
482
  })
588
483
  )
589
-
484
+
590
485
  # Unfold configuration
591
486
  compressed_fields = True
592
487
  warn_unsaved_form = True
593
-
488
+
594
489
  # Form field overrides
595
490
  formfield_overrides = {
596
491
  models.TextField: {"widget": WysiwygWidget}
597
492
  }
598
-
493
+
599
494
  actions = ['make_public', 'make_private']
600
-
495
+
601
496
  @display(description="Category Name")
602
497
  def name_display(self, obj):
603
498
  """Display category name."""
604
- config = StatusBadgeConfig(show_icons=True, icon=Icons.FOLDER)
605
499
  return StatusBadge.create(
606
500
  text=obj.name,
607
501
  variant="primary",
608
- config=config
502
+ config=DocumentAdminConfigs.CATEGORY
609
503
  )
610
-
611
- @display(description="Visibility")
504
+
612
505
  def visibility_display(self, obj):
613
- """Display visibility status."""
614
- if obj.is_public:
615
- config = StatusBadgeConfig(show_icons=True, icon=Icons.PUBLIC)
616
- return StatusBadge.create(text="Public", variant="success", config=config)
617
- else:
618
- config = StatusBadgeConfig(show_icons=True, icon=Icons.LOCK)
619
- return StatusBadge.create(text="Private", variant="danger", config=config)
620
-
506
+ """Visibility display (delegates to helper)."""
507
+ return DocumentDisplayHelpers.display_visibility(obj)
508
+
621
509
  @display(description="Documents", ordering="document_count")
622
510
  def document_count(self, obj):
623
511
  """Display count of documents in this category."""
624
512
  count = obj.documents.count()
625
513
  return f"{count} documents"
626
-
627
- @display(description="Created")
514
+
628
515
  def created_at_display(self, obj):
629
- """Created time with relative display."""
630
- config = DateTimeDisplayConfig(show_relative=True)
631
- return self.display_datetime_relative(obj, 'created_at', config)
632
-
633
- @action(description="Make public", variant=ActionVariant.SUCCESS)
634
- def make_public(self, request, queryset):
635
- """Make selected categories public."""
636
- updated = queryset.update(is_public=True)
637
- messages.success(request, f"Made {updated} categories public.")
638
-
639
- @action(description="Make private", variant=ActionVariant.WARNING)
640
- def make_private(self, request, queryset):
641
- """Make selected categories private."""
642
- updated = queryset.update(is_public=False)
643
- messages.warning(request, f"Made {updated} categories private.")
644
-
516
+ """Created time display (delegates to helper)."""
517
+ return DocumentDisplayHelpers.display_created_at(obj, self)
518
+
519
+ # Visibility actions (delegate to shared actions, using make_* aliases)
520
+ make_public = VisibilityActions.mark_as_public
521
+ make_private = VisibilityActions.mark_as_private
522
+
645
523
  def get_queryset(self, request):
646
524
  """Optimize queryset with prefetch_related."""
647
525
  return super().get_queryset(request).prefetch_related('documents')
648
-
526
+
649
527
  def changelist_view(self, request, extra_context=None):
650
528
  """Add category statistics to changelist."""
651
529
  extra_context = extra_context or {}
652
-
653
530
  queryset = self.get_queryset(request)
654
- stats = queryset.aggregate(
655
- total_categories=Count('id'),
656
- public_categories=Count('id', filter=models.Q(is_public=True)),
657
- private_categories=Count('id', filter=models.Q(is_public=False))
658
- )
659
-
660
- extra_context['category_stats'] = {
661
- 'total_categories': stats['total_categories'] or 0,
662
- 'public_categories': stats['public_categories'] or 0,
663
- 'private_categories': stats['private_categories'] or 0
664
- }
665
-
531
+ extra_context['category_stats'] = CategoryStatistics.get_category_stats(queryset)
666
532
  return super().changelist_view(request, extra_context)