django-cfg 1.3.5__py3-none-any.whl → 1.3.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/apps/urls.py +1 -2
  97. django_cfg/config.py +1 -1
  98. django_cfg/core/config.py +10 -5
  99. django_cfg/core/generation.py +1 -1
  100. django_cfg/management/commands/__init__.py +13 -1
  101. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  102. django_cfg/management/commands/app_agent_generate.py +342 -0
  103. django_cfg/management/commands/app_agent_info.py +308 -0
  104. django_cfg/management/commands/migrate_all.py +9 -3
  105. django_cfg/management/commands/migrator.py +11 -6
  106. django_cfg/management/commands/rundramatiq.py +3 -2
  107. django_cfg/middleware/__init__.py +0 -2
  108. django_cfg/models/api_keys.py +115 -0
  109. django_cfg/modules/django_admin/__init__.py +64 -0
  110. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  111. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  112. django_cfg/modules/django_admin/decorators/display.py +106 -0
  113. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  114. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  115. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  116. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  117. django_cfg/modules/django_admin/models/__init__.py +20 -0
  118. django_cfg/modules/django_admin/models/action_models.py +33 -0
  119. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  120. django_cfg/modules/django_admin/models/base.py +26 -0
  121. django_cfg/modules/django_admin/models/display_models.py +31 -0
  122. django_cfg/modules/django_admin/utils/badges.py +159 -0
  123. django_cfg/modules/django_admin/utils/displays.py +247 -0
  124. django_cfg/modules/django_app_agent/__init__.py +87 -0
  125. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  126. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  127. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  128. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  129. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  130. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  135. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  136. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  137. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  138. django_cfg/modules/django_app_agent/core/config.py +300 -0
  139. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  140. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  141. django_cfg/modules/django_app_agent/models/base.py +283 -0
  142. django_cfg/modules/django_app_agent/models/context.py +496 -0
  143. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  144. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  145. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  146. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  147. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  153. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  154. django_cfg/modules/django_app_agent/services/base.py +437 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  160. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  165. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  171. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  172. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  188. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  195. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  196. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  197. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  198. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  199. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  200. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  201. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  202. django_cfg/modules/django_currency/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  204. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  205. django_cfg/modules/django_currency/core/converter.py +12 -12
  206. django_cfg/modules/django_currency/database/__init__.py +2 -2
  207. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  208. django_cfg/modules/django_llm/llm/client.py +10 -2
  209. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  210. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  211. django_cfg/modules/django_unfold/dashboard.py +14 -13
  212. django_cfg/modules/django_unfold/models/config.py +1 -1
  213. django_cfg/registry/core.py +3 -0
  214. django_cfg/registry/third_party.py +2 -2
  215. django_cfg/template_archive/django_sample.zip +0 -0
  216. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  217. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/RECORD +224 -118
  218. django_cfg/apps/accounts/admin/activity.py +0 -96
  219. django_cfg/apps/accounts/admin/group.py +0 -17
  220. django_cfg/apps/accounts/admin/otp.py +0 -59
  221. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  222. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  223. django_cfg/apps/accounts/admin/user.py +0 -300
  224. django_cfg/apps/agents/core/agent.py +0 -281
  225. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  226. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  227. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  228. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  229. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  230. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  231. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  232. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  236. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  237. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  239. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  242. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  243. django_cfg/apps/tasks/admin.py +0 -320
  244. django_cfg/middleware/static_nocache.py +0 -55
  245. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  249. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  250. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  251. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  252. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,327 @@
1
+ """
2
+ Twilio Response admin interface using Django Admin Utilities.
3
+
4
+ Enhanced Twilio response management with Material Icons and optimized queries.
5
+ """
6
+
7
+ from django.contrib import admin
8
+ from django.utils.html import format_html
9
+ from unfold.admin import ModelAdmin
10
+ from django_cfg import ExportMixin, ExportForm
11
+
12
+ from django_cfg.modules.django_admin import (
13
+ OptimizedModelAdmin,
14
+ DisplayMixin,
15
+ MoneyDisplayConfig,
16
+ DateTimeDisplayConfig,
17
+ StatusBadgeConfig,
18
+ Icons,
19
+ display
20
+ )
21
+ from django_cfg.modules.django_admin.utils.badges import StatusBadge
22
+
23
+ from ..models import TwilioResponse
24
+ from .filters import TwilioResponseStatusFilter, TwilioResponseTypeFilter
25
+ from .resources import TwilioResponseResource
26
+
27
+
28
+ class TwilioResponseInline(admin.TabularInline):
29
+ """Inline for showing Twilio responses in related models."""
30
+ model = TwilioResponse
31
+ extra = 0
32
+ readonly_fields = ['created_at', 'status', 'message_sid', 'error_code']
33
+ fields = ['response_type', 'service_type', 'status', 'message_sid', 'error_code', 'created_at']
34
+
35
+ def has_add_permission(self, request, obj=None):
36
+ return False
37
+
38
+
39
+
40
+ @admin.register(TwilioResponse)
41
+ class TwilioResponseAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
42
+ """Enhanced Twilio Response admin using Django Admin Utilities."""
43
+
44
+ # Export configuration
45
+ resource_class = TwilioResponseResource
46
+ export_form_class = ExportForm
47
+
48
+ # Performance optimization
49
+ select_related_fields = ['otp_secret']
50
+
51
+ list_display = [
52
+ 'identifier_display',
53
+ 'service_type_display',
54
+ 'response_type_display',
55
+ 'status_display',
56
+ 'recipient_display',
57
+ 'price_display',
58
+ 'created_display',
59
+ 'error_status_display'
60
+ ]
61
+ list_display_links = ['identifier_display']
62
+ list_filter = [
63
+ TwilioResponseStatusFilter,
64
+ TwilioResponseTypeFilter,
65
+ 'service_type',
66
+ 'response_type',
67
+ 'created_at',
68
+ ]
69
+ search_fields = [
70
+ 'message_sid',
71
+ 'verification_sid',
72
+ 'to_number',
73
+ 'error_message',
74
+ 'otp_secret__recipient'
75
+ ]
76
+ readonly_fields = [
77
+ 'created_at',
78
+ 'updated_at',
79
+ 'twilio_created_at',
80
+ 'response_data_display',
81
+ 'request_data_display'
82
+ ]
83
+ ordering = ['-created_at']
84
+
85
+ fieldsets = (
86
+ (
87
+ 'Basic Information',
88
+ {
89
+ 'fields': (
90
+ 'response_type',
91
+ 'service_type',
92
+ 'status',
93
+ 'otp_secret'
94
+ ),
95
+ },
96
+ ),
97
+ (
98
+ 'Twilio Identifiers',
99
+ {
100
+ 'fields': (
101
+ 'message_sid',
102
+ 'verification_sid',
103
+ ),
104
+ },
105
+ ),
106
+ (
107
+ 'Recipients',
108
+ {
109
+ 'fields': (
110
+ 'to_number',
111
+ 'from_number',
112
+ ),
113
+ },
114
+ ),
115
+ (
116
+ 'Error Information',
117
+ {
118
+ 'fields': (
119
+ 'error_code',
120
+ 'error_message',
121
+ ),
122
+ 'classes': ('collapse',),
123
+ },
124
+ ),
125
+ (
126
+ 'Pricing',
127
+ {
128
+ 'fields': (
129
+ 'price',
130
+ 'price_unit',
131
+ ),
132
+ 'classes': ('collapse',),
133
+ },
134
+ ),
135
+ (
136
+ 'Request/Response Data',
137
+ {
138
+ 'fields': (
139
+ 'request_data_display',
140
+ 'response_data_display',
141
+ ),
142
+ 'classes': ('collapse',),
143
+ },
144
+ ),
145
+ (
146
+ 'Timestamps',
147
+ {
148
+ 'fields': (
149
+ 'created_at',
150
+ 'updated_at',
151
+ 'twilio_created_at',
152
+ ),
153
+ 'classes': ('collapse',),
154
+ },
155
+ ),
156
+ )
157
+
158
+ @display(description="Identifier")
159
+ def identifier_display(self, obj):
160
+ """Main identifier display with appropriate icon."""
161
+ identifier = obj.message_sid or obj.verification_sid
162
+ if not identifier:
163
+ return "—"
164
+
165
+ # Truncate long identifiers
166
+ if len(identifier) > 20:
167
+ identifier = f"{identifier[:17]}..."
168
+
169
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.FINGERPRINT)
170
+ return StatusBadge.create(
171
+ text=identifier,
172
+ variant="info",
173
+ config=config
174
+ )
175
+
176
+ @display(description="Service")
177
+ def service_type_display(self, obj):
178
+ """Service type display with appropriate icon."""
179
+ service_icons = {
180
+ 'sms': Icons.SMS,
181
+ 'voice': Icons.PHONE,
182
+ 'verify': Icons.VERIFIED,
183
+ 'email': Icons.EMAIL,
184
+ }
185
+
186
+ icon = service_icons.get(obj.service_type, Icons.CLOUD)
187
+
188
+ config = StatusBadgeConfig(show_icons=True, icon=icon)
189
+ return StatusBadge.create(
190
+ text=obj.get_service_type_display(),
191
+ variant="primary",
192
+ config=config
193
+ )
194
+
195
+ @display(description="Type")
196
+ def response_type_display(self, obj):
197
+ """Response type display with appropriate icon."""
198
+ type_icons = {
199
+ 'send': Icons.SEND,
200
+ 'verify': Icons.VERIFIED,
201
+ 'check': Icons.CHECK_CIRCLE,
202
+ }
203
+
204
+ icon = type_icons.get(obj.response_type, Icons.DESCRIPTION)
205
+
206
+ config = StatusBadgeConfig(show_icons=True, icon=icon)
207
+ return StatusBadge.create(
208
+ text=obj.get_response_type_display(),
209
+ variant="info",
210
+ config=config
211
+ )
212
+
213
+ @display(description="Status", label=True)
214
+ def status_display(self, obj):
215
+ """Enhanced status display with appropriate colors and icons."""
216
+ if obj.has_error:
217
+ status = obj.status or 'Error'
218
+ icon = Icons.ERROR
219
+ variant = "danger"
220
+ elif obj.is_successful:
221
+ status = obj.status or 'Success'
222
+ icon = Icons.CHECK_CIRCLE
223
+ variant = "success"
224
+ else:
225
+ status = obj.status or 'Pending'
226
+ icon = Icons.SCHEDULE
227
+ variant = "warning"
228
+
229
+ config = StatusBadgeConfig(
230
+ show_icons=True,
231
+ icon=icon,
232
+ custom_mappings={status: variant}
233
+ )
234
+
235
+ return self.display_status_auto(
236
+ type('obj', (), {'status': status})(),
237
+ 'status',
238
+ config
239
+ )
240
+
241
+ @display(description="Recipient")
242
+ def recipient_display(self, obj):
243
+ """Recipient display with privacy masking."""
244
+ if not obj.to_number:
245
+ return "—"
246
+
247
+ # Mask phone numbers and emails for privacy
248
+ recipient = obj.to_number
249
+ if '@' in recipient:
250
+ # Email masking
251
+ local, domain = recipient.split('@', 1)
252
+ masked_local = local[:2] + '*' * (len(local) - 2) if len(local) > 2 else local
253
+ masked_recipient = f"{masked_local}@{domain}"
254
+ icon = Icons.EMAIL
255
+ else:
256
+ # Phone masking
257
+ masked_recipient = f"***{recipient[-4:]}" if len(recipient) > 4 else "***"
258
+ icon = Icons.PHONE
259
+
260
+ config = StatusBadgeConfig(show_icons=True, icon=icon)
261
+ return StatusBadge.create(
262
+ text=masked_recipient,
263
+ variant="secondary",
264
+ config=config
265
+ )
266
+
267
+ @display(description="Price")
268
+ def price_display(self, obj):
269
+ """Price display with currency formatting."""
270
+ if not obj.price or not obj.price_unit:
271
+ return "—"
272
+
273
+ config = MoneyDisplayConfig(
274
+ currency=obj.price_unit.upper(),
275
+ decimal_places=4,
276
+ show_sign=False
277
+ )
278
+
279
+ return self.display_money_amount(
280
+ type('obj', (), {'price': obj.price})(),
281
+ 'price',
282
+ config
283
+ )
284
+
285
+ @display(description="Created")
286
+ def created_display(self, obj):
287
+ """Created time with relative display."""
288
+ config = DateTimeDisplayConfig(show_relative=True)
289
+ return self.display_datetime_relative(obj, 'created_at', config)
290
+
291
+ @display(description="Error")
292
+ def error_status_display(self, obj):
293
+ """Error status indicator."""
294
+ if obj.has_error:
295
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.ERROR)
296
+ return StatusBadge.create(text="Error", variant="danger", config=config)
297
+
298
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.CHECK_CIRCLE)
299
+ return StatusBadge.create(text="OK", variant="success", config=config)
300
+
301
+ def request_data_display(self, obj):
302
+ """Display formatted request data."""
303
+ if not obj.request_data:
304
+ return "—"
305
+
306
+ import json
307
+ try:
308
+ formatted = json.dumps(obj.request_data, indent=2, ensure_ascii=False)
309
+ return format_html('<pre style="font-size: 12px; max-height: 300px; overflow-y: auto;">{}</pre>', formatted)
310
+ except (TypeError, ValueError):
311
+ return str(obj.request_data)
312
+
313
+ request_data_display.short_description = 'Request Data'
314
+
315
+ def response_data_display(self, obj):
316
+ """Display formatted response data."""
317
+ if not obj.response_data:
318
+ return "—"
319
+
320
+ import json
321
+ try:
322
+ formatted = json.dumps(obj.response_data, indent=2, ensure_ascii=False)
323
+ return format_html('<pre style="font-size: 12px; max-height: 300px; overflow-y: auto;">{}</pre>', formatted)
324
+ except (TypeError, ValueError):
325
+ return str(obj.response_data)
326
+
327
+ response_data_display.short_description = 'Response Data'
@@ -0,0 +1,362 @@
1
+ """
2
+ User admin interface using Django Admin Utilities.
3
+
4
+ Enhanced user management with Material Icons, status badges, and optimized queries.
5
+ """
6
+
7
+ from django.contrib import admin
8
+ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
9
+ from django.shortcuts import redirect
10
+ from django.urls import reverse
11
+ from unfold.admin import ModelAdmin
12
+ from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm
13
+ from django_cfg import ImportExportModelAdmin
14
+
15
+ from django_cfg.modules.base import BaseCfgModule
16
+ from django_cfg.modules.django_admin import (
17
+ OptimizedModelAdmin,
18
+ DisplayMixin,
19
+ StandaloneActionsMixin,
20
+ standalone_action,
21
+ UserDisplayConfig,
22
+ DateTimeDisplayConfig,
23
+ StatusBadgeConfig,
24
+ Icons,
25
+ ActionVariant,
26
+ display,
27
+ action
28
+ )
29
+ from django_cfg.modules.django_admin.utils.badges import StatusBadge
30
+
31
+ from ..models import CustomUser
32
+ from .filters import UserStatusFilter
33
+ from .inlines import UserRegistrationSourceInline, UserActivityInline, UserEmailLogInline, UserSupportTicketsInline
34
+ from .resources import CustomUserResource
35
+
36
+
37
+ @admin.register(CustomUser)
38
+ class CustomUserAdmin(BaseUserAdmin, OptimizedModelAdmin, DisplayMixin, StandaloneActionsMixin, ModelAdmin, ImportExportModelAdmin):
39
+ """Enhanced user admin using Django Admin Utilities."""
40
+
41
+ # Import/Export configuration
42
+ resource_class = CustomUserResource
43
+
44
+ # Forms loaded from unfold.forms
45
+ form = UserChangeForm
46
+ add_form = UserCreationForm
47
+ change_password_form = AdminPasswordChangeForm
48
+
49
+ # Performance optimization
50
+ select_related_fields = []
51
+ prefetch_related_fields = ['groups', 'user_permissions']
52
+
53
+ list_display = [
54
+ 'avatar_display',
55
+ 'email_display',
56
+ 'full_name_display',
57
+ 'status_display',
58
+ 'sources_count_display',
59
+ 'activity_count_display',
60
+ 'emails_count_display',
61
+ 'tickets_count_display',
62
+ 'last_login_display',
63
+ 'date_joined_display',
64
+ ]
65
+ list_display_links = ['avatar_display', 'email_display', 'full_name_display']
66
+ search_fields = ['email', 'first_name', 'last_name']
67
+ list_filter = [UserStatusFilter, 'is_staff', 'is_active', 'date_joined']
68
+ ordering = ['-date_joined']
69
+ readonly_fields = ['date_joined', 'last_login']
70
+
71
+ # Register standalone actions
72
+ actions_list = ['view_user_emails', 'view_user_tickets', 'export_user_data']
73
+
74
+ fieldsets = (
75
+ (
76
+ "Personal Information",
77
+ {
78
+ "fields": ("email", "first_name", "last_name", "avatar"),
79
+ },
80
+ ),
81
+ (
82
+ "Contact Information",
83
+ {
84
+ "fields": ("company", "phone", "position"),
85
+ },
86
+ ),
87
+ (
88
+ "Authentication",
89
+ {
90
+ "fields": ("password",),
91
+ "classes": ("collapse",),
92
+ },
93
+ ),
94
+ (
95
+ "Permissions & Status",
96
+ {
97
+ "fields": (
98
+ ("is_active", "is_staff", "is_superuser"),
99
+ ("groups",),
100
+ ("user_permissions",),
101
+ ),
102
+ },
103
+ ),
104
+ (
105
+ "Important Dates",
106
+ {
107
+ "fields": ("last_login", "date_joined"),
108
+ "classes": ("collapse",),
109
+ },
110
+ ),
111
+ )
112
+
113
+ add_fieldsets = (
114
+ (
115
+ None,
116
+ {
117
+ "classes": ("wide",),
118
+ "fields": ("email", "password1", "password2"),
119
+ },
120
+ ),
121
+ )
122
+
123
+ def get_inlines(self, request, obj):
124
+ """Get inlines based on enabled apps."""
125
+ inlines = [UserRegistrationSourceInline, UserActivityInline]
126
+
127
+ # Add email log inline if newsletter app is enabled
128
+ try:
129
+ base_module = BaseCfgModule()
130
+ if base_module.is_newsletter_enabled():
131
+ inlines.append(UserEmailLogInline)
132
+ if base_module.is_support_enabled():
133
+ inlines.append(UserSupportTicketsInline)
134
+ except Exception:
135
+ pass
136
+
137
+ return inlines
138
+
139
+ @display(description="Avatar")
140
+ def avatar_display(self, obj):
141
+ """Enhanced avatar display with fallback initials."""
142
+ config = UserDisplayConfig(
143
+ show_avatar=True,
144
+ avatar_size=32
145
+ )
146
+ return self.display_user_avatar(obj, config)
147
+
148
+ @display(description="Email")
149
+ def email_display(self, obj):
150
+ """Email display with user icon."""
151
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.EMAIL)
152
+ return StatusBadge.create(
153
+ text=obj.email,
154
+ variant="info",
155
+ config=config
156
+ )
157
+
158
+ @display(description="Full Name")
159
+ def full_name_display(self, obj):
160
+ """Full name display."""
161
+ full_name = obj.__class__.objects.get_full_name(obj)
162
+ if not full_name:
163
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.PERSON)
164
+ return StatusBadge.create(text="No name", variant="secondary", config=config)
165
+
166
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.PERSON)
167
+ return StatusBadge.create(text=full_name, variant="primary", config=config)
168
+
169
+ @display(description="Status", label=True)
170
+ def status_display(self, obj):
171
+ """Enhanced status display with appropriate icons and colors."""
172
+ if obj.is_superuser:
173
+ status = "Superuser"
174
+ icon = Icons.ADMIN_PANEL_SETTINGS
175
+ variant = "danger"
176
+ elif obj.is_staff:
177
+ status = "Staff"
178
+ icon = Icons.SETTINGS
179
+ variant = "warning"
180
+ elif obj.is_active:
181
+ status = "Active"
182
+ icon = Icons.CHECK_CIRCLE
183
+ variant = "success"
184
+ else:
185
+ status = "Inactive"
186
+ icon = Icons.CANCEL
187
+ variant = "secondary"
188
+
189
+ config = StatusBadgeConfig(
190
+ show_icons=True,
191
+ icon=icon,
192
+ custom_mappings={status: variant}
193
+ )
194
+ return self.display_status_auto(
195
+ type('obj', (), {'status': status})(),
196
+ 'status',
197
+ config
198
+ )
199
+
200
+ @display(description="Sources")
201
+ def sources_count_display(self, obj):
202
+ """Show count of registration sources for user."""
203
+ count = obj.user_registration_sources.count()
204
+ if count == 0:
205
+ return "—"
206
+
207
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.SOURCE)
208
+ return StatusBadge.create(
209
+ text=f"{count} source{'s' if count != 1 else ''}",
210
+ variant="info",
211
+ config=config
212
+ )
213
+
214
+ @display(description="Activities")
215
+ def activity_count_display(self, obj):
216
+ """Show count of user activities."""
217
+ count = obj.activities.count()
218
+ if count == 0:
219
+ return "—"
220
+
221
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.HISTORY)
222
+ return StatusBadge.create(
223
+ text=f"{count} activit{'ies' if count != 1 else 'y'}",
224
+ variant="info",
225
+ config=config
226
+ )
227
+
228
+ @display(description="Emails")
229
+ def emails_count_display(self, obj):
230
+ """Show count of emails sent to user (if newsletter app is enabled)."""
231
+ try:
232
+ base_module = BaseCfgModule()
233
+
234
+ if not base_module.is_newsletter_enabled():
235
+ return "—"
236
+
237
+ from django_cfg.apps.newsletter.models import EmailLog
238
+ count = EmailLog.objects.filter(user=obj).count()
239
+ if count == 0:
240
+ return "—"
241
+
242
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.EMAIL)
243
+ return StatusBadge.create(
244
+ text=f"{count} email{'s' if count != 1 else ''}",
245
+ variant="success",
246
+ config=config
247
+ )
248
+ except (ImportError, Exception):
249
+ return "—"
250
+
251
+ @display(description="Tickets")
252
+ def tickets_count_display(self, obj):
253
+ """Show count of support tickets for user (if support app is enabled)."""
254
+ try:
255
+ from django_cfg.modules.base import BaseCfgModule
256
+ base_module = BaseCfgModule()
257
+
258
+ if not base_module.is_support_enabled():
259
+ return "—"
260
+
261
+ from django_cfg.apps.support.models import Ticket
262
+ count = Ticket.objects.filter(user=obj).count()
263
+ if count == 0:
264
+ return "—"
265
+
266
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.SUPPORT_AGENT)
267
+ return StatusBadge.create(
268
+ text=f"{count} ticket{'s' if count != 1 else ''}",
269
+ variant="warning",
270
+ config=config
271
+ )
272
+ except (ImportError, Exception):
273
+ return "—"
274
+
275
+ @display(description="Last Login")
276
+ def last_login_display(self, obj):
277
+ """Last login with relative time."""
278
+ if not obj.last_login:
279
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.LOGIN)
280
+ return StatusBadge.create(text="Never", variant="secondary", config=config)
281
+
282
+ config = DateTimeDisplayConfig(show_relative=True)
283
+ return self.display_datetime_relative(obj, 'last_login', config)
284
+
285
+ @display(description="Joined")
286
+ def date_joined_display(self, obj):
287
+ """Join date with relative time."""
288
+ config = DateTimeDisplayConfig(show_relative=True)
289
+ return self.display_datetime_relative(obj, 'date_joined', config)
290
+
291
+ # Standalone Actions
292
+ @standalone_action(
293
+ description="📧 View Email History",
294
+ variant=ActionVariant.INFO,
295
+ icon="mail_outline",
296
+ success_message="Redirected to email history for {result}",
297
+ error_message="❌ Error accessing email history: {error}"
298
+ )
299
+ def view_user_emails(self, request, object_id):
300
+ """View all emails sent to this user."""
301
+ # Get the user object
302
+ user = self.get_object(request, object_id)
303
+ if not user:
304
+ raise Exception("User not found")
305
+
306
+ # Check if newsletter app is enabled
307
+ from django_cfg.modules.base import BaseCfgModule
308
+ base_module = BaseCfgModule()
309
+
310
+ if not base_module.is_newsletter_enabled():
311
+ raise Exception("Newsletter app is not enabled")
312
+
313
+ # Redirect to EmailLog changelist filtered by this user
314
+ url = reverse('admin:django_cfg_newsletter_emaillog_changelist')
315
+ redirect_url = f"{url}?user__id__exact={user.id}"
316
+
317
+ # Return redirect (handled by standalone_action decorator)
318
+ return redirect(redirect_url)
319
+
320
+ @standalone_action(
321
+ description="🎫 View Support Tickets",
322
+ variant=ActionVariant.SUCCESS,
323
+ icon="support_agent",
324
+ success_message="Redirected to support tickets for {result}",
325
+ error_message="❌ Error accessing support tickets: {error}"
326
+ )
327
+ def view_user_tickets(self, request, object_id):
328
+ """View all support tickets for this user."""
329
+ # Get the user object
330
+ user = self.get_object(request, object_id)
331
+ if not user:
332
+ raise Exception("User not found")
333
+
334
+ # Check if support app is enabled
335
+ from django_cfg.modules.base import BaseCfgModule
336
+ base_module = BaseCfgModule()
337
+
338
+ if not base_module.is_support_enabled():
339
+ raise Exception("Support app is not enabled")
340
+
341
+ # Redirect to Ticket changelist filtered by this user
342
+ url = reverse('admin:django_cfg_support_ticket_changelist')
343
+ redirect_url = f"{url}?user__id__exact={user.id}"
344
+
345
+ return redirect(redirect_url)
346
+
347
+ @standalone_action(
348
+ description="📊 Export User Data",
349
+ variant=ActionVariant.WARNING,
350
+ icon="download",
351
+ success_message="📊 User data export completed: {result}",
352
+ error_message="❌ Export failed: {error}"
353
+ )
354
+ def export_user_data(self, request, object_id):
355
+ """Export comprehensive user data."""
356
+ user = self.get_object(request, object_id)
357
+ if not user:
358
+ raise Exception("User not found")
359
+
360
+ # Here you would implement actual export logic
361
+ # For now, just return a success message
362
+ return f"Data for {user.get_full_name() or user.email}"