django-cfg 1.3.7__py3-none-any.whl → 1.3.11__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 (246) 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 +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  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 +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,300 +0,0 @@
1
- """
2
- User admin configuration.
3
- """
4
-
5
- from django.contrib import admin
6
- from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
7
- from django.utils.html import format_html
8
- from django.contrib.humanize.templatetags.humanize import naturaltime, naturalday
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 unfold.decorators import action
14
- from unfold.enums import ActionVariant
15
- from django_cfg import ImportExportModelAdmin
16
-
17
- from ..models import CustomUser
18
- from .filters import UserStatusFilter
19
- from .inlines import UserRegistrationSourceInline, UserActivityInline, UserEmailLogInline, UserSupportTicketsInline
20
- from .resources import CustomUserResource
21
-
22
-
23
- @admin.register(CustomUser)
24
- class CustomUserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin):
25
- # Import/Export configuration
26
- resource_class = CustomUserResource
27
-
28
- # Forms loaded from `unfold.forms`
29
- form = UserChangeForm
30
- add_form = UserCreationForm
31
- change_password_form = AdminPasswordChangeForm
32
-
33
- list_display = [
34
- "avatar",
35
- "email",
36
- "full_name",
37
- "status",
38
- "sources_count",
39
- "activity_count",
40
- "emails_count",
41
- "tickets_count",
42
- "last_login_display",
43
- "date_joined_display",
44
- ]
45
- list_display_links = ["avatar", "email", "full_name"]
46
- search_fields = ["email", "first_name", "last_name"]
47
- list_filter = [UserStatusFilter, "is_staff", "is_active", "date_joined"]
48
- ordering = ["-date_joined"]
49
- readonly_fields = ["date_joined", "last_login"]
50
- def get_inlines(self, request, obj):
51
- """Get inlines based on enabled apps."""
52
- inlines = [UserRegistrationSourceInline, UserActivityInline]
53
-
54
- # Add email log inline if newsletter app is enabled
55
- try:
56
- from django_cfg.modules.base import BaseCfgModule
57
- base_module = BaseCfgModule()
58
- if base_module.is_newsletter_enabled():
59
- inlines.append(UserEmailLogInline)
60
- if base_module.is_support_enabled():
61
- inlines.append(UserSupportTicketsInline)
62
- except Exception:
63
- pass
64
-
65
- return inlines
66
-
67
- # Static actions for Unfold - always show, but check inside methods
68
- actions_detail = ["view_user_emails", "view_user_tickets"]
69
-
70
- fieldsets = (
71
- (
72
- "Personal Information",
73
- {
74
- "fields": ("email", "first_name", "last_name", "avatar"),
75
- },
76
- ),
77
- (
78
- "Contact Information",
79
- {
80
- "fields": ("company", "phone", "position"),
81
- },
82
- ),
83
- (
84
- "Authentication",
85
- {
86
- "fields": ("password",),
87
- "classes": ("collapse",),
88
- },
89
- ),
90
- (
91
- "Permissions & Status",
92
- {
93
- "fields": (
94
- ("is_active", "is_staff", "is_superuser"),
95
- ("groups",),
96
- ("user_permissions",),
97
- ),
98
- },
99
- ),
100
- (
101
- "Important Dates",
102
- {
103
- "fields": ("last_login", "date_joined"),
104
- "classes": ("collapse",),
105
- },
106
- ),
107
- )
108
-
109
- add_fieldsets = (
110
- (
111
- None,
112
- {
113
- "classes": ("wide",),
114
- "fields": ("email", "password1", "password2"),
115
- },
116
- ),
117
- )
118
-
119
- def full_name(self, obj):
120
- """Get user's full name."""
121
- return obj.__class__.objects.get_full_name(obj) or "—"
122
-
123
- full_name.short_description = "Full Name"
124
-
125
- def status(self, obj):
126
- """Enhanced status display with icons."""
127
- if obj.is_superuser:
128
- return format_html('<span style="color: #dc3545;">👑 Superuser</span>')
129
- elif obj.is_staff:
130
- return format_html('<span style="color: #fd7e14;">⚙️ Staff</span>')
131
- elif obj.is_active:
132
- return format_html('<span style="color: #198754;">✅ Active</span>')
133
- else:
134
- return format_html('<span style="color: #6c757d;">❌ Inactive</span>')
135
-
136
- status.short_description = "Status"
137
-
138
- def sources_count(self, obj):
139
- """Show count of sources for user."""
140
- count = obj.user_registration_sources.count()
141
- if count == 0:
142
- return "—"
143
- return f"{count} source{'s' if count != 1 else ''}"
144
-
145
- sources_count.short_description = "Sources"
146
-
147
- def activity_count(self, obj):
148
- """Show count of user activities."""
149
- count = obj.activities.count()
150
- if count == 0:
151
- return "—"
152
- return f"{count} activit{'ies' if count != 1 else 'y'}"
153
-
154
- activity_count.short_description = "Activities"
155
-
156
- def emails_count(self, obj):
157
- """Show count of emails sent to user (if newsletter app is enabled)."""
158
- try:
159
- from django_cfg.modules.base import BaseCfgModule
160
- base_module = BaseCfgModule()
161
-
162
- if not base_module.is_newsletter_enabled():
163
- return "—"
164
-
165
- from django_cfg.apps.newsletter.models import EmailLog
166
- count = EmailLog.objects.filter(user=obj).count()
167
- if count == 0:
168
- return "—"
169
- return f"{count} email{'s' if count != 1 else ''}"
170
- except (ImportError, Exception):
171
- return "—"
172
-
173
- emails_count.short_description = "Emails"
174
-
175
- def tickets_count(self, obj):
176
- """Show count of support tickets for user (if support app is enabled)."""
177
- try:
178
- from django_cfg.modules.base import BaseCfgModule
179
- base_module = BaseCfgModule()
180
-
181
- if not base_module.is_support_enabled():
182
- return "—"
183
-
184
- from django_cfg.apps.support.models import Ticket
185
- count = Ticket.objects.filter(user=obj).count()
186
- if count == 0:
187
- return "—"
188
- return f"{count} ticket{'s' if count != 1 else ''}"
189
- except (ImportError, Exception):
190
- return "—"
191
-
192
- tickets_count.short_description = "Tickets"
193
-
194
- def last_login_display(self, obj):
195
- """Last login with natural time."""
196
- if obj.last_login:
197
- return naturaltime(obj.last_login)
198
- return "Never"
199
-
200
- last_login_display.short_description = "Last Login"
201
-
202
- def date_joined_display(self, obj):
203
- """Join date with natural day."""
204
- return naturalday(obj.date_joined)
205
-
206
- date_joined_display.short_description = "Joined"
207
-
208
- def avatar(self, obj):
209
- """Enhanced avatar display."""
210
- if obj.avatar:
211
- return format_html(
212
- '<img src="{}" style="width: 32px; height: 32px; border-radius: 50%; object-fit: cover;" />',
213
- obj.avatar.url,
214
- )
215
- else:
216
- initials = obj.__class__.objects.get_initials(obj)
217
- return format_html(
218
- '<div style="width: 32px; height: 32px; border-radius: 50%; background: #6c757d; '
219
- "color: white; display: flex; align-items: center; justify-content: center; "
220
- 'font-weight: bold; font-size: 12px;">{}</div>',
221
- initials,
222
- )
223
-
224
- avatar.short_description = "Avatar"
225
-
226
- @action(
227
- description="📧 View Email History",
228
- icon="mail_outline",
229
- variant=ActionVariant.INFO
230
- )
231
- def view_user_emails(self, request, object_id):
232
- """View all emails sent to this user."""
233
- try:
234
- # Get the user object
235
- user = self.get_object(request, object_id)
236
- if not user:
237
- self.message_user(request, "User not found.", level='error')
238
- return redirect(request.META.get('HTTP_REFERER', '/admin/'))
239
-
240
- # Check if newsletter app is enabled
241
- from django_cfg.modules.base import BaseCfgModule
242
- base_module = BaseCfgModule()
243
-
244
- if not base_module.is_newsletter_enabled():
245
- self.message_user(
246
- request,
247
- "Newsletter app is not enabled.",
248
- level='warning'
249
- )
250
- return redirect(request.META.get('HTTP_REFERER', '/admin/'))
251
-
252
- # Redirect to EmailLog changelist filtered by this user
253
- url = reverse('admin:django_cfg_newsletter_emaillog_changelist')
254
- return redirect(f"{url}?user__id__exact={user.id}")
255
-
256
- except Exception as e:
257
- self.message_user(
258
- request,
259
- f"Error accessing email history: {e}",
260
- level='error'
261
- )
262
- return redirect(request.META.get('HTTP_REFERER', '/admin/'))
263
-
264
- @action(
265
- description="🎫 View Support Tickets",
266
- icon="support_agent",
267
- variant=ActionVariant.SUCCESS
268
- )
269
- def view_user_tickets(self, request, object_id):
270
- """View all support tickets for this user."""
271
- try:
272
- # Get the user object
273
- user = self.get_object(request, object_id)
274
- if not user:
275
- self.message_user(request, "User not found.", level='error')
276
- return redirect(request.META.get('HTTP_REFERER', '/admin/'))
277
-
278
- # Check if support app is enabled
279
- from django_cfg.modules.base import BaseCfgModule
280
- base_module = BaseCfgModule()
281
-
282
- if not base_module.is_support_enabled():
283
- self.message_user(
284
- request,
285
- "Support app is not enabled.",
286
- level='warning'
287
- )
288
- return redirect(request.META.get('HTTP_REFERER', '/admin/'))
289
-
290
- # Redirect to Ticket changelist filtered by this user
291
- url = reverse('admin:django_cfg_support_ticket_changelist')
292
- return redirect(f"{url}?user__id__exact={user.id}")
293
-
294
- except Exception as e:
295
- self.message_user(
296
- request,
297
- f"Error accessing support tickets: {e}",
298
- level='error'
299
- )
300
- return redirect(request.META.get('HTTP_REFERER', '/admin/'))
@@ -1,281 +0,0 @@
1
- """
2
- Django Agent - Wrapper around Pydantic AI Agent with Django integration.
3
- """
4
-
5
- import time
6
- import logging
7
- from typing import TypeVar, Generic, Type, Any, Optional, Dict, Callable
8
- from dataclasses import dataclass
9
-
10
- from pydantic_ai import Agent
11
- from pydantic_ai.models import Model
12
-
13
- from .exceptions import ExecutionError, ConfigurationError
14
- from .models import ExecutionResult
15
-
16
- # Type variables for generic typing
17
- DepsT = TypeVar('DepsT')
18
- OutputT = TypeVar('OutputT')
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class DjangoAgent(Generic[DepsT, OutputT]):
24
- """
25
- Django-integrated agent wrapper around Pydantic AI Agent.
26
-
27
- Provides Django-specific functionality like:
28
- - Integration with django_llm module
29
- - Caching support
30
- - Metrics collection
31
- - Error handling
32
- - Type safety
33
- """
34
-
35
- def __init__(
36
- self,
37
- name: str,
38
- deps_type: Type[DepsT],
39
- output_type: Type[OutputT],
40
- instructions: str,
41
- model: Optional[str] = None,
42
- llm_client: Optional[Any] = None,
43
- timeout: int = 300,
44
- max_retries: int = 3,
45
- enable_caching: bool = True
46
- ):
47
- """
48
- Initialize Django Agent.
49
-
50
- Args:
51
- name: Unique agent identifier
52
- deps_type: Type for dependency injection (must be dataclass)
53
- output_type: Pydantic model for output validation
54
- instructions: System prompt for the agent
55
- model: Override model (uses client default if None)
56
- llm_client: Optional LLM client (uses default if None)
57
- timeout: Execution timeout in seconds
58
- max_retries: Maximum retry attempts
59
- enable_caching: Whether to enable result caching
60
- """
61
- self.name = name
62
- self.deps_type = deps_type
63
- self.output_type = output_type
64
- self.instructions = instructions
65
- self.timeout = timeout
66
- self.max_retries = max_retries
67
- self.enable_caching = enable_caching
68
-
69
- # Get LLM client
70
- self.llm_client = llm_client or self._get_default_llm_client()
71
-
72
- # Determine model to use
73
- model_name = model or self._get_default_model()
74
-
75
- # Create Pydantic AI agent
76
- self.agent = Agent[DepsT, OutputT](
77
- model=model_name,
78
- deps_type=deps_type,
79
- output_type=output_type,
80
- instructions=instructions,
81
- retries=max_retries
82
- )
83
-
84
- # Initialize metrics
85
- self._execution_count = 0
86
- self._total_execution_time = 0.0
87
- self._error_count = 0
88
- self._cache_hits = 0
89
-
90
- logger.info(f"Initialized DjangoAgent '{name}' with model '{model_name}'")
91
-
92
- async def run(self, prompt: str, deps: DepsT, **kwargs) -> ExecutionResult:
93
- """
94
- Run agent with Django-specific enhancements.
95
-
96
- Args:
97
- prompt: User prompt/instruction
98
- deps: Dependencies instance
99
- **kwargs: Additional Pydantic AI parameters
100
-
101
- Returns:
102
- ExecutionResult with output, usage, and metadata
103
- """
104
- start_time = time.time()
105
- self._execution_count += 1
106
-
107
- # Check cache if enabled
108
- if self.enable_caching:
109
- cached_result = await self._check_cache(prompt, deps)
110
- if cached_result:
111
- self._cache_hits += 1
112
- logger.debug(f"Cache hit for agent '{self.name}'")
113
- return ExecutionResult(
114
- agent_name=self.name,
115
- output=cached_result,
116
- execution_time=0.0,
117
- cached=True
118
- )
119
-
120
- try:
121
- # Execute agent
122
- logger.debug(f"Executing agent '{self.name}' with prompt: {prompt[:100]}...")
123
-
124
- result = await self.agent.run(prompt, deps=deps, **kwargs)
125
-
126
- execution_time = time.time() - start_time
127
- self._total_execution_time += execution_time
128
-
129
- # Extract metrics
130
- tokens_used = 0
131
- cost = 0.0
132
- if hasattr(result, 'usage') and result.usage:
133
- tokens_used = getattr(result.usage, 'total_tokens', 0)
134
- # Calculate cost based on model and tokens (implement based on your pricing)
135
- cost = self._calculate_cost(tokens_used)
136
-
137
- execution_result = ExecutionResult(
138
- agent_name=self.name,
139
- output=result.output,
140
- execution_time=execution_time,
141
- tokens_used=tokens_used,
142
- cost=cost
143
- )
144
-
145
- # Cache result if enabled
146
- if self.enable_caching:
147
- await self._cache_result(prompt, deps, result.output)
148
-
149
- logger.info(
150
- f"Agent '{self.name}' executed successfully in {execution_time:.2f}s "
151
- f"(tokens: {tokens_used}, cost: ${cost:.4f})"
152
- )
153
-
154
- return execution_result
155
-
156
- except Exception as e:
157
- self._error_count += 1
158
- execution_time = time.time() - start_time
159
-
160
- logger.error(f"Agent '{self.name}' execution failed: {e}")
161
-
162
- raise ExecutionError(
163
- f"Agent '{self.name}' execution failed: {str(e)}",
164
- agent_name=self.name,
165
- original_error=e
166
- )
167
-
168
- def tool(self, func: Callable) -> Callable:
169
- """
170
- Register tool with agent.
171
-
172
- Args:
173
- func: Function to register as agent tool
174
-
175
- Returns:
176
- Decorated function
177
- """
178
- return self.agent.tool(func)
179
-
180
- def get_metrics(self) -> Dict[str, Any]:
181
- """Get agent execution metrics."""
182
- return {
183
- 'name': self.name,
184
- 'execution_count': self._execution_count,
185
- 'total_execution_time': self._total_execution_time,
186
- 'average_execution_time': (
187
- self._total_execution_time / self._execution_count
188
- if self._execution_count > 0 else 0
189
- ),
190
- 'error_count': self._error_count,
191
- 'success_rate': (
192
- (self._execution_count - self._error_count) / self._execution_count
193
- if self._execution_count > 0 else 0
194
- ),
195
- 'cache_hits': self._cache_hits,
196
- 'cache_hit_rate': (
197
- self._cache_hits / self._execution_count
198
- if self._execution_count > 0 else 0
199
- )
200
- }
201
-
202
- def reset_metrics(self):
203
- """Reset agent metrics."""
204
- self._execution_count = 0
205
- self._total_execution_time = 0.0
206
- self._error_count = 0
207
- self._cache_hits = 0
208
-
209
- def _get_default_llm_client(self):
210
- """Get default LLM client from django_llm module."""
211
- try:
212
- from django_cfg.modules.django_llm import DjangoLLM
213
- llm_service = DjangoLLM()
214
- if llm_service.is_configured:
215
- return llm_service.client
216
- except ImportError:
217
- logger.warning("django_llm module not available, using default client")
218
-
219
- return None
220
-
221
- def _get_default_model(self) -> str:
222
- """Get default model name."""
223
- if self.llm_client and hasattr(self.llm_client, 'primary_provider'):
224
- if self.llm_client.primary_provider == 'openrouter':
225
- return 'openai:gpt-4o-mini'
226
- elif self.llm_client.primary_provider == 'openai':
227
- return 'openai:gpt-4o-mini'
228
-
229
- return 'openai:gpt-4o-mini' # Default fallback
230
-
231
- async def _check_cache(self, prompt: str, deps: DepsT) -> Optional[Any]:
232
- """Check cache for existing result."""
233
- if not self.llm_client or not hasattr(self.llm_client, 'cache'):
234
- return None
235
-
236
- try:
237
- cache_key = self._generate_cache_key(prompt, deps)
238
- return self.llm_client.cache.get(cache_key)
239
- except Exception as e:
240
- logger.warning(f"Cache check failed: {e}")
241
- return None
242
-
243
- async def _cache_result(self, prompt: str, deps: DepsT, result: Any):
244
- """Cache execution result."""
245
- if not self.llm_client or not hasattr(self.llm_client, 'cache'):
246
- return
247
-
248
- try:
249
- cache_key = self._generate_cache_key(prompt, deps)
250
- self.llm_client.cache.set(cache_key, result)
251
- except Exception as e:
252
- logger.warning(f"Cache storage failed: {e}")
253
-
254
- def _generate_cache_key(self, prompt: str, deps: DepsT) -> str:
255
- """Generate cache key for prompt and dependencies."""
256
- # Create a simple hash-based key
257
- import hashlib
258
-
259
- # Include agent name, prompt, and relevant deps data
260
- key_data = f"{self.name}:{prompt}"
261
-
262
- # Add user info if available
263
- if hasattr(deps, 'user') and deps.user:
264
- key_data += f":user_{deps.user.id}"
265
-
266
- # Add other relevant dependency data
267
- if hasattr(deps, 'to_dict'):
268
- deps_str = str(sorted(deps.to_dict().items()))
269
- key_data += f":deps_{deps_str}"
270
-
271
- return hashlib.md5(key_data.encode()).hexdigest()
272
-
273
- def _calculate_cost(self, tokens_used: int) -> float:
274
- """Calculate cost based on tokens used."""
275
- # Implement based on your pricing model
276
- # This is a simple example
277
- cost_per_1k_tokens = 0.002 # Example: $0.002 per 1K tokens
278
- return (tokens_used / 1000) * cost_per_1k_tokens
279
-
280
- def __repr__(self) -> str:
281
- return f"DjangoAgent(name='{self.name}', deps_type={self.deps_type.__name__}, output_type={self.output_type.__name__})"