django-cfg 1.3.5__py3-none-any.whl → 1.3.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +258 -0
- django_cfg/apps/payments/admin/payments_admin.py +171 -461
- 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 +105 -34
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
- 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/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
- django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
- django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -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/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +1 -1
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +95 -39
- django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
- django_cfg/apps/payments/services/providers/models/base.py +122 -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.py → nowpayments/provider.py} +240 -209
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +4 -32
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
- django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
- 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/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +5 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +4 -3
- 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/urls.py +1 -2
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/app_agent_diagnose.py +470 -0
- django_cfg/management/commands/app_agent_generate.py +342 -0
- django_cfg/management/commands/app_agent_info.py +308 -0
- 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/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- 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_app_agent/__init__.py +87 -0
- django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
- django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
- django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
- django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
- django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
- django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
- django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
- django_cfg/modules/django_app_agent/core/__init__.py +33 -0
- django_cfg/modules/django_app_agent/core/config.py +300 -0
- django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
- django_cfg/modules/django_app_agent/models/__init__.py +71 -0
- django_cfg/modules/django_app_agent/models/base.py +283 -0
- django_cfg/modules/django_app_agent/models/context.py +496 -0
- django_cfg/modules/django_app_agent/models/enums.py +481 -0
- django_cfg/modules/django_app_agent/models/requests.py +500 -0
- django_cfg/modules/django_app_agent/models/responses.py +585 -0
- django_cfg/modules/django_app_agent/pytest.ini +6 -0
- django_cfg/modules/django_app_agent/services/__init__.py +42 -0
- django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
- django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
- django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
- django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
- django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
- django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
- django_cfg/modules/django_app_agent/services/base.py +437 -0
- django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
- django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
- django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
- django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
- django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
- django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
- django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
- django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
- django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
- django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
- django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
- django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
- django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
- django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
- django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
- django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
- django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
- django_cfg/modules/django_app_agent/services/report_service.py +332 -0
- django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
- django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
- django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
- django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
- django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
- django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
- django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
- django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
- django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
- django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
- django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
- django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
- django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
- django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
- django_cfg/modules/django_app_agent/ui/cli.py +419 -0
- django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
- django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
- django_cfg/modules/django_app_agent/utils/logging.py +360 -0
- django_cfg/modules/django_app_agent/utils/validation.py +417 -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_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 +3 -0
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/RECORD +224 -118
- 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/tasks/admin.py +0 -320
- 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.5.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,202 @@
|
|
1
|
+
"""
|
2
|
+
Standalone Actions Mixin for Django Admin.
|
3
|
+
|
4
|
+
Provides convenient decorators and utilities for creating standalone action buttons
|
5
|
+
that work independently of selected items (not bulk actions).
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Callable, Optional, Any
|
9
|
+
from functools import wraps
|
10
|
+
from django.contrib import messages
|
11
|
+
from django.shortcuts import redirect
|
12
|
+
from django.http import HttpResponse
|
13
|
+
import threading
|
14
|
+
|
15
|
+
from unfold.decorators import action as unfold_action
|
16
|
+
from unfold.enums import ActionVariant as UnfoldActionVariant
|
17
|
+
from django_cfg.modules.django_logger import get_logger
|
18
|
+
from ..models.action_models import ActionVariant
|
19
|
+
|
20
|
+
|
21
|
+
logger = get_logger("django_admin.mixins.standalone_actions")
|
22
|
+
|
23
|
+
|
24
|
+
def standalone_action(
|
25
|
+
description: str,
|
26
|
+
variant: Optional[ActionVariant] = None,
|
27
|
+
icon: Optional[str] = None,
|
28
|
+
url_path: Optional[str] = None,
|
29
|
+
background: bool = False,
|
30
|
+
success_message: Optional[str] = None,
|
31
|
+
error_message: Optional[str] = None
|
32
|
+
):
|
33
|
+
"""
|
34
|
+
Decorator for creating standalone action buttons.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
description: Button text
|
38
|
+
variant: Button style (ActionVariant enum)
|
39
|
+
icon: Material icon name
|
40
|
+
url_path: URL path for the action (auto-generated if not provided)
|
41
|
+
background: Run action in background thread
|
42
|
+
success_message: Success message template (can use {result} placeholder)
|
43
|
+
error_message: Error message template (can use {error} placeholder)
|
44
|
+
|
45
|
+
Usage:
|
46
|
+
@standalone_action(
|
47
|
+
description="Update Rates",
|
48
|
+
variant=ActionVariant.SUCCESS,
|
49
|
+
icon="sync",
|
50
|
+
background=True,
|
51
|
+
success_message="💱 Rates update started! Refresh in 2-3 minutes.",
|
52
|
+
error_message="❌ Failed to start update: {error}"
|
53
|
+
)
|
54
|
+
def update_rates(self, request):
|
55
|
+
# Your logic here
|
56
|
+
call_command('manage_currencies', '--populate')
|
57
|
+
return "Update completed"
|
58
|
+
"""
|
59
|
+
def decorator(func: Callable) -> Callable:
|
60
|
+
@wraps(func)
|
61
|
+
def wrapper(self, request: Any) -> HttpResponse:
|
62
|
+
try:
|
63
|
+
if background:
|
64
|
+
# Run in background thread
|
65
|
+
def background_task():
|
66
|
+
try:
|
67
|
+
result = func(self, request)
|
68
|
+
logger.info(f"Background action {func.__name__} completed: {result}")
|
69
|
+
except Exception as e:
|
70
|
+
logger.error(f"Background action {func.__name__} failed: {e}")
|
71
|
+
|
72
|
+
thread = threading.Thread(target=background_task)
|
73
|
+
thread.daemon = True
|
74
|
+
thread.start()
|
75
|
+
|
76
|
+
# Show immediate success message
|
77
|
+
if success_message:
|
78
|
+
messages.success(request, success_message)
|
79
|
+
else:
|
80
|
+
messages.success(request, f"{description} started in background.")
|
81
|
+
|
82
|
+
else:
|
83
|
+
# Run synchronously
|
84
|
+
result = func(self, request)
|
85
|
+
|
86
|
+
# Show success message
|
87
|
+
if success_message:
|
88
|
+
msg = success_message.format(result=result) if result else success_message
|
89
|
+
messages.success(request, msg)
|
90
|
+
else:
|
91
|
+
messages.success(request, f"{description} completed successfully.")
|
92
|
+
|
93
|
+
logger.info(f"Standalone action {func.__name__} executed by {request.user.username}")
|
94
|
+
|
95
|
+
except Exception as e:
|
96
|
+
# Show error message
|
97
|
+
if error_message:
|
98
|
+
msg = error_message.format(error=str(e))
|
99
|
+
messages.error(request, msg)
|
100
|
+
else:
|
101
|
+
messages.error(request, f"❌ {description} failed: {str(e)}")
|
102
|
+
|
103
|
+
logger.error(f"Standalone action {func.__name__} failed: {e}")
|
104
|
+
|
105
|
+
# Always redirect back
|
106
|
+
return redirect(request.META.get('HTTP_REFERER', '/admin/'))
|
107
|
+
|
108
|
+
# Convert ActionVariant to UnfoldActionVariant
|
109
|
+
unfold_variant = UnfoldActionVariant.DEFAULT
|
110
|
+
if variant:
|
111
|
+
variant_mapping = {
|
112
|
+
ActionVariant.DEFAULT: UnfoldActionVariant.DEFAULT,
|
113
|
+
ActionVariant.PRIMARY: UnfoldActionVariant.PRIMARY,
|
114
|
+
ActionVariant.SUCCESS: UnfoldActionVariant.SUCCESS,
|
115
|
+
ActionVariant.INFO: UnfoldActionVariant.INFO,
|
116
|
+
ActionVariant.WARNING: UnfoldActionVariant.WARNING,
|
117
|
+
ActionVariant.DANGER: UnfoldActionVariant.DANGER,
|
118
|
+
}
|
119
|
+
unfold_variant = variant_mapping.get(variant, UnfoldActionVariant.DEFAULT)
|
120
|
+
|
121
|
+
# Auto-generate url_path if not provided
|
122
|
+
final_url_path = url_path or func.__name__.replace('_', '-')
|
123
|
+
|
124
|
+
# Apply unfold decorator
|
125
|
+
decorator_kwargs = {
|
126
|
+
'description': description,
|
127
|
+
'variant': unfold_variant,
|
128
|
+
'url_path': final_url_path
|
129
|
+
}
|
130
|
+
if icon:
|
131
|
+
decorator_kwargs['icon'] = icon
|
132
|
+
|
133
|
+
return unfold_action(**decorator_kwargs)(wrapper)
|
134
|
+
|
135
|
+
return decorator
|
136
|
+
|
137
|
+
|
138
|
+
class StandaloneActionsMixin:
|
139
|
+
"""
|
140
|
+
Mixin for Django admin classes that provides utilities for standalone actions.
|
141
|
+
|
142
|
+
Usage:
|
143
|
+
class MyAdmin(OptimizedModelAdmin, DisplayMixin, StandaloneActionsMixin, ModelAdmin):
|
144
|
+
actions_list = ['update_data', 'sync_external']
|
145
|
+
|
146
|
+
@standalone_action(
|
147
|
+
description="Update Data",
|
148
|
+
variant=ActionVariant.SUCCESS,
|
149
|
+
icon="sync",
|
150
|
+
background=True
|
151
|
+
)
|
152
|
+
def update_data(self, request):
|
153
|
+
# Your update logic
|
154
|
+
return "Data updated"
|
155
|
+
"""
|
156
|
+
|
157
|
+
def get_standalone_actions(self):
|
158
|
+
"""Get list of standalone action method names."""
|
159
|
+
return getattr(self, 'actions_list', [])
|
160
|
+
|
161
|
+
def execute_background_task(self, task_func: Callable, *args, **kwargs):
|
162
|
+
"""
|
163
|
+
Utility method to execute tasks in background.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
task_func: Function to execute
|
167
|
+
*args, **kwargs: Arguments for the function
|
168
|
+
"""
|
169
|
+
def background_task():
|
170
|
+
try:
|
171
|
+
result = task_func(*args, **kwargs)
|
172
|
+
logger.info(f"Background task {task_func.__name__} completed: {result}")
|
173
|
+
return result
|
174
|
+
except Exception as e:
|
175
|
+
logger.error(f"Background task {task_func.__name__} failed: {e}")
|
176
|
+
raise
|
177
|
+
|
178
|
+
thread = threading.Thread(target=background_task)
|
179
|
+
thread.daemon = True
|
180
|
+
thread.start()
|
181
|
+
return thread
|
182
|
+
|
183
|
+
def send_admin_notification(self, request, message: str, level: str = 'INFO'):
|
184
|
+
"""
|
185
|
+
Send notification to admin user.
|
186
|
+
|
187
|
+
Args:
|
188
|
+
request: Django request object
|
189
|
+
message: Notification message
|
190
|
+
level: Message level (SUCCESS, INFO, WARNING, ERROR)
|
191
|
+
"""
|
192
|
+
level_mapping = {
|
193
|
+
'SUCCESS': messages.SUCCESS,
|
194
|
+
'INFO': messages.INFO,
|
195
|
+
'WARNING': messages.WARNING,
|
196
|
+
'ERROR': messages.ERROR,
|
197
|
+
}
|
198
|
+
|
199
|
+
django_level = level_mapping.get(level.upper(), messages.INFO)
|
200
|
+
messages.add_message(request, django_level, message)
|
201
|
+
|
202
|
+
logger.info(f"Admin notification sent to {request.user.username}: {message}")
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
Pydantic 2 models for configuration.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .base import BaseConfig
|
6
|
+
from .display_models import UserDisplayConfig, MoneyDisplayConfig, DateTimeDisplayConfig
|
7
|
+
from .badge_models import BadgeConfig, BadgeVariant, StatusBadgeConfig
|
8
|
+
from .action_models import ActionVariant, ActionConfig
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"BaseConfig",
|
12
|
+
"UserDisplayConfig",
|
13
|
+
"MoneyDisplayConfig",
|
14
|
+
"DateTimeDisplayConfig",
|
15
|
+
"BadgeConfig",
|
16
|
+
"BadgeVariant",
|
17
|
+
"StatusBadgeConfig",
|
18
|
+
"ActionVariant",
|
19
|
+
"ActionConfig",
|
20
|
+
]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
"""
|
2
|
+
Action configuration models.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from enum import Enum
|
6
|
+
from pydantic import Field
|
7
|
+
from typing import Optional, List
|
8
|
+
from .base import BaseConfig
|
9
|
+
|
10
|
+
|
11
|
+
class ActionVariant(str, Enum):
|
12
|
+
"""
|
13
|
+
Action variant enum for consistent styling.
|
14
|
+
|
15
|
+
Based on Unfold ActionVariant but with our own namespace.
|
16
|
+
Matches unfold.enums.ActionVariant exactly.
|
17
|
+
"""
|
18
|
+
DEFAULT = "default"
|
19
|
+
PRIMARY = "primary"
|
20
|
+
SUCCESS = "success"
|
21
|
+
INFO = "info"
|
22
|
+
WARNING = "warning"
|
23
|
+
DANGER = "danger"
|
24
|
+
|
25
|
+
|
26
|
+
class ActionConfig(BaseConfig):
|
27
|
+
"""Action configuration."""
|
28
|
+
variant: ActionVariant = Field(default=ActionVariant.PRIMARY)
|
29
|
+
icon: Optional[str] = Field(default=None)
|
30
|
+
permissions: List[str] = Field(default=[])
|
31
|
+
confirm_message: Optional[str] = Field(default=None)
|
32
|
+
success_message: Optional[str] = Field(default=None)
|
33
|
+
error_message: Optional[str] = Field(default=None)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
Badge configuration models.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from pydantic import Field
|
6
|
+
from typing import Optional, Dict
|
7
|
+
from .base import BaseConfig, BadgeVariant
|
8
|
+
|
9
|
+
|
10
|
+
class BadgeConfig(BaseConfig):
|
11
|
+
"""Base badge configuration."""
|
12
|
+
variant: BadgeVariant = Field(default=BadgeVariant.INFO)
|
13
|
+
icon: Optional[str] = Field(default=None)
|
14
|
+
css_classes: list = Field(default=[])
|
15
|
+
|
16
|
+
|
17
|
+
class StatusBadgeConfig(BadgeConfig):
|
18
|
+
"""Status badge configuration."""
|
19
|
+
custom_mappings: Dict[str, str] = Field(default={})
|
20
|
+
show_icons: bool = Field(default=True)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
Base Pydantic 2 models.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from pydantic import BaseModel, Field, ConfigDict
|
6
|
+
from typing import List
|
7
|
+
from enum import Enum
|
8
|
+
|
9
|
+
|
10
|
+
class BaseConfig(BaseModel):
|
11
|
+
"""Base configuration for all utilities."""
|
12
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
13
|
+
|
14
|
+
cache_timeout: int = Field(default=300, ge=0, le=3600)
|
15
|
+
enable_icons: bool = Field(default=True)
|
16
|
+
debug_mode: bool = Field(default=False)
|
17
|
+
|
18
|
+
|
19
|
+
class BadgeVariant(str, Enum):
|
20
|
+
"""Badge color variants."""
|
21
|
+
SUCCESS = "success"
|
22
|
+
WARNING = "warning"
|
23
|
+
DANGER = "danger"
|
24
|
+
INFO = "info"
|
25
|
+
PRIMARY = "primary"
|
26
|
+
SECONDARY = "secondary"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"""
|
2
|
+
Display configuration models.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from pydantic import Field
|
6
|
+
from .base import BaseConfig
|
7
|
+
|
8
|
+
|
9
|
+
class UserDisplayConfig(BaseConfig):
|
10
|
+
"""User display configuration."""
|
11
|
+
show_email: bool = Field(default=True)
|
12
|
+
show_avatar: bool = Field(default=True)
|
13
|
+
avatar_size: int = Field(default=32, ge=16, le=128)
|
14
|
+
|
15
|
+
|
16
|
+
class MoneyDisplayConfig(BaseConfig):
|
17
|
+
"""Money display configuration."""
|
18
|
+
currency: str = Field(default="USD", min_length=3, max_length=3)
|
19
|
+
show_sign: bool = Field(default=True)
|
20
|
+
decimal_places: int = Field(default=2, ge=0, le=8)
|
21
|
+
thousand_separator: bool = Field(default=True)
|
22
|
+
show_currency_symbol: bool = Field(default=True)
|
23
|
+
smart_decimal_places: bool = Field(default=False) # Auto-adjust decimal places based on value
|
24
|
+
rate_mode: bool = Field(default=False) # Special formatting for exchange rates
|
25
|
+
|
26
|
+
|
27
|
+
class DateTimeDisplayConfig(BaseConfig):
|
28
|
+
"""DateTime display configuration."""
|
29
|
+
show_relative: bool = Field(default=True)
|
30
|
+
show_seconds: bool = Field(default=False)
|
31
|
+
datetime_format: str = Field(default="%Y-%m-%d %H:%M:%S")
|
@@ -0,0 +1,159 @@
|
|
1
|
+
"""
|
2
|
+
Badge utilities with Material Icons.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import Optional, Union
|
7
|
+
from django.utils.html import format_html, escape
|
8
|
+
from django.utils.safestring import SafeString
|
9
|
+
from django.contrib.humanize.templatetags.humanize import intcomma
|
10
|
+
|
11
|
+
from ..models.badge_models import BadgeConfig, StatusBadgeConfig
|
12
|
+
from ..models.base import BadgeVariant
|
13
|
+
from ..icons import Icons
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class StatusBadge:
|
19
|
+
"""Status badge utilities."""
|
20
|
+
|
21
|
+
# Status mappings
|
22
|
+
STATUS_MAPPINGS = {
|
23
|
+
'active': BadgeVariant.SUCCESS,
|
24
|
+
'success': BadgeVariant.SUCCESS,
|
25
|
+
'completed': BadgeVariant.SUCCESS,
|
26
|
+
'pending': BadgeVariant.WARNING,
|
27
|
+
'processing': BadgeVariant.WARNING,
|
28
|
+
'failed': BadgeVariant.DANGER,
|
29
|
+
'error': BadgeVariant.DANGER,
|
30
|
+
'cancelled': BadgeVariant.DANGER,
|
31
|
+
'inactive': BadgeVariant.SECONDARY,
|
32
|
+
}
|
33
|
+
|
34
|
+
# Variant classes with semantic colors
|
35
|
+
VARIANT_CLASSES = {
|
36
|
+
BadgeVariant.SUCCESS: 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200',
|
37
|
+
BadgeVariant.WARNING: 'bg-warning-100 text-warning-800 dark:bg-warning-900 dark:text-warning-200',
|
38
|
+
BadgeVariant.DANGER: 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
|
39
|
+
BadgeVariant.INFO: 'bg-info-100 text-info-800 dark:bg-info-900 dark:text-info-200',
|
40
|
+
BadgeVariant.PRIMARY: 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200',
|
41
|
+
BadgeVariant.SECONDARY: 'bg-base-100 text-font-default-light dark:bg-base-800 dark:text-font-default-dark',
|
42
|
+
}
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def auto(cls, status: str, config: Optional[StatusBadgeConfig] = None) -> SafeString:
|
46
|
+
"""Auto status badge with color mapping."""
|
47
|
+
config = config or StatusBadgeConfig()
|
48
|
+
|
49
|
+
if not status:
|
50
|
+
return format_html('<span class="text-font-subtle-light dark:text-font-subtle-dark">—</span>')
|
51
|
+
|
52
|
+
# Determine variant
|
53
|
+
status_lower = status.lower().replace('_', '').replace('-', '')
|
54
|
+
variant = BadgeVariant.INFO
|
55
|
+
|
56
|
+
for keyword, mapped_variant in cls.STATUS_MAPPINGS.items():
|
57
|
+
if keyword in status_lower:
|
58
|
+
variant = mapped_variant
|
59
|
+
break
|
60
|
+
|
61
|
+
# Use custom mapping if provided
|
62
|
+
if config.custom_mappings and status in config.custom_mappings:
|
63
|
+
variant_str = config.custom_mappings[status]
|
64
|
+
try:
|
65
|
+
variant = BadgeVariant(variant_str)
|
66
|
+
except ValueError:
|
67
|
+
pass
|
68
|
+
|
69
|
+
return cls.create(status.replace('_', ' ').title(), variant, config)
|
70
|
+
|
71
|
+
@classmethod
|
72
|
+
def create(cls, text: str, variant: Union[BadgeVariant, str] = BadgeVariant.INFO,
|
73
|
+
config: Optional[StatusBadgeConfig] = None, icon: Optional[str] = None) -> SafeString:
|
74
|
+
"""Create custom badge."""
|
75
|
+
config = config or StatusBadgeConfig()
|
76
|
+
|
77
|
+
if isinstance(variant, str):
|
78
|
+
try:
|
79
|
+
variant = BadgeVariant(variant)
|
80
|
+
except ValueError:
|
81
|
+
variant = BadgeVariant.INFO
|
82
|
+
|
83
|
+
css_classes = cls.VARIANT_CLASSES.get(variant, cls.VARIANT_CLASSES[BadgeVariant.INFO])
|
84
|
+
|
85
|
+
if config.css_classes:
|
86
|
+
css_classes += ' ' + ' '.join(config.css_classes)
|
87
|
+
|
88
|
+
# Icon with Material Icons integration
|
89
|
+
icon_html = ""
|
90
|
+
if icon or (config.show_icons and config.icon):
|
91
|
+
icon_to_use = icon or config.icon
|
92
|
+
if icon_to_use:
|
93
|
+
# Use custom icon
|
94
|
+
icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon_to_use)
|
95
|
+
else:
|
96
|
+
# Auto-detect icon based on variant
|
97
|
+
icon_map = {
|
98
|
+
BadgeVariant.SUCCESS: Icons.CHECK_CIRCLE,
|
99
|
+
BadgeVariant.WARNING: Icons.WARNING,
|
100
|
+
BadgeVariant.DANGER: Icons.ERROR,
|
101
|
+
BadgeVariant.INFO: Icons.INFO,
|
102
|
+
BadgeVariant.PRIMARY: Icons.STAR,
|
103
|
+
BadgeVariant.SECONDARY: Icons.INFO,
|
104
|
+
}
|
105
|
+
icon_name = icon_map.get(variant, Icons.INFO)
|
106
|
+
icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon_name)
|
107
|
+
|
108
|
+
return format_html(
|
109
|
+
'<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {}">'
|
110
|
+
'{}{}'
|
111
|
+
'</span>',
|
112
|
+
css_classes, icon_html, escape(text)
|
113
|
+
)
|
114
|
+
|
115
|
+
|
116
|
+
class ProgressBadge:
|
117
|
+
"""Progress badge utilities."""
|
118
|
+
|
119
|
+
@classmethod
|
120
|
+
def percentage(cls, percentage: Union[int, float]) -> SafeString:
|
121
|
+
"""Progress badge with percentage."""
|
122
|
+
percentage = max(0, min(100, float(percentage)))
|
123
|
+
|
124
|
+
if percentage >= 100:
|
125
|
+
variant = BadgeVariant.SUCCESS
|
126
|
+
elif percentage >= 75:
|
127
|
+
variant = BadgeVariant.INFO
|
128
|
+
elif percentage >= 50:
|
129
|
+
variant = BadgeVariant.WARNING
|
130
|
+
else:
|
131
|
+
variant = BadgeVariant.SECONDARY
|
132
|
+
|
133
|
+
return StatusBadge.create(f"{percentage:.0f}%", variant)
|
134
|
+
|
135
|
+
|
136
|
+
class CounterBadge:
|
137
|
+
"""Counter badge utilities."""
|
138
|
+
|
139
|
+
@classmethod
|
140
|
+
def simple(cls, count: int, label: str = None) -> SafeString:
|
141
|
+
"""Simple counter badge."""
|
142
|
+
if count == 0:
|
143
|
+
variant = BadgeVariant.SECONDARY
|
144
|
+
elif count < 10:
|
145
|
+
variant = BadgeVariant.INFO
|
146
|
+
elif count < 100:
|
147
|
+
variant = BadgeVariant.WARNING
|
148
|
+
else:
|
149
|
+
variant = BadgeVariant.SUCCESS
|
150
|
+
|
151
|
+
# Format with humanize for large numbers
|
152
|
+
if count >= 1000:
|
153
|
+
count_text = intcomma(count)
|
154
|
+
else:
|
155
|
+
count_text = str(count)
|
156
|
+
|
157
|
+
display_text = f"{count_text} {label}" if label else count_text
|
158
|
+
|
159
|
+
return StatusBadge.create(display_text, variant)
|