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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/__init__.py +24 -8
- django_cfg/apps/accounts/admin/activity_admin.py +146 -0
- django_cfg/apps/accounts/admin/filters.py +98 -22
- django_cfg/apps/accounts/admin/group_admin.py +86 -0
- django_cfg/apps/accounts/admin/inlines.py +42 -13
- django_cfg/apps/accounts/admin/otp_admin.py +115 -0
- django_cfg/apps/accounts/admin/registration_admin.py +173 -0
- django_cfg/apps/accounts/admin/resources.py +123 -19
- django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
- django_cfg/apps/accounts/admin/user_admin.py +362 -0
- django_cfg/apps/agents/admin/__init__.py +17 -4
- django_cfg/apps/agents/admin/execution_admin.py +204 -183
- django_cfg/apps/agents/admin/registry_admin.py +230 -255
- django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
- django_cfg/apps/agents/core/__init__.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +221 -0
- django_cfg/apps/agents/core/exceptions.py +14 -0
- django_cfg/apps/agents/core/orchestrator.py +18 -3
- django_cfg/apps/knowbase/admin/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
- django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
- django_cfg/apps/knowbase/admin/document_admin.py +269 -262
- django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
- django_cfg/apps/knowbase/config/settings.py +21 -4
- django_cfg/apps/knowbase/views/chat_views.py +3 -0
- django_cfg/apps/leads/admin/__init__.py +3 -1
- django_cfg/apps/leads/admin/leads_admin.py +235 -35
- django_cfg/apps/maintenance/admin/__init__.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
- django_cfg/apps/maintenance/admin/log_admin.py +143 -61
- django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
- django_cfg/apps/maintenance/admin/site_admin.py +213 -352
- django_cfg/apps/newsletter/admin/__init__.py +29 -2
- django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
- django_cfg/apps/payments/admin/__init__.py +18 -27
- django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
- django_cfg/apps/payments/admin/balance_admin.py +166 -632
- django_cfg/apps/payments/admin/currencies_admin.py +235 -607
- django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
- django_cfg/apps/payments/admin/filters.py +83 -3
- django_cfg/apps/payments/admin/networks_admin.py +269 -0
- django_cfg/apps/payments/admin/payments_admin.py +183 -460
- django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
- django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
- django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
- django_cfg/apps/payments/middleware/api_access.py +32 -6
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/balance.py +12 -0
- django_cfg/apps/payments/models/currencies.py +106 -32
- django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +266 -39
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +303 -41
- django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
- django_cfg/apps/payments/services/providers/models/base.py +145 -0
- django_cfg/apps/payments/services/providers/models/providers.py +87 -0
- django_cfg/apps/payments/services/providers/models/universal.py +48 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
- django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +9 -37
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
- django_cfg/apps/payments/tasks/__init__.py +39 -0
- django_cfg/apps/payments/tasks/types.py +73 -0
- django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
- django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
- django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
- django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
- django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
- django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +8 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +22 -8
- django_cfg/apps/support/admin/__init__.py +10 -1
- django_cfg/apps/support/admin/support_admin.py +338 -141
- django_cfg/apps/tasks/admin/__init__.py +11 -0
- django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/migrate_all.py +9 -3
- django_cfg/management/commands/migrator.py +11 -6
- django_cfg/management/commands/rundramatiq.py +3 -2
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_admin/__init__.py +64 -0
- django_cfg/modules/django_admin/decorators/__init__.py +13 -0
- django_cfg/modules/django_admin/decorators/actions.py +106 -0
- django_cfg/modules/django_admin/decorators/display.py +106 -0
- django_cfg/modules/django_admin/mixins/__init__.py +14 -0
- django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
- django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
- django_cfg/modules/django_admin/models/__init__.py +20 -0
- django_cfg/modules/django_admin/models/action_models.py +33 -0
- django_cfg/modules/django_admin/models/badge_models.py +20 -0
- django_cfg/modules/django_admin/models/base.py +26 -0
- django_cfg/modules/django_admin/models/display_models.py +31 -0
- django_cfg/modules/django_admin/utils/badges.py +159 -0
- django_cfg/modules/django_admin/utils/displays.py +247 -0
- django_cfg/modules/django_currency/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
- django_cfg/modules/django_currency/core/converter.py +12 -12
- django_cfg/modules/django_currency/database/__init__.py +2 -2
- django_cfg/modules/django_currency/database/database_loader.py +93 -42
- django_cfg/modules/django_llm/llm/client.py +10 -2
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
- django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
- django_cfg/modules/django_unfold/dashboard.py +14 -13
- django_cfg/modules/django_unfold/models/config.py +1 -1
- django_cfg/registry/core.py +7 -9
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
- django_cfg/apps/accounts/admin/activity.py +0 -96
- django_cfg/apps/accounts/admin/group.py +0 -17
- django_cfg/apps/accounts/admin/otp.py +0 -59
- django_cfg/apps/accounts/admin/registration_source.py +0 -97
- django_cfg/apps/accounts/admin/twilio_response.py +0 -227
- django_cfg/apps/accounts/admin/user.py +0 -300
- django_cfg/apps/agents/core/agent.py +0 -281
- django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
- django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
- django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
- django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
- django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
- django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
- django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
- django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
- django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
- django_cfg/apps/tasks/admin.py +0 -320
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/middleware/static_nocache.py +0 -55
- django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
- /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,17 +1,31 @@
|
|
1
1
|
"""
|
2
|
-
Chat admin interfaces
|
2
|
+
Chat admin interfaces using Django Admin Utilities.
|
3
|
+
|
4
|
+
Enhanced chat management with Material Icons and optimized queries.
|
3
5
|
"""
|
4
6
|
|
5
|
-
from django.contrib import admin
|
6
|
-
from django.utils.html import format_html
|
7
|
+
from django.contrib import admin, messages
|
7
8
|
from django.urls import reverse
|
9
|
+
from django.utils.safestring import mark_safe
|
8
10
|
from django.db import models
|
9
11
|
from django.db.models import Count, Sum, Avg, Q
|
10
12
|
from unfold.admin import ModelAdmin, TabularInline
|
11
|
-
from unfold.decorators import display
|
12
13
|
from unfold.contrib.filters.admin import AutocompleteSelectFilter
|
13
14
|
from django_cfg import ExportMixin
|
14
15
|
|
16
|
+
from django_cfg.modules.django_admin import (
|
17
|
+
OptimizedModelAdmin,
|
18
|
+
DisplayMixin,
|
19
|
+
MoneyDisplayConfig,
|
20
|
+
StatusBadgeConfig,
|
21
|
+
DateTimeDisplayConfig,
|
22
|
+
Icons,
|
23
|
+
ActionVariant,
|
24
|
+
display,
|
25
|
+
action
|
26
|
+
)
|
27
|
+
from django_cfg.modules.django_admin.utils.badges import StatusBadge
|
28
|
+
|
15
29
|
from ..models import ChatSession, ChatMessage
|
16
30
|
|
17
31
|
|
@@ -22,20 +36,17 @@ class ChatMessageInline(TabularInline):
|
|
22
36
|
verbose_name = "Chat Message"
|
23
37
|
verbose_name_plural = "💬 Chat Messages (Read-only)"
|
24
38
|
extra = 0
|
25
|
-
max_num = 0
|
26
|
-
can_delete = False
|
27
|
-
show_change_link = True
|
39
|
+
max_num = 0
|
40
|
+
can_delete = False
|
41
|
+
show_change_link = True
|
28
42
|
|
29
43
|
def has_add_permission(self, request, obj=None):
|
30
|
-
"""Disable adding new messages through inline."""
|
31
44
|
return False
|
32
45
|
|
33
46
|
def has_change_permission(self, request, obj=None):
|
34
|
-
"""Disable editing messages through inline."""
|
35
47
|
return False
|
36
48
|
|
37
49
|
def has_delete_permission(self, request, obj=None):
|
38
|
-
"""Disable deleting messages through inline."""
|
39
50
|
return False
|
40
51
|
|
41
52
|
fields = [
|
@@ -47,41 +58,41 @@ class ChatMessageInline(TabularInline):
|
|
47
58
|
'cost_display_inline', 'processing_time_inline', 'created_at'
|
48
59
|
]
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
classes = ['collapse'] # Collapsed by default
|
61
|
+
hide_title = False
|
62
|
+
classes = ['collapse']
|
53
63
|
|
54
64
|
@display(description="Role")
|
55
65
|
def role_badge_inline(self, obj):
|
56
66
|
"""Display message role with color coding for inline."""
|
57
|
-
|
58
|
-
'user': '
|
59
|
-
'assistant': '
|
60
|
-
'system': '
|
67
|
+
role_variants = {
|
68
|
+
'user': 'primary',
|
69
|
+
'assistant': 'success',
|
70
|
+
'system': 'info'
|
61
71
|
}
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
72
|
+
variant = role_variants.get(obj.role, 'secondary')
|
73
|
+
|
74
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.PERSON)
|
75
|
+
return StatusBadge.create(
|
76
|
+
text=obj.role.upper(),
|
77
|
+
variant=variant,
|
78
|
+
config=config
|
68
79
|
)
|
69
80
|
|
70
81
|
@display(description="Content Preview")
|
71
82
|
def content_preview_inline(self, obj):
|
72
83
|
"""Shortened content preview for inline display."""
|
73
84
|
if not obj.content:
|
74
|
-
return "
|
75
|
-
|
76
|
-
return format_html(
|
77
|
-
'<div style="max-width: 250px; font-size: 12px; color: #666; '
|
78
|
-
'font-family: monospace;">{}</div>',
|
79
|
-
preview
|
80
|
-
)
|
85
|
+
return "—"
|
86
|
+
return obj.content[:80] + "..." if len(obj.content) > 80 else obj.content
|
81
87
|
|
82
88
|
@display(description="Cost (USD)")
|
83
89
|
def cost_display_inline(self, obj):
|
84
90
|
"""Display cost with currency formatting for inline."""
|
91
|
+
config = MoneyDisplayConfig(
|
92
|
+
currency="USD",
|
93
|
+
decimal_places=6,
|
94
|
+
show_sign=False
|
95
|
+
)
|
85
96
|
return f"${obj.cost_usd:.6f}"
|
86
97
|
|
87
98
|
@display(description="Time")
|
@@ -100,95 +111,142 @@ class ChatMessageInline(TabularInline):
|
|
100
111
|
|
101
112
|
|
102
113
|
@admin.register(ChatSession)
|
103
|
-
class ChatSessionAdmin(ModelAdmin, ExportMixin):
|
104
|
-
"""Admin interface for ChatSession model
|
114
|
+
class ChatSessionAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
|
115
|
+
"""Admin interface for ChatSession model using Django Admin Utilities."""
|
116
|
+
|
117
|
+
# Performance optimization
|
118
|
+
select_related_fields = ['user']
|
105
119
|
|
106
120
|
list_display = [
|
107
|
-
'
|
108
|
-
'
|
121
|
+
'title_display', 'user_display', 'status_display', 'messages_count_display',
|
122
|
+
'total_tokens_display', 'total_cost_display', 'last_activity_display', 'created_at_display'
|
109
123
|
]
|
110
|
-
|
124
|
+
list_display_links = ['title_display']
|
125
|
+
ordering = ['-updated_at']
|
111
126
|
inlines = [ChatMessageInline]
|
112
127
|
list_filter = [
|
113
|
-
'is_active', '
|
128
|
+
'is_active', 'created_at',
|
114
129
|
('user', AutocompleteSelectFilter)
|
115
130
|
]
|
116
131
|
search_fields = ['title', 'user__username', 'user__email']
|
117
132
|
autocomplete_fields = ['user']
|
118
133
|
readonly_fields = [
|
119
|
-
'id', 'messages_count', 'total_tokens_used', 'total_cost_usd',
|
120
|
-
'created_at', 'updated_at'
|
134
|
+
'id', 'user', 'messages_count', 'total_tokens_used', 'total_cost_usd',
|
135
|
+
'created_at', 'updated_at'
|
121
136
|
]
|
122
137
|
|
123
138
|
fieldsets = (
|
124
|
-
('
|
125
|
-
'fields': ('id', 'title', 'user', 'is_active')
|
139
|
+
('💬 Session Info', {
|
140
|
+
'fields': ('id', 'title', 'user', 'is_active'),
|
141
|
+
'classes': ('tab',)
|
126
142
|
}),
|
127
|
-
('
|
128
|
-
'fields': ('
|
143
|
+
('📊 Statistics', {
|
144
|
+
'fields': ('message_count', 'total_tokens', 'total_cost_usd'),
|
145
|
+
'classes': ('tab',)
|
129
146
|
}),
|
130
|
-
('
|
131
|
-
'fields': (
|
132
|
-
'messages_count', 'total_tokens_used', 'total_cost_usd',
|
133
|
-
'avg_tokens_per_message'
|
134
|
-
)
|
135
|
-
}),
|
136
|
-
('Timestamps', {
|
147
|
+
('⏰ Activity', {
|
137
148
|
'fields': ('created_at', 'updated_at'),
|
138
|
-
'classes': ('
|
139
|
-
})
|
149
|
+
'classes': ('tab',)
|
150
|
+
}),
|
140
151
|
)
|
141
152
|
|
142
|
-
|
143
|
-
compressed_fields = True
|
144
|
-
warn_unsaved_form = True
|
145
|
-
|
146
|
-
def get_queryset(self, request):
|
147
|
-
"""Optimize queryset with select_related."""
|
148
|
-
return super().get_queryset(request).select_related('user')
|
153
|
+
actions = ['activate_sessions', 'deactivate_sessions', 'clear_old_sessions']
|
149
154
|
|
150
155
|
@display(description="Session Title", ordering="title")
|
151
156
|
def title_display(self, obj):
|
152
|
-
"""Display session title
|
157
|
+
"""Display session title."""
|
153
158
|
title = obj.title or "Untitled Session"
|
154
159
|
if len(title) > 50:
|
155
160
|
title = title[:47] + "..."
|
156
|
-
|
157
|
-
|
158
|
-
|
161
|
+
|
162
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.CHAT)
|
163
|
+
return StatusBadge.create(
|
164
|
+
text=title,
|
165
|
+
variant="primary",
|
166
|
+
config=config
|
159
167
|
)
|
160
168
|
|
161
|
-
@display(description="
|
162
|
-
def
|
163
|
-
"""
|
164
|
-
if obj.
|
165
|
-
return
|
166
|
-
|
167
|
-
)
|
168
|
-
return format_html(
|
169
|
-
'<span style="color: gray; font-weight: bold;">● Inactive</span>'
|
170
|
-
)
|
169
|
+
@display(description="User")
|
170
|
+
def user_display(self, obj):
|
171
|
+
"""User display."""
|
172
|
+
if not obj.user:
|
173
|
+
return "—"
|
174
|
+
return self.display_user_simple(obj.user)
|
171
175
|
|
172
|
-
@display(description="
|
173
|
-
def
|
174
|
-
"""Display
|
175
|
-
|
176
|
+
@display(description="Status")
|
177
|
+
def status_display(self, obj):
|
178
|
+
"""Display session status."""
|
179
|
+
if obj.is_active:
|
180
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.CHECK_CIRCLE)
|
181
|
+
return StatusBadge.create(text="Active", variant="success", config=config)
|
182
|
+
else:
|
183
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.PAUSE_CIRCLE)
|
184
|
+
return StatusBadge.create(text="Inactive", variant="secondary", config=config)
|
185
|
+
|
186
|
+
@display(description="Messages", ordering="message_count")
|
187
|
+
def messages_count_display(self, obj):
|
188
|
+
"""Display messages count."""
|
189
|
+
count = obj.message_count
|
190
|
+
return f"{count} messages"
|
191
|
+
|
192
|
+
@display(description="Tokens", ordering="total_tokens")
|
193
|
+
def total_tokens_display(self, obj):
|
194
|
+
"""Display total tokens with formatting."""
|
195
|
+
tokens = obj.total_tokens
|
176
196
|
if tokens > 1000:
|
177
197
|
return f"{tokens/1000:.1f}K"
|
178
198
|
return str(tokens)
|
179
199
|
|
180
200
|
@display(description="Cost (USD)", ordering="total_cost_usd")
|
181
|
-
def
|
182
|
-
"""Display cost with currency formatting."""
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
201
|
+
def total_cost_display(self, obj):
|
202
|
+
"""Display total cost with currency formatting."""
|
203
|
+
config = MoneyDisplayConfig(
|
204
|
+
currency="USD",
|
205
|
+
decimal_places=6,
|
206
|
+
show_sign=False
|
207
|
+
)
|
208
|
+
return self.display_money_amount(obj, 'total_cost_usd', config)
|
209
|
+
|
210
|
+
@display(description="Last Activity", ordering="last_activity_at")
|
211
|
+
def last_activity_display(self, obj):
|
212
|
+
"""Last activity time with relative display."""
|
213
|
+
if not obj.last_activity_at:
|
214
|
+
return "—"
|
215
|
+
config = DateTimeDisplayConfig(show_relative=True)
|
216
|
+
return self.display_datetime_relative(obj, 'last_activity_at', config)
|
217
|
+
|
218
|
+
@display(description="Created")
|
219
|
+
def created_at_display(self, obj):
|
220
|
+
"""Created time with relative display."""
|
221
|
+
config = DateTimeDisplayConfig(show_relative=True)
|
222
|
+
return self.display_datetime_relative(obj, 'created_at', config)
|
223
|
+
|
224
|
+
@action(description="Activate sessions", variant=ActionVariant.SUCCESS)
|
225
|
+
def activate_sessions(self, request, queryset):
|
226
|
+
"""Activate selected sessions."""
|
227
|
+
updated = queryset.update(is_active=True)
|
228
|
+
messages.success(request, f"Activated {updated} sessions.")
|
229
|
+
|
230
|
+
@action(description="Deactivate sessions", variant=ActionVariant.WARNING)
|
231
|
+
def deactivate_sessions(self, request, queryset):
|
232
|
+
"""Deactivate selected sessions."""
|
233
|
+
updated = queryset.update(is_active=False)
|
234
|
+
messages.warning(request, f"Deactivated {updated} sessions.")
|
235
|
+
|
236
|
+
@action(description="Clear old sessions", variant=ActionVariant.DANGER)
|
237
|
+
def clear_old_sessions(self, request, queryset):
|
238
|
+
"""Clear old inactive sessions."""
|
239
|
+
from datetime import timedelta
|
240
|
+
from django.utils import timezone
|
241
|
+
|
242
|
+
cutoff_date = timezone.now() - timedelta(days=30)
|
243
|
+
old_sessions = queryset.filter(is_active=False, last_activity_at__lt=cutoff_date)
|
244
|
+
count = old_sessions.count()
|
245
|
+
|
246
|
+
if count > 0:
|
247
|
+
messages.warning(request, f"Clear old sessions functionality not implemented yet. {count} old sessions found.")
|
248
|
+
else:
|
249
|
+
messages.info(request, "No old sessions found to clear.")
|
192
250
|
|
193
251
|
def changelist_view(self, request, extra_context=None):
|
194
252
|
"""Add session statistics to changelist."""
|
@@ -198,17 +256,9 @@ class ChatSessionAdmin(ModelAdmin, ExportMixin):
|
|
198
256
|
stats = queryset.aggregate(
|
199
257
|
total_sessions=Count('id'),
|
200
258
|
active_sessions=Count('id', filter=Q(is_active=True)),
|
201
|
-
total_messages=Sum('
|
202
|
-
total_tokens=Sum('
|
203
|
-
total_cost=Sum('total_cost_usd')
|
204
|
-
avg_messages_per_session=Avg('messages_count')
|
205
|
-
)
|
206
|
-
|
207
|
-
# Model breakdown
|
208
|
-
model_counts = dict(
|
209
|
-
queryset.values_list('model_name').annotate(
|
210
|
-
count=Count('id')
|
211
|
-
)
|
259
|
+
total_messages=Sum('message_count'),
|
260
|
+
total_tokens=Sum('total_tokens'),
|
261
|
+
total_cost=Sum('total_cost_usd')
|
212
262
|
)
|
213
263
|
|
214
264
|
extra_context['session_stats'] = {
|
@@ -216,108 +266,107 @@ class ChatSessionAdmin(ModelAdmin, ExportMixin):
|
|
216
266
|
'active_sessions': stats['active_sessions'] or 0,
|
217
267
|
'total_messages': stats['total_messages'] or 0,
|
218
268
|
'total_tokens': stats['total_tokens'] or 0,
|
219
|
-
'total_cost': f"${(stats['total_cost'] or 0):.6f}"
|
220
|
-
'avg_messages_per_session': f"{(stats['avg_messages_per_session'] or 0):.1f}",
|
221
|
-
'model_counts': model_counts
|
269
|
+
'total_cost': f"${(stats['total_cost'] or 0):.6f}"
|
222
270
|
}
|
223
271
|
|
224
272
|
return super().changelist_view(request, extra_context)
|
225
273
|
|
226
274
|
|
227
275
|
@admin.register(ChatMessage)
|
228
|
-
class ChatMessageAdmin(ModelAdmin, ExportMixin):
|
229
|
-
"""Admin interface for ChatMessage model
|
276
|
+
class ChatMessageAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
|
277
|
+
"""Admin interface for ChatMessage model using Django Admin Utilities."""
|
278
|
+
|
279
|
+
# Performance optimization
|
280
|
+
select_related_fields = ['session', 'user']
|
230
281
|
|
231
282
|
list_display = [
|
232
|
-
'
|
233
|
-
'tokens_display', 'cost_display', 'processing_time_display', '
|
283
|
+
'message_display', 'session_display', 'user_display', 'role_display',
|
284
|
+
'tokens_display', 'cost_display', 'processing_time_display', 'created_at_display'
|
234
285
|
]
|
235
|
-
|
286
|
+
list_display_links = ['message_display']
|
287
|
+
ordering = ['-created_at']
|
236
288
|
list_filter = [
|
237
|
-
'role', '
|
289
|
+
'role', 'created_at',
|
238
290
|
('user', AutocompleteSelectFilter),
|
239
291
|
('session', AutocompleteSelectFilter)
|
240
292
|
]
|
241
|
-
search_fields = ['
|
293
|
+
search_fields = ['content', 'user__username', 'session__title']
|
294
|
+
autocomplete_fields = ['user', 'session']
|
242
295
|
readonly_fields = [
|
243
|
-
'id', 'tokens_used', 'cost_usd', 'processing_time_ms',
|
244
|
-
'created_at', 'updated_at', '
|
296
|
+
'id', 'user', 'tokens_used', 'cost_usd', 'processing_time_ms',
|
297
|
+
'created_at', 'updated_at', 'content_preview'
|
245
298
|
]
|
246
299
|
|
247
300
|
fieldsets = (
|
248
|
-
('
|
249
|
-
'fields': ('id', 'session', 'user', 'role')
|
250
|
-
|
251
|
-
('Content', {
|
252
|
-
'fields': ('content', 'content_stats')
|
301
|
+
('💬 Message Info', {
|
302
|
+
'fields': ('id', 'session', 'user', 'role'),
|
303
|
+
'classes': ('tab',)
|
253
304
|
}),
|
254
|
-
('
|
255
|
-
'fields': ('
|
256
|
-
'classes': ('
|
305
|
+
('📝 Content', {
|
306
|
+
'fields': ('content_preview', 'content'),
|
307
|
+
'classes': ('tab',)
|
257
308
|
}),
|
258
|
-
('
|
259
|
-
'fields': ('tokens_used', 'cost_usd', 'processing_time_ms')
|
309
|
+
('📊 Metrics', {
|
310
|
+
'fields': ('tokens_used', 'cost_usd', 'processing_time_ms'),
|
311
|
+
'classes': ('tab',)
|
260
312
|
}),
|
261
|
-
('
|
262
|
-
'fields': ('model_name', 'finish_reason'),
|
263
|
-
'classes': ('collapse',)
|
264
|
-
}),
|
265
|
-
('Timestamps', {
|
313
|
+
('⏰ Timestamps', {
|
266
314
|
'fields': ('created_at', 'updated_at'),
|
267
|
-
'classes': ('collapse'
|
315
|
+
'classes': ('tab', 'collapse')
|
268
316
|
})
|
269
317
|
)
|
270
318
|
|
271
|
-
|
272
|
-
compressed_fields = True
|
273
|
-
warn_unsaved_form = True
|
319
|
+
actions = ['delete_user_messages', 'delete_assistant_messages']
|
274
320
|
|
275
|
-
|
276
|
-
|
277
|
-
|
321
|
+
@display(description="Message", ordering="id")
|
322
|
+
def message_display(self, obj):
|
323
|
+
"""Display message identifier."""
|
324
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.MESSAGE)
|
325
|
+
return StatusBadge.create(
|
326
|
+
text=f"#{str(obj.id)[:8]}",
|
327
|
+
variant="secondary",
|
328
|
+
config=config
|
329
|
+
)
|
278
330
|
|
279
331
|
@display(description="Session", ordering="session__title")
|
280
|
-
def
|
281
|
-
"""Display session title
|
282
|
-
|
283
|
-
title = obj.session.title or "Untitled Session"
|
284
|
-
if len(title) > 30:
|
285
|
-
title = title[:27] + "..."
|
286
|
-
return format_html(
|
287
|
-
'<a href="{}" style="text-decoration: none;">{}</a>',
|
288
|
-
url,
|
289
|
-
title
|
290
|
-
)
|
332
|
+
def session_display(self, obj):
|
333
|
+
"""Display session title."""
|
334
|
+
return obj.session.title or "Untitled Session"
|
291
335
|
|
292
|
-
@display(description="
|
293
|
-
def
|
336
|
+
@display(description="User")
|
337
|
+
def user_display(self, obj):
|
338
|
+
"""User display."""
|
339
|
+
if not obj.user:
|
340
|
+
return "—"
|
341
|
+
return self.display_user_simple(obj.user)
|
342
|
+
|
343
|
+
@display(description="Role")
|
344
|
+
def role_display(self, obj):
|
294
345
|
"""Display message role with color coding."""
|
295
|
-
|
296
|
-
'user': '
|
297
|
-
'assistant': '
|
298
|
-
'system': '
|
346
|
+
role_variants = {
|
347
|
+
'user': 'primary',
|
348
|
+
'assistant': 'success',
|
349
|
+
'system': 'info'
|
299
350
|
}
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
'
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
'font-family: monospace; font-size: 13px;">{}</div>',
|
315
|
-
preview
|
351
|
+
variant = role_variants.get(obj.role, 'secondary')
|
352
|
+
|
353
|
+
role_icons = {
|
354
|
+
'user': Icons.PERSON,
|
355
|
+
'assistant': Icons.SMART_TOY,
|
356
|
+
'system': Icons.SETTINGS
|
357
|
+
}
|
358
|
+
icon = role_icons.get(obj.role, Icons.MESSAGE)
|
359
|
+
|
360
|
+
config = StatusBadgeConfig(show_icons=True, icon=icon)
|
361
|
+
return StatusBadge.create(
|
362
|
+
text=obj.role.title(),
|
363
|
+
variant=variant,
|
364
|
+
config=config
|
316
365
|
)
|
317
366
|
|
318
367
|
@display(description="Tokens", ordering="tokens_used")
|
319
368
|
def tokens_display(self, obj):
|
320
|
-
"""Display
|
369
|
+
"""Display tokens used with formatting."""
|
321
370
|
tokens = obj.tokens_used
|
322
371
|
if tokens > 1000:
|
323
372
|
return f"{tokens/1000:.1f}K"
|
@@ -326,11 +375,16 @@ class ChatMessageAdmin(ModelAdmin, ExportMixin):
|
|
326
375
|
@display(description="Cost (USD)", ordering="cost_usd")
|
327
376
|
def cost_display(self, obj):
|
328
377
|
"""Display cost with currency formatting."""
|
329
|
-
|
378
|
+
config = MoneyDisplayConfig(
|
379
|
+
currency="USD",
|
380
|
+
decimal_places=6,
|
381
|
+
show_sign=False
|
382
|
+
)
|
383
|
+
return self.display_money_amount(obj, 'cost_usd', config)
|
330
384
|
|
331
385
|
@display(description="Processing Time", ordering="processing_time_ms")
|
332
386
|
def processing_time_display(self, obj):
|
333
|
-
"""Display processing time
|
387
|
+
"""Display processing time."""
|
334
388
|
ms = obj.processing_time_ms
|
335
389
|
if ms < 1000:
|
336
390
|
return f"{ms}ms"
|
@@ -338,18 +392,38 @@ class ChatMessageAdmin(ModelAdmin, ExportMixin):
|
|
338
392
|
seconds = ms / 1000
|
339
393
|
return f"{seconds:.1f}s"
|
340
394
|
|
341
|
-
@display(description="
|
342
|
-
def
|
343
|
-
"""
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
395
|
+
@display(description="Created")
|
396
|
+
def created_at_display(self, obj):
|
397
|
+
"""Created time with relative display."""
|
398
|
+
config = DateTimeDisplayConfig(show_relative=True)
|
399
|
+
return self.display_datetime_relative(obj, 'created_at', config)
|
400
|
+
|
401
|
+
@display(description="Content Preview")
|
402
|
+
def content_preview(self, obj):
|
403
|
+
"""Display content preview with truncation."""
|
404
|
+
return obj.content[:200] + "..." if len(obj.content) > 200 else obj.content
|
405
|
+
|
406
|
+
@action(description="Delete user messages", variant=ActionVariant.DANGER)
|
407
|
+
def delete_user_messages(self, request, queryset):
|
408
|
+
"""Delete user messages from selection."""
|
409
|
+
user_messages = queryset.filter(role='user')
|
410
|
+
count = user_messages.count()
|
411
|
+
|
412
|
+
if count > 0:
|
413
|
+
messages.warning(request, f"Delete user messages functionality not implemented yet. {count} user messages selected.")
|
414
|
+
else:
|
415
|
+
messages.info(request, "No user messages in selection.")
|
416
|
+
|
417
|
+
@action(description="Delete assistant messages", variant=ActionVariant.DANGER)
|
418
|
+
def delete_assistant_messages(self, request, queryset):
|
419
|
+
"""Delete assistant messages from selection."""
|
420
|
+
assistant_messages = queryset.filter(role='assistant')
|
421
|
+
count = assistant_messages.count()
|
422
|
+
|
423
|
+
if count > 0:
|
424
|
+
messages.warning(request, f"Delete assistant messages functionality not implemented yet. {count} assistant messages selected.")
|
425
|
+
else:
|
426
|
+
messages.info(request, "No assistant messages in selection.")
|
353
427
|
|
354
428
|
def changelist_view(self, request, extra_context=None):
|
355
429
|
"""Add message statistics to changelist."""
|
@@ -360,28 +434,20 @@ class ChatMessageAdmin(ModelAdmin, ExportMixin):
|
|
360
434
|
total_messages=Count('id'),
|
361
435
|
user_messages=Count('id', filter=Q(role='user')),
|
362
436
|
assistant_messages=Count('id', filter=Q(role='assistant')),
|
437
|
+
system_messages=Count('id', filter=Q(role='system')),
|
363
438
|
total_tokens=Sum('tokens_used'),
|
364
439
|
total_cost=Sum('cost_usd'),
|
365
|
-
avg_processing_time=Avg('processing_time_ms')
|
366
|
-
avg_tokens_per_message=Avg('tokens_used')
|
367
|
-
)
|
368
|
-
|
369
|
-
# Role breakdown
|
370
|
-
role_counts = dict(
|
371
|
-
queryset.values_list('role').annotate(
|
372
|
-
count=Count('id')
|
373
|
-
)
|
440
|
+
avg_processing_time=Avg('processing_time_ms')
|
374
441
|
)
|
375
442
|
|
376
443
|
extra_context['message_stats'] = {
|
377
444
|
'total_messages': stats['total_messages'] or 0,
|
378
445
|
'user_messages': stats['user_messages'] or 0,
|
379
446
|
'assistant_messages': stats['assistant_messages'] or 0,
|
447
|
+
'system_messages': stats['system_messages'] or 0,
|
380
448
|
'total_tokens': stats['total_tokens'] or 0,
|
381
449
|
'total_cost': f"${(stats['total_cost'] or 0):.6f}",
|
382
|
-
'avg_processing_time': f"{(stats['avg_processing_time'] or 0):.0f}ms"
|
383
|
-
'avg_tokens_per_message': f"{(stats['avg_tokens_per_message'] or 0):.0f}",
|
384
|
-
'role_counts': role_counts
|
450
|
+
'avg_processing_time': f"{(stats['avg_processing_time'] or 0):.0f}ms"
|
385
451
|
}
|
386
452
|
|
387
453
|
return super().changelist_view(request, extra_context)
|