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,9 +1,10 @@
|
|
1
1
|
"""
|
2
|
-
Toolsets admin interfaces
|
2
|
+
Toolsets admin interfaces using Django Admin Utilities.
|
3
|
+
|
4
|
+
Enhanced toolset management with Material Icons and optimized queries.
|
3
5
|
"""
|
4
6
|
|
5
7
|
from django.contrib import admin, messages
|
6
|
-
from django.utils.html import format_html
|
7
8
|
from django.urls import reverse
|
8
9
|
from django.utils.safestring import mark_safe
|
9
10
|
from django.db import models
|
@@ -13,38 +14,51 @@ from django.db.models.fields.json import JSONField
|
|
13
14
|
from datetime import timedelta
|
14
15
|
from django_json_widget.widgets import JSONEditorWidget
|
15
16
|
from unfold.admin import ModelAdmin, TabularInline
|
16
|
-
from unfold.decorators import display, action
|
17
|
-
from unfold.enums import ActionVariant
|
18
17
|
from unfold.contrib.filters.admin import AutocompleteSelectFilter, AutocompleteSelectMultipleFilter
|
19
18
|
from unfold.contrib.forms.widgets import WysiwygWidget
|
20
|
-
from django_cfg import ExportMixin,
|
19
|
+
from django_cfg import ExportMixin, ExportForm
|
20
|
+
|
21
|
+
from django_cfg.modules.django_admin import (
|
22
|
+
OptimizedModelAdmin,
|
23
|
+
DisplayMixin,
|
24
|
+
MoneyDisplayConfig,
|
25
|
+
StatusBadgeConfig,
|
26
|
+
DateTimeDisplayConfig,
|
27
|
+
Icons,
|
28
|
+
ActionVariant,
|
29
|
+
display,
|
30
|
+
action
|
31
|
+
)
|
32
|
+
from django_cfg.modules.django_admin.utils.badges import StatusBadge
|
21
33
|
|
22
34
|
from ..models.toolsets import ToolExecution, ApprovalLog, ToolsetConfiguration
|
23
35
|
|
24
36
|
|
25
37
|
@admin.register(ToolExecution)
|
26
|
-
class ToolExecutionAdmin(ModelAdmin, ExportMixin):
|
27
|
-
"""
|
38
|
+
class ToolExecutionAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
|
39
|
+
"""Enhanced admin for ToolExecution model using Django Admin Utilities."""
|
40
|
+
|
41
|
+
# Performance optimization
|
42
|
+
select_related_fields = ['agent_execution', 'approval_log']
|
28
43
|
|
29
44
|
# Export-only configuration
|
30
45
|
export_form_class = ExportForm
|
31
46
|
|
32
47
|
list_display = [
|
33
|
-
'id_display', 'tool_name_display', '
|
34
|
-
'
|
48
|
+
'id_display', 'tool_name_display', 'toolset_display', 'status_display',
|
49
|
+
'duration_display', 'retry_count_display', 'created_at_display'
|
35
50
|
]
|
36
|
-
|
51
|
+
list_display_links = ['id_display', 'tool_name_display']
|
37
52
|
list_filter = [
|
38
|
-
'status', 'tool_name', '
|
39
|
-
('user', AutocompleteSelectFilter),
|
53
|
+
'status', 'tool_name', 'created_at',
|
40
54
|
('agent_execution', AutocompleteSelectFilter)
|
41
55
|
]
|
42
|
-
search_fields = ['tool_name', 'toolset_name', '
|
43
|
-
autocomplete_fields = ['
|
56
|
+
search_fields = ['tool_name', 'toolset_name', 'arguments', 'result']
|
57
|
+
autocomplete_fields = ['agent_execution']
|
44
58
|
readonly_fields = [
|
45
|
-
'id', 'execution_time', 'created_at', 'started_at', 'completed_at'
|
46
|
-
'duration_display', 'arguments_preview', 'result_preview', 'error_preview'
|
59
|
+
'id', 'execution_time', 'retry_count', 'created_at', 'started_at', 'completed_at'
|
47
60
|
]
|
61
|
+
ordering = ['-created_at']
|
48
62
|
|
49
63
|
# Unfold form field overrides
|
50
64
|
formfield_overrides = {
|
@@ -53,177 +67,146 @@ class ToolExecutionAdmin(ModelAdmin, ExportMixin):
|
|
53
67
|
}
|
54
68
|
|
55
69
|
fieldsets = (
|
56
|
-
("
|
57
|
-
'fields': ('id', 'tool_name', '
|
70
|
+
("🔧 Tool Info", {
|
71
|
+
'fields': ('id', 'tool_name', 'toolset_class', 'agent_execution'),
|
58
72
|
'classes': ('tab',)
|
59
73
|
}),
|
60
74
|
("📝 Execution Data", {
|
61
|
-
'fields': ('
|
75
|
+
'fields': ('arguments', 'result', 'error_message'),
|
62
76
|
'classes': ('tab',)
|
63
77
|
}),
|
64
78
|
("📊 Metrics", {
|
65
|
-
'fields': ('execution_time', 'retry_count'),
|
79
|
+
'fields': ('execution_time', 'retry_count', 'status'),
|
66
80
|
'classes': ('tab',)
|
67
81
|
}),
|
68
|
-
("
|
69
|
-
'fields': ('
|
82
|
+
("🔐 Approval", {
|
83
|
+
'fields': ('approval_log',),
|
70
84
|
'classes': ('tab', 'collapse')
|
71
85
|
}),
|
72
86
|
("⏰ Timestamps", {
|
73
|
-
'fields': ('created_at', 'started_at', 'completed_at'
|
87
|
+
'fields': ('created_at', 'started_at', 'completed_at'),
|
74
88
|
'classes': ('tab', 'collapse')
|
75
89
|
}),
|
76
90
|
)
|
77
91
|
|
78
|
-
actions = ['
|
92
|
+
actions = ['retry_failed_executions', 'clear_errors']
|
79
93
|
|
80
94
|
@display(description="ID")
|
81
95
|
def id_display(self, obj):
|
82
96
|
"""Enhanced ID display."""
|
83
|
-
|
84
|
-
|
85
|
-
str(obj.id)[:8]
|
97
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.TAG)
|
98
|
+
return StatusBadge.create(
|
99
|
+
text=f"#{str(obj.id)[:8]}",
|
100
|
+
variant="secondary",
|
101
|
+
config=config
|
86
102
|
)
|
87
103
|
|
88
104
|
@display(description="Tool")
|
89
105
|
def tool_name_display(self, obj):
|
90
106
|
"""Enhanced tool name display."""
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
107
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.BUILD)
|
108
|
+
return StatusBadge.create(
|
109
|
+
text=obj.tool_name,
|
110
|
+
variant="primary",
|
111
|
+
config=config
|
96
112
|
)
|
97
113
|
|
98
114
|
@display(description="Toolset")
|
99
|
-
def
|
100
|
-
"""Toolset badge."""
|
101
|
-
if not obj.
|
102
|
-
return "
|
103
|
-
|
104
|
-
|
105
|
-
|
115
|
+
def toolset_display(self, obj):
|
116
|
+
"""Toolset class display with badge."""
|
117
|
+
if not obj.toolset_class:
|
118
|
+
return "—"
|
119
|
+
|
120
|
+
# Extract class name from full path
|
121
|
+
class_name = obj.toolset_class.split('.')[-1] if '.' in obj.toolset_class else obj.toolset_class
|
122
|
+
|
123
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.EXTENSION)
|
124
|
+
return StatusBadge.create(
|
125
|
+
text=class_name,
|
126
|
+
variant="info",
|
127
|
+
config=config
|
106
128
|
)
|
107
129
|
|
108
130
|
@display(description="Status")
|
109
|
-
def
|
110
|
-
"""Status
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
'
|
121
|
-
color_class, obj.get_status_display()
|
131
|
+
def status_display(self, obj):
|
132
|
+
"""Status display with appropriate icons."""
|
133
|
+
status_config = StatusBadgeConfig(
|
134
|
+
custom_mappings={
|
135
|
+
'pending': 'warning',
|
136
|
+
'running': 'info',
|
137
|
+
'completed': 'success',
|
138
|
+
'failed': 'danger',
|
139
|
+
'cancelled': 'secondary'
|
140
|
+
},
|
141
|
+
show_icons=True,
|
142
|
+
icon=Icons.PLAY_ARROW if obj.status == 'running' else Icons.CHECK_CIRCLE if obj.status == 'completed' else Icons.ERROR if obj.status == 'failed' else Icons.SCHEDULE
|
122
143
|
)
|
144
|
+
return self.display_status_auto(obj, 'status', status_config)
|
123
145
|
|
124
|
-
@display(description="
|
125
|
-
def
|
126
|
-
"""
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
'</div>',
|
131
|
-
f"{obj.execution_time:.3f}s" if obj.execution_time else "-"
|
132
|
-
)
|
146
|
+
@display(description="Duration")
|
147
|
+
def duration_display(self, obj):
|
148
|
+
"""Execution duration display."""
|
149
|
+
if obj.execution_time:
|
150
|
+
return f"{obj.execution_time:.3f}s"
|
151
|
+
return "—"
|
133
152
|
|
134
153
|
@display(description="Retries")
|
135
|
-
def
|
136
|
-
"""Retry count badge."""
|
154
|
+
def retry_count_display(self, obj):
|
155
|
+
"""Retry count display with badge."""
|
137
156
|
if obj.retry_count > 0:
|
138
|
-
|
139
|
-
|
140
|
-
|
157
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.REFRESH)
|
158
|
+
variant = "warning" if obj.retry_count > 2 else "info"
|
159
|
+
return StatusBadge.create(
|
160
|
+
text=str(obj.retry_count),
|
161
|
+
variant=variant,
|
162
|
+
config=config
|
141
163
|
)
|
142
|
-
return
|
143
|
-
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">0</span>'
|
144
|
-
)
|
164
|
+
return "0"
|
145
165
|
|
146
|
-
@display(description="
|
147
|
-
def
|
148
|
-
"""
|
149
|
-
|
150
|
-
|
151
|
-
return "-"
|
152
|
-
|
153
|
-
@display(description="Arguments Preview")
|
154
|
-
def arguments_preview(self, obj):
|
155
|
-
"""Preview of arguments."""
|
156
|
-
if not obj.arguments:
|
157
|
-
return "-"
|
158
|
-
preview = str(obj.arguments)[:200] + "..." if len(str(obj.arguments)) > 200 else str(obj.arguments)
|
159
|
-
return format_html(
|
160
|
-
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
161
|
-
preview
|
162
|
-
)
|
163
|
-
|
164
|
-
@display(description="Result Preview")
|
165
|
-
def result_preview(self, obj):
|
166
|
-
"""Preview of result."""
|
167
|
-
if not obj.result:
|
168
|
-
return "-"
|
169
|
-
preview = str(obj.result)[:200] + "..." if len(str(obj.result)) > 200 else str(obj.result)
|
170
|
-
return format_html(
|
171
|
-
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
172
|
-
preview
|
173
|
-
)
|
166
|
+
@display(description="Created")
|
167
|
+
def created_at_display(self, obj):
|
168
|
+
"""Created time with relative display."""
|
169
|
+
config = DateTimeDisplayConfig(show_relative=True)
|
170
|
+
return self.display_datetime_relative(obj, 'created_at', config)
|
174
171
|
|
175
|
-
@
|
176
|
-
def
|
177
|
-
"""Preview of error message."""
|
178
|
-
if not obj.error_message:
|
179
|
-
return "-"
|
180
|
-
preview = obj.error_message[:200] + "..." if len(obj.error_message) > 200 else obj.error_message
|
181
|
-
return format_html(
|
182
|
-
'<div class="text-sm text-red-600 max-w-md">{}</div>',
|
183
|
-
preview
|
184
|
-
)
|
185
|
-
|
186
|
-
@action(description="Retry failed tools", icon="refresh", variant=ActionVariant.WARNING)
|
187
|
-
def retry_failed_tools(self, request, queryset):
|
172
|
+
@action(description="Retry failed executions", variant=ActionVariant.WARNING)
|
173
|
+
def retry_failed_executions(self, request, queryset):
|
188
174
|
"""Retry failed tool executions."""
|
189
175
|
failed_count = queryset.filter(status='failed').count()
|
190
|
-
messages.warning(request, f"Retry functionality not implemented yet. {failed_count} failed
|
176
|
+
messages.warning(request, f"Retry functionality not implemented yet. {failed_count} failed executions selected.")
|
191
177
|
|
192
|
-
@action(description="Clear
|
178
|
+
@action(description="Clear error messages", variant=ActionVariant.INFO)
|
193
179
|
def clear_errors(self, request, queryset):
|
194
|
-
"""Clear error messages."""
|
195
|
-
|
196
|
-
messages.info(request, f"
|
197
|
-
|
198
|
-
def get_queryset(self, request):
|
199
|
-
"""Optimize queryset."""
|
200
|
-
return super().get_queryset(request).select_related('user', 'agent_execution')
|
180
|
+
"""Clear error messages from executions."""
|
181
|
+
updated = queryset.update(error_message=None)
|
182
|
+
messages.info(request, f"Cleared error messages from {updated} executions.")
|
201
183
|
|
202
184
|
|
203
185
|
@admin.register(ApprovalLog)
|
204
|
-
class ApprovalLogAdmin(ModelAdmin, ExportMixin):
|
205
|
-
"""
|
186
|
+
class ApprovalLogAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
|
187
|
+
"""Enhanced admin for ApprovalLog model using Django Admin Utilities."""
|
188
|
+
|
189
|
+
# Performance optimization
|
190
|
+
select_related_fields = ['approved_by']
|
206
191
|
|
207
192
|
# Export-only configuration
|
208
193
|
export_form_class = ExportForm
|
209
194
|
|
210
195
|
list_display = [
|
211
|
-
'approval_id_display', 'tool_name_display', '
|
212
|
-
'
|
196
|
+
'approval_id_display', 'tool_name_display', 'status_display',
|
197
|
+
'approved_by_display', 'decision_time_display', 'expires_at_display'
|
213
198
|
]
|
214
|
-
|
199
|
+
list_display_links = ['approval_id_display', 'tool_name_display']
|
215
200
|
list_filter = [
|
216
|
-
'status', 'tool_name', 'requested_at',
|
217
|
-
('
|
218
|
-
('approved_by', AutocompleteSelectFilter),
|
219
|
-
('rejected_by', AutocompleteSelectFilter)
|
201
|
+
'status', 'tool_name', 'requested_at', 'expires_at',
|
202
|
+
('approved_by', AutocompleteSelectFilter)
|
220
203
|
]
|
221
|
-
search_fields = ['
|
222
|
-
autocomplete_fields = ['
|
204
|
+
search_fields = ['tool_name', 'tool_args', 'justification']
|
205
|
+
autocomplete_fields = ['approved_by']
|
223
206
|
readonly_fields = [
|
224
|
-
'
|
225
|
-
'is_expired', 'tool_args_preview', 'justification_preview'
|
207
|
+
'id', 'requested_at', 'decided_at', 'expires_at'
|
226
208
|
]
|
209
|
+
ordering = ['-requested_at']
|
227
210
|
|
228
211
|
# Unfold form field overrides
|
229
212
|
formfield_overrides = {
|
@@ -232,170 +215,134 @@ class ApprovalLogAdmin(ModelAdmin, ExportMixin):
|
|
232
215
|
}
|
233
216
|
|
234
217
|
fieldsets = (
|
235
|
-
("
|
236
|
-
'fields': ('
|
218
|
+
("🔐 Approval Info", {
|
219
|
+
'fields': ('id', 'tool_name', 'status', 'approved_by'),
|
237
220
|
'classes': ('tab',)
|
238
221
|
}),
|
239
222
|
("📝 Request Details", {
|
240
|
-
'fields': ('
|
223
|
+
'fields': ('tool_arguments', 'justification'),
|
241
224
|
'classes': ('tab',)
|
242
225
|
}),
|
243
|
-
("
|
244
|
-
'fields': ('
|
226
|
+
("⏰ Timing", {
|
227
|
+
'fields': ('created_at', 'decision_time', 'expires_at'),
|
245
228
|
'classes': ('tab',)
|
246
229
|
}),
|
247
|
-
("⏰ Timestamps", {
|
248
|
-
'fields': ('requested_at', 'decided_at', 'expires_at', 'time_to_decision', 'is_expired'),
|
249
|
-
'classes': ('tab', 'collapse')
|
250
|
-
}),
|
251
230
|
)
|
252
231
|
|
253
|
-
actions = ['
|
232
|
+
actions = ['approve_pending', 'reject_pending', 'extend_expiry']
|
254
233
|
|
255
234
|
@display(description="Approval ID")
|
256
235
|
def approval_id_display(self, obj):
|
257
236
|
"""Enhanced approval ID display."""
|
258
|
-
|
259
|
-
|
260
|
-
obj.
|
237
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.VERIFIED)
|
238
|
+
return StatusBadge.create(
|
239
|
+
text=f"#{str(obj.id)[:8]}",
|
240
|
+
variant="secondary",
|
241
|
+
config=config
|
261
242
|
)
|
262
243
|
|
263
244
|
@display(description="Tool")
|
264
245
|
def tool_name_display(self, obj):
|
265
246
|
"""Enhanced tool name display."""
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
247
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.BUILD)
|
248
|
+
return StatusBadge.create(
|
249
|
+
text=obj.tool_name,
|
250
|
+
variant="primary",
|
251
|
+
config=config
|
271
252
|
)
|
272
253
|
|
273
254
|
@display(description="Status")
|
274
|
-
def
|
275
|
-
"""Status
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
'
|
285
|
-
color_class, obj.get_status_display()
|
255
|
+
def status_display(self, obj):
|
256
|
+
"""Status display with appropriate icons."""
|
257
|
+
status_config = StatusBadgeConfig(
|
258
|
+
custom_mappings={
|
259
|
+
'pending': 'warning',
|
260
|
+
'approved': 'success',
|
261
|
+
'rejected': 'danger',
|
262
|
+
'expired': 'secondary'
|
263
|
+
},
|
264
|
+
show_icons=True,
|
265
|
+
icon=Icons.CHECK_CIRCLE if obj.status == 'approved' else Icons.CANCEL if obj.status == 'rejected' else Icons.SCHEDULE if obj.status == 'pending' else Icons.TIMER_OFF
|
286
266
|
)
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
return
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
)
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
decision_time
|
320
|
-
)
|
321
|
-
|
322
|
-
@display(description="Expiry", boolean=True)
|
323
|
-
def expiry_status(self, obj):
|
324
|
-
"""Expiry status."""
|
325
|
-
return not obj.is_expired
|
326
|
-
|
327
|
-
@display(description="Tool Args Preview")
|
328
|
-
def tool_args_preview(self, obj):
|
329
|
-
"""Preview of tool arguments."""
|
330
|
-
if not obj.tool_args:
|
331
|
-
return "-"
|
332
|
-
preview = str(obj.tool_args)[:200] + "..." if len(str(obj.tool_args)) > 200 else str(obj.tool_args)
|
333
|
-
return format_html(
|
334
|
-
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
335
|
-
preview
|
267
|
+
return self.display_status_auto(obj, 'status', status_config)
|
268
|
+
|
269
|
+
@display(description="Approved By")
|
270
|
+
def approved_by_display(self, obj):
|
271
|
+
"""Approved by user display."""
|
272
|
+
if not obj.approved_by:
|
273
|
+
return "—"
|
274
|
+
return self.display_user_simple(obj.approved_by)
|
275
|
+
|
276
|
+
@display(description="Decision Time")
|
277
|
+
def decision_time_display(self, obj):
|
278
|
+
"""Decision time display."""
|
279
|
+
if obj.decision_time:
|
280
|
+
return f"{obj.decision_time:.2f}s"
|
281
|
+
return "—"
|
282
|
+
|
283
|
+
@display(description="Expires")
|
284
|
+
def expires_at_display(self, obj):
|
285
|
+
"""Expiry time with relative display."""
|
286
|
+
if not obj.expires_at:
|
287
|
+
return "—"
|
288
|
+
|
289
|
+
config = DateTimeDisplayConfig(show_relative=True)
|
290
|
+
return self.display_datetime_relative(obj, 'expires_at', config)
|
291
|
+
|
292
|
+
@action(description="Approve pending", variant=ActionVariant.SUCCESS)
|
293
|
+
def approve_pending(self, request, queryset):
|
294
|
+
"""Approve pending approvals."""
|
295
|
+
updated = queryset.filter(status='pending').update(
|
296
|
+
status='approved',
|
297
|
+
approved_by=request.user,
|
298
|
+
decision_time=timezone.now()
|
336
299
|
)
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
preview
|
300
|
+
messages.success(request, f"Approved {updated} pending requests.")
|
301
|
+
|
302
|
+
@action(description="Reject pending", variant=ActionVariant.DANGER)
|
303
|
+
def reject_pending(self, request, queryset):
|
304
|
+
"""Reject pending approvals."""
|
305
|
+
updated = queryset.filter(status='pending').update(
|
306
|
+
status='rejected',
|
307
|
+
approved_by=request.user,
|
308
|
+
decision_time=timezone.now()
|
347
309
|
)
|
310
|
+
messages.warning(request, f"Rejected {updated} pending requests.")
|
348
311
|
|
349
|
-
@action(description="
|
350
|
-
def approve_selected(self, request, queryset):
|
351
|
-
"""Approve selected requests."""
|
352
|
-
count = 0
|
353
|
-
for approval in queryset.filter(status='pending'):
|
354
|
-
approval.approve(request.user)
|
355
|
-
count += 1
|
356
|
-
|
357
|
-
messages.success(request, f"Approved {count} requests.")
|
358
|
-
|
359
|
-
@action(description="Reject selected requests", icon="close", variant=ActionVariant.DANGER)
|
360
|
-
def reject_selected(self, request, queryset):
|
361
|
-
"""Reject selected requests."""
|
362
|
-
count = 0
|
363
|
-
for approval in queryset.filter(status='pending'):
|
364
|
-
approval.reject(request.user, "Bulk rejection by admin")
|
365
|
-
count += 1
|
366
|
-
|
367
|
-
messages.warning(request, f"Rejected {count} requests.")
|
368
|
-
|
369
|
-
@action(description="Extend expiry", icon="schedule", variant=ActionVariant.INFO)
|
312
|
+
@action(description="Extend expiry", variant=ActionVariant.INFO)
|
370
313
|
def extend_expiry(self, request, queryset):
|
371
|
-
"""Extend expiry time for
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
"""Optimize queryset."""
|
377
|
-
return super().get_queryset(request).select_related('user', 'approved_by', 'rejected_by')
|
314
|
+
"""Extend expiry time for pending approvals."""
|
315
|
+
from datetime import timedelta
|
316
|
+
new_expiry = timezone.now() + timedelta(hours=24)
|
317
|
+
updated = queryset.filter(status='pending').update(expires_at=new_expiry)
|
318
|
+
messages.info(request, f"Extended expiry for {updated} approvals by 24 hours.")
|
378
319
|
|
379
320
|
|
380
321
|
@admin.register(ToolsetConfiguration)
|
381
|
-
class ToolsetConfigurationAdmin(ModelAdmin,
|
382
|
-
"""
|
322
|
+
class ToolsetConfigurationAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
|
323
|
+
"""Enhanced admin for ToolsetConfiguration model using Django Admin Utilities."""
|
324
|
+
|
325
|
+
# Performance optimization
|
326
|
+
select_related_fields = ['created_by']
|
383
327
|
|
384
|
-
#
|
385
|
-
import_form_class = ImportForm
|
328
|
+
# Export-only configuration
|
386
329
|
export_form_class = ExportForm
|
387
330
|
|
388
331
|
list_display = [
|
389
|
-
'name_display', '
|
332
|
+
'name_display', 'toolset_class_display', 'status_display',
|
333
|
+
'usage_count_display', 'created_by_display', 'created_at_display'
|
390
334
|
]
|
391
|
-
|
335
|
+
list_display_links = ['name_display']
|
392
336
|
list_filter = [
|
393
|
-
'is_active', 'created_at',
|
337
|
+
'is_active', 'toolset_class', 'created_at',
|
394
338
|
('created_by', AutocompleteSelectFilter)
|
395
339
|
]
|
396
340
|
search_fields = ['name', 'description', 'toolset_class']
|
397
|
-
autocomplete_fields = ['created_by'
|
398
|
-
readonly_fields = [
|
341
|
+
autocomplete_fields = ['created_by']
|
342
|
+
readonly_fields = [
|
343
|
+
'id', 'created_at', 'updated_at'
|
344
|
+
]
|
345
|
+
ordering = ['-created_at']
|
399
346
|
|
400
347
|
# Unfold form field overrides
|
401
348
|
formfield_overrides = {
|
@@ -404,90 +351,96 @@ class ToolsetConfigurationAdmin(ModelAdmin, ImportExportModelAdmin):
|
|
404
351
|
}
|
405
352
|
|
406
353
|
fieldsets = (
|
407
|
-
("
|
408
|
-
'fields': ('name', 'description', 'toolset_class'),
|
354
|
+
("⚙️ Configuration Info", {
|
355
|
+
'fields': ('id', 'name', 'description', 'toolset_class'),
|
409
356
|
'classes': ('tab',)
|
410
357
|
}),
|
411
|
-
("
|
412
|
-
'fields': ('
|
358
|
+
("🔧 Settings", {
|
359
|
+
'fields': ('configuration', 'is_active'),
|
413
360
|
'classes': ('tab',)
|
414
361
|
}),
|
415
|
-
("
|
416
|
-
'fields': ('
|
362
|
+
("📊 Usage", {
|
363
|
+
'fields': ('usage_count',),
|
417
364
|
'classes': ('tab',)
|
418
365
|
}),
|
419
|
-
("
|
420
|
-
'fields': ('created_by', 'created_at', 'updated_at'),
|
366
|
+
("👤 Metadata", {
|
367
|
+
'fields': ('created_by', 'updated_by', 'created_at', 'updated_at'),
|
421
368
|
'classes': ('tab', 'collapse')
|
422
369
|
}),
|
423
370
|
)
|
424
371
|
|
425
|
-
actions = ['
|
372
|
+
actions = ['activate_configurations', 'deactivate_configurations', 'reset_usage']
|
426
373
|
|
427
|
-
@display(description="
|
374
|
+
@display(description="Configuration Name")
|
428
375
|
def name_display(self, obj):
|
429
|
-
"""Enhanced name display."""
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
376
|
+
"""Enhanced configuration name display."""
|
377
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.SETTINGS)
|
378
|
+
return StatusBadge.create(
|
379
|
+
text=obj.name,
|
380
|
+
variant="primary",
|
381
|
+
config=config
|
435
382
|
)
|
436
383
|
|
437
|
-
@display(description="Class")
|
438
|
-
def
|
439
|
-
"""Toolset class badge."""
|
384
|
+
@display(description="Toolset Class")
|
385
|
+
def toolset_class_display(self, obj):
|
386
|
+
"""Toolset class display with badge."""
|
440
387
|
if not obj.toolset_class:
|
441
|
-
return "
|
388
|
+
return "—"
|
389
|
+
|
390
|
+
# Extract class name from full path
|
442
391
|
class_name = obj.toolset_class.split('.')[-1] if '.' in obj.toolset_class else obj.toolset_class
|
443
|
-
return format_html(
|
444
|
-
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-cyan-100 text-cyan-800">{}</span>',
|
445
|
-
class_name
|
446
|
-
)
|
447
|
-
|
448
|
-
@display(description="Status", boolean=True)
|
449
|
-
def status_badge(self, obj):
|
450
|
-
"""Status badge."""
|
451
|
-
return obj.is_active
|
452
|
-
|
453
|
-
@display(description="Usage")
|
454
|
-
def usage_info(self, obj):
|
455
|
-
"""Usage information."""
|
456
|
-
users_count = obj.allowed_users.count() if obj.allowed_users.exists() else 0
|
457
|
-
groups_count = obj.allowed_groups.count() if obj.allowed_groups.exists() else 0
|
458
392
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
users_count,
|
465
|
-
groups_count
|
393
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.EXTENSION)
|
394
|
+
return StatusBadge.create(
|
395
|
+
text=class_name,
|
396
|
+
variant="info",
|
397
|
+
config=config
|
466
398
|
)
|
467
399
|
|
468
|
-
@display(description="
|
469
|
-
def
|
470
|
-
"""
|
471
|
-
if
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
)
|
400
|
+
@display(description="Status")
|
401
|
+
def status_display(self, obj):
|
402
|
+
"""Status display based on active state."""
|
403
|
+
if obj.is_active:
|
404
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.CHECK_CIRCLE)
|
405
|
+
return StatusBadge.create(text="Active", variant="success", config=config)
|
406
|
+
else:
|
407
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.PAUSE_CIRCLE)
|
408
|
+
return StatusBadge.create(text="Inactive", variant="secondary", config=config)
|
478
409
|
|
479
|
-
@
|
480
|
-
def
|
481
|
-
"""
|
410
|
+
@display(description="Usage")
|
411
|
+
def usage_count_display(self, obj):
|
412
|
+
"""Usage count display."""
|
413
|
+
if not obj.usage_count:
|
414
|
+
return "Not used"
|
415
|
+
return f"{obj.usage_count} times"
|
416
|
+
|
417
|
+
@display(description="Created By")
|
418
|
+
def created_by_display(self, obj):
|
419
|
+
"""Created by user display."""
|
420
|
+
if not obj.created_by:
|
421
|
+
return "—"
|
422
|
+
return self.display_user_simple(obj.created_by)
|
423
|
+
|
424
|
+
@display(description="Created")
|
425
|
+
def created_at_display(self, obj):
|
426
|
+
"""Created time with relative display."""
|
427
|
+
config = DateTimeDisplayConfig(show_relative=True)
|
428
|
+
return self.display_datetime_relative(obj, 'created_at', config)
|
429
|
+
|
430
|
+
@action(description="Activate configurations", variant=ActionVariant.SUCCESS)
|
431
|
+
def activate_configurations(self, request, queryset):
|
432
|
+
"""Activate selected configurations."""
|
482
433
|
updated = queryset.update(is_active=True)
|
483
|
-
messages.success(request, f"Activated {updated}
|
434
|
+
messages.success(request, f"Activated {updated} configurations.")
|
484
435
|
|
485
|
-
@action(description="Deactivate
|
486
|
-
def
|
487
|
-
"""Deactivate selected
|
436
|
+
@action(description="Deactivate configurations", variant=ActionVariant.WARNING)
|
437
|
+
def deactivate_configurations(self, request, queryset):
|
438
|
+
"""Deactivate selected configurations."""
|
488
439
|
updated = queryset.update(is_active=False)
|
489
|
-
messages.warning(request, f"Deactivated {updated}
|
440
|
+
messages.warning(request, f"Deactivated {updated} configurations.")
|
490
441
|
|
491
|
-
|
492
|
-
|
493
|
-
|
442
|
+
@action(description="Reset usage count", variant=ActionVariant.INFO)
|
443
|
+
def reset_usage(self, request, queryset):
|
444
|
+
"""Reset usage count for selected configurations."""
|
445
|
+
updated = queryset.update(usage_count=0)
|
446
|
+
messages.info(request, f"Reset usage count for {updated} configurations.")
|