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,248 @@
|
|
1
|
+
"""
|
2
|
+
Tariffs Admin interface using Django Admin Utilities.
|
3
|
+
|
4
|
+
Clean tariff management with pricing and limits display.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.contrib import admin
|
8
|
+
from django.db.models import Count, Q
|
9
|
+
from django.utils import timezone
|
10
|
+
from django.utils.html import format_html
|
11
|
+
from datetime import timedelta
|
12
|
+
|
13
|
+
from unfold.admin import ModelAdmin
|
14
|
+
|
15
|
+
from django_cfg.modules.django_admin import (
|
16
|
+
OptimizedModelAdmin,
|
17
|
+
DisplayMixin,
|
18
|
+
MoneyDisplayConfig,
|
19
|
+
DateTimeDisplayConfig,
|
20
|
+
StatusBadgeConfig,
|
21
|
+
Icons,
|
22
|
+
ActionVariant,
|
23
|
+
display,
|
24
|
+
action
|
25
|
+
)
|
26
|
+
from django_cfg.modules.django_admin.utils.badges import StatusBadge
|
27
|
+
|
28
|
+
from ..models.tariffs import Tariff, TariffEndpointGroup
|
29
|
+
|
30
|
+
|
31
|
+
@admin.register(Tariff)
|
32
|
+
class TariffAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
|
33
|
+
"""
|
34
|
+
Admin interface for Tariff model.
|
35
|
+
|
36
|
+
Features:
|
37
|
+
- Clean pricing display
|
38
|
+
- Rate limits visualization
|
39
|
+
- Feature flags display
|
40
|
+
- Automatic query optimization
|
41
|
+
- Type-safe configuration
|
42
|
+
"""
|
43
|
+
|
44
|
+
# Performance optimization
|
45
|
+
select_related_fields = []
|
46
|
+
annotations = {}
|
47
|
+
|
48
|
+
# List configuration
|
49
|
+
list_display = [
|
50
|
+
'name_display',
|
51
|
+
'code_display',
|
52
|
+
'pricing_display',
|
53
|
+
'rate_limits_display',
|
54
|
+
'features_display',
|
55
|
+
'status_display',
|
56
|
+
'created_at_display'
|
57
|
+
]
|
58
|
+
|
59
|
+
list_filter = [
|
60
|
+
'is_active',
|
61
|
+
'is_public',
|
62
|
+
'supports_webhooks',
|
63
|
+
'priority_support',
|
64
|
+
'created_at'
|
65
|
+
]
|
66
|
+
|
67
|
+
search_fields = ['name', 'code', 'description']
|
68
|
+
|
69
|
+
readonly_fields = [
|
70
|
+
'created_at',
|
71
|
+
'updated_at',
|
72
|
+
'yearly_discount_percentage'
|
73
|
+
]
|
74
|
+
|
75
|
+
# Display methods using Unfold features
|
76
|
+
@display(description="Name", ordering="name")
|
77
|
+
def name_display(self, obj):
|
78
|
+
"""Tariff name display."""
|
79
|
+
return obj.name
|
80
|
+
|
81
|
+
@display(description="Code", ordering="code")
|
82
|
+
def code_display(self, obj):
|
83
|
+
"""Tariff code display."""
|
84
|
+
return obj.code
|
85
|
+
|
86
|
+
@display(description="Pricing", ordering="monthly_price_usd")
|
87
|
+
def pricing_display(self, obj):
|
88
|
+
"""Pricing display using utilities."""
|
89
|
+
monthly = self.display_money_amount(
|
90
|
+
obj,
|
91
|
+
'monthly_price_usd',
|
92
|
+
MoneyDisplayConfig(currency="USD", show_sign=False)
|
93
|
+
)
|
94
|
+
|
95
|
+
if obj.yearly_price_usd:
|
96
|
+
yearly = self.display_money_amount(
|
97
|
+
obj,
|
98
|
+
'yearly_price_usd',
|
99
|
+
MoneyDisplayConfig(currency="USD", show_sign=False)
|
100
|
+
)
|
101
|
+
discount = f"{obj.yearly_discount_percentage:.0f}% off"
|
102
|
+
return format_html("{}/mo • {}/yr ({})", monthly, yearly, discount)
|
103
|
+
|
104
|
+
return format_html("{}/mo", monthly)
|
105
|
+
|
106
|
+
@display(description="Rate Limits")
|
107
|
+
def rate_limits_display(self, obj):
|
108
|
+
"""Rate limits display."""
|
109
|
+
return f"{obj.requests_per_hour:,}/hr • {obj.requests_per_day:,}/day • {obj.requests_per_month:,}/mo"
|
110
|
+
|
111
|
+
@display(description="Features")
|
112
|
+
def features_display(self, obj):
|
113
|
+
"""Features display."""
|
114
|
+
features = []
|
115
|
+
|
116
|
+
features.append(f"{obj.max_api_keys} API keys")
|
117
|
+
|
118
|
+
if obj.supports_webhooks:
|
119
|
+
features.append("Webhooks")
|
120
|
+
|
121
|
+
if obj.priority_support:
|
122
|
+
features.append("Priority Support")
|
123
|
+
|
124
|
+
return " • ".join(features)
|
125
|
+
|
126
|
+
@display(description="Status", label={
|
127
|
+
"Active": "success",
|
128
|
+
"Private": "warning",
|
129
|
+
"Inactive": "danger"
|
130
|
+
})
|
131
|
+
def status_display(self, obj):
|
132
|
+
"""Status display using Unfold label feature."""
|
133
|
+
if obj.is_active and obj.is_public:
|
134
|
+
return "Active"
|
135
|
+
elif obj.is_active:
|
136
|
+
return "Private"
|
137
|
+
else:
|
138
|
+
return "Inactive"
|
139
|
+
|
140
|
+
@display(description="Created")
|
141
|
+
def created_at_display(self, obj):
|
142
|
+
"""Created at display."""
|
143
|
+
config = DateTimeDisplayConfig(
|
144
|
+
show_relative=True,
|
145
|
+
show_seconds=False
|
146
|
+
)
|
147
|
+
return self.display_datetime_relative(obj, 'created_at', config)
|
148
|
+
|
149
|
+
|
150
|
+
@admin.register(TariffEndpointGroup)
|
151
|
+
class TariffEndpointGroupAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
|
152
|
+
"""
|
153
|
+
Admin interface for TariffEndpointGroup model.
|
154
|
+
|
155
|
+
Features:
|
156
|
+
- Tariff and endpoint group relationships
|
157
|
+
- Custom rate limit display
|
158
|
+
- Clean utilities integration
|
159
|
+
"""
|
160
|
+
|
161
|
+
# Performance optimization
|
162
|
+
select_related_fields = ['tariff', 'endpoint_group']
|
163
|
+
annotations = {}
|
164
|
+
|
165
|
+
# List configuration
|
166
|
+
list_display = [
|
167
|
+
'tariff_display',
|
168
|
+
'endpoint_group_display',
|
169
|
+
'rate_limit_display',
|
170
|
+
'status_display',
|
171
|
+
'created_at_display'
|
172
|
+
]
|
173
|
+
|
174
|
+
list_filter = [
|
175
|
+
'is_enabled',
|
176
|
+
'tariff__is_active',
|
177
|
+
'created_at'
|
178
|
+
]
|
179
|
+
|
180
|
+
search_fields = [
|
181
|
+
'tariff__name',
|
182
|
+
'endpoint_group__name'
|
183
|
+
]
|
184
|
+
|
185
|
+
readonly_fields = [
|
186
|
+
'created_at',
|
187
|
+
'updated_at',
|
188
|
+
'effective_rate_limit'
|
189
|
+
]
|
190
|
+
|
191
|
+
# Display methods
|
192
|
+
@display(description="Tariff")
|
193
|
+
def tariff_display(self, obj):
|
194
|
+
"""Tariff display."""
|
195
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.PRICE_CHANGE)
|
196
|
+
return StatusBadge.create(
|
197
|
+
text=obj.tariff.name,
|
198
|
+
variant="primary",
|
199
|
+
config=config
|
200
|
+
)
|
201
|
+
|
202
|
+
@display(description="Endpoint Group")
|
203
|
+
def endpoint_group_display(self, obj):
|
204
|
+
"""Endpoint group display."""
|
205
|
+
config = StatusBadgeConfig(show_icons=True, icon=Icons.GROUP)
|
206
|
+
return StatusBadge.create(
|
207
|
+
text=obj.endpoint_group.name,
|
208
|
+
variant="info",
|
209
|
+
config=config
|
210
|
+
)
|
211
|
+
|
212
|
+
@display(description="Rate Limit")
|
213
|
+
def rate_limit_display(self, obj):
|
214
|
+
"""Rate limit display."""
|
215
|
+
effective = obj.effective_rate_limit
|
216
|
+
|
217
|
+
if obj.custom_rate_limit:
|
218
|
+
return f"{effective:,}/hr (custom)"
|
219
|
+
else:
|
220
|
+
return f"{effective:,}/hr (default)"
|
221
|
+
|
222
|
+
@display(description="Status")
|
223
|
+
def status_display(self, obj):
|
224
|
+
"""Status display."""
|
225
|
+
if obj.is_enabled:
|
226
|
+
status = "Enabled"
|
227
|
+
variant = "success"
|
228
|
+
icon = Icons.CHECK_CIRCLE
|
229
|
+
else:
|
230
|
+
status = "Disabled"
|
231
|
+
variant = "danger"
|
232
|
+
icon = Icons.CANCEL
|
233
|
+
|
234
|
+
config = StatusBadgeConfig(show_icons=True, icon=icon)
|
235
|
+
return StatusBadge.create(
|
236
|
+
text=status,
|
237
|
+
variant=variant,
|
238
|
+
config=config
|
239
|
+
)
|
240
|
+
|
241
|
+
@display(description="Created")
|
242
|
+
def created_at_display(self, obj):
|
243
|
+
"""Created at display."""
|
244
|
+
config = DateTimeDisplayConfig(
|
245
|
+
show_relative=True,
|
246
|
+
show_seconds=False
|
247
|
+
)
|
248
|
+
return self.display_datetime_relative(obj, 'created_at', config)
|
@@ -21,65 +21,118 @@ class AdminUserSerializer(serializers.ModelSerializer):
|
|
21
21
|
read_only_fields = fields
|
22
22
|
|
23
23
|
|
24
|
-
class AdminPaymentListSerializer(serializers.
|
24
|
+
class AdminPaymentListSerializer(serializers.Serializer):
|
25
25
|
"""
|
26
26
|
Serializer for payment list in admin interface.
|
27
|
+
Uses UniversalPayment only for data extraction.
|
27
28
|
"""
|
29
|
+
id = serializers.UUIDField(read_only=True)
|
28
30
|
user = AdminUserSerializer(read_only=True)
|
29
|
-
|
30
|
-
|
31
|
+
amount_usd = serializers.FloatField(read_only=True)
|
32
|
+
currency_code = serializers.SerializerMethodField()
|
33
|
+
currency_name = serializers.SerializerMethodField()
|
34
|
+
provider = serializers.CharField(read_only=True)
|
35
|
+
provider_display = serializers.SerializerMethodField()
|
36
|
+
status = serializers.CharField(read_only=True)
|
37
|
+
status_display = serializers.SerializerMethodField()
|
38
|
+
pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, read_only=True)
|
39
|
+
pay_address = serializers.CharField(read_only=True)
|
40
|
+
transaction_hash = serializers.CharField(read_only=True)
|
41
|
+
created_at = serializers.DateTimeField(read_only=True)
|
42
|
+
updated_at = serializers.DateTimeField(read_only=True)
|
43
|
+
description = serializers.CharField(read_only=True)
|
31
44
|
age = serializers.SerializerMethodField()
|
32
45
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
46
|
+
def get_currency_code(self, obj):
|
47
|
+
"""Get currency code from related Currency model."""
|
48
|
+
return obj.currency.code if obj.currency else None
|
49
|
+
|
50
|
+
def get_currency_name(self, obj):
|
51
|
+
"""Get currency name from related Currency model."""
|
52
|
+
return obj.currency.name if obj.currency else None
|
53
|
+
|
54
|
+
def get_provider_display(self, obj):
|
55
|
+
"""Get human-readable provider name."""
|
56
|
+
return obj.get_provider_display()
|
57
|
+
|
58
|
+
def get_status_display(self, obj):
|
59
|
+
"""Get human-readable status."""
|
60
|
+
return obj.get_status_display()
|
41
61
|
|
42
62
|
def get_age(self, obj):
|
43
63
|
"""Get human-readable age of payment."""
|
44
|
-
|
64
|
+
from django.contrib.humanize.templatetags.humanize import naturaltime
|
65
|
+
return naturaltime(obj.created_at)
|
45
66
|
|
46
67
|
|
47
|
-
class AdminPaymentDetailSerializer(serializers.
|
68
|
+
class AdminPaymentDetailSerializer(serializers.Serializer):
|
48
69
|
"""
|
49
70
|
Detailed serializer for individual payment in admin interface.
|
71
|
+
Uses UniversalPayment only for data extraction.
|
50
72
|
"""
|
73
|
+
id = serializers.UUIDField(read_only=True)
|
51
74
|
user = AdminUserSerializer(read_only=True)
|
52
|
-
|
53
|
-
|
75
|
+
internal_payment_id = serializers.CharField(read_only=True)
|
76
|
+
amount_usd = serializers.FloatField(read_only=True)
|
77
|
+
actual_amount_usd = serializers.FloatField(read_only=True)
|
78
|
+
fee_amount_usd = serializers.FloatField(read_only=True)
|
79
|
+
currency_code = serializers.SerializerMethodField()
|
80
|
+
currency_name = serializers.SerializerMethodField()
|
81
|
+
provider = serializers.CharField(read_only=True)
|
82
|
+
provider_display = serializers.SerializerMethodField()
|
83
|
+
status = serializers.CharField(read_only=True)
|
84
|
+
status_display = serializers.SerializerMethodField()
|
85
|
+
pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, read_only=True)
|
86
|
+
pay_address = serializers.CharField(read_only=True)
|
87
|
+
payment_url = serializers.URLField(read_only=True)
|
88
|
+
transaction_hash = serializers.CharField(read_only=True)
|
89
|
+
confirmations_count = serializers.IntegerField(read_only=True)
|
90
|
+
security_nonce = serializers.CharField(read_only=True)
|
91
|
+
expires_at = serializers.DateTimeField(read_only=True)
|
92
|
+
completed_at = serializers.DateTimeField(read_only=True)
|
93
|
+
description = serializers.CharField(read_only=True)
|
94
|
+
callback_url = serializers.URLField(read_only=True)
|
95
|
+
cancel_url = serializers.URLField(read_only=True)
|
96
|
+
provider_data = serializers.JSONField(read_only=True)
|
97
|
+
webhook_data = serializers.JSONField(read_only=True)
|
98
|
+
created_at = serializers.DateTimeField(read_only=True)
|
99
|
+
updated_at = serializers.DateTimeField(read_only=True)
|
54
100
|
age = serializers.SerializerMethodField()
|
55
101
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
102
|
+
def get_currency_code(self, obj):
|
103
|
+
"""Get currency code from related Currency model."""
|
104
|
+
return obj.currency.code if obj.currency else None
|
105
|
+
|
106
|
+
def get_currency_name(self, obj):
|
107
|
+
"""Get currency name from related Currency model."""
|
108
|
+
return obj.currency.name if obj.currency else None
|
109
|
+
|
110
|
+
def get_provider_display(self, obj):
|
111
|
+
"""Get human-readable provider name."""
|
112
|
+
return obj.get_provider_display()
|
113
|
+
|
114
|
+
def get_status_display(self, obj):
|
115
|
+
"""Get human-readable status."""
|
116
|
+
return obj.get_status_display()
|
67
117
|
|
68
118
|
def get_age(self, obj):
|
69
119
|
"""Get human-readable age of payment."""
|
70
|
-
|
120
|
+
from django.contrib.humanize.templatetags.humanize import naturaltime
|
121
|
+
return naturaltime(obj.created_at)
|
71
122
|
|
72
123
|
|
73
|
-
class AdminPaymentCreateSerializer(serializers.
|
124
|
+
class AdminPaymentCreateSerializer(serializers.Serializer):
|
74
125
|
"""
|
75
126
|
Serializer for creating payments in admin interface.
|
127
|
+
Uses UniversalPayment only for data creation.
|
76
128
|
"""
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
129
|
+
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
|
130
|
+
amount_usd = serializers.FloatField(min_value=1.0, max_value=100000.0)
|
131
|
+
currency_code = serializers.CharField(max_length=10, help_text="Currency code (e.g., BTC, ETH)")
|
132
|
+
provider = serializers.CharField(max_length=50)
|
133
|
+
description = serializers.CharField(required=False, allow_blank=True)
|
134
|
+
callback_url = serializers.URLField(required=False, allow_blank=True)
|
135
|
+
cancel_url = serializers.URLField(required=False, allow_blank=True)
|
83
136
|
|
84
137
|
def validate_amount_usd(self, value):
|
85
138
|
"""Validate USD amount."""
|
@@ -88,6 +141,24 @@ class AdminPaymentCreateSerializer(serializers.ModelSerializer):
|
|
88
141
|
if value > 100000: # Max $100k per payment
|
89
142
|
raise serializers.ValidationError("Amount exceeds maximum limit")
|
90
143
|
return value
|
144
|
+
|
145
|
+
def create(self, validated_data):
|
146
|
+
"""Create payment with currency lookup."""
|
147
|
+
from django_cfg.apps.payments.models.currencies import Currency
|
148
|
+
import uuid
|
149
|
+
|
150
|
+
# Extract currency_code and find Currency object
|
151
|
+
currency_code = validated_data.pop('currency_code')
|
152
|
+
try:
|
153
|
+
currency = Currency.objects.get(code=currency_code)
|
154
|
+
except Currency.DoesNotExist:
|
155
|
+
raise serializers.ValidationError(f"Currency {currency_code} not found")
|
156
|
+
|
157
|
+
# Generate internal payment ID and create payment
|
158
|
+
validated_data['currency'] = currency
|
159
|
+
validated_data['internal_payment_id'] = f"PAY-{uuid.uuid4().hex[:12].upper()}"
|
160
|
+
|
161
|
+
return UniversalPayment.objects.create(**validated_data)
|
91
162
|
|
92
163
|
|
93
164
|
class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
|
@@ -96,12 +96,8 @@
|
|
96
96
|
class="block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white disabled:opacity-50"
|
97
97
|
required>
|
98
98
|
<option value="">Select currency...</option>
|
99
|
-
<template x-for="currency in currencies" :key="currency.code">
|
100
|
-
<option :value="currency.code">
|
101
|
-
<span x-text="currency.code"></span> -
|
102
|
-
<span x-text="currency.name"></span>
|
103
|
-
<span x-show="currency.type === 'crypto'" class="text-blue-600"> (Crypto)</span>
|
104
|
-
<span x-show="currency.network_name" class="text-gray-500" x-text="`[${currency.network_name}]`"></span>
|
99
|
+
<template x-for="currency in currencies" :key="`${currency.code}-${currency.provider_code || currency.network || Math.random()}`">
|
100
|
+
<option :value="currency.code" x-text="`${currency.code} - ${currency.name}${currency.type === 'crypto' ? ' (Crypto)' : ''}${currency.network_name ? ' [' + currency.network_name + ']' : ''}`">
|
105
101
|
</option>
|
106
102
|
</template>
|
107
103
|
</select>
|
@@ -122,22 +118,22 @@
|
|
122
118
|
<span class="text-gray-500">Network:</span>
|
123
119
|
<span x-text="getCurrencyInfo(form.currency_code)?.network_name"></span>
|
124
120
|
</div>
|
125
|
-
<div x-show="getCurrencyInfo(form.currency_code)?.
|
121
|
+
<div x-show="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd || getCurrencyInfo(form.currency_code)?.provider_max_amount_usd" class="mt-1">
|
126
122
|
<span class="text-gray-500">Limits:</span>
|
127
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
128
|
-
Min: $<span x-text="getCurrencyInfo(form.currency_code)?.
|
123
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd">
|
124
|
+
Min: $<span x-text="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd"></span>
|
129
125
|
</span>
|
130
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
131
|
-
Max: $<span x-text="getCurrencyInfo(form.currency_code)?.
|
126
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_max_amount_usd">
|
127
|
+
Max: $<span x-text="getCurrencyInfo(form.currency_code)?.provider_max_amount_usd"></span>
|
132
128
|
</span>
|
133
129
|
</div>
|
134
|
-
<div x-show="getCurrencyInfo(form.currency_code)?.
|
130
|
+
<div x-show="getCurrencyInfo(form.currency_code)?.provider_fee_percentage > 0 || getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd > 0" class="mt-1">
|
135
131
|
<span class="text-gray-500">Fees:</span>
|
136
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
137
|
-
<span x-text="(getCurrencyInfo(form.currency_code)?.
|
132
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_fee_percentage > 0">
|
133
|
+
<span x-text="(getCurrencyInfo(form.currency_code)?.provider_fee_percentage * 100).toFixed(2)"></span>%
|
138
134
|
</span>
|
139
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
140
|
-
+ $<span x-text="getCurrencyInfo(form.currency_code)?.
|
135
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd > 0">
|
136
|
+
+ $<span x-text="getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd"></span>
|
141
137
|
</span>
|
142
138
|
</div>
|
143
139
|
</div>
|
@@ -13,6 +13,7 @@ from .api import (
|
|
13
13
|
AdminPaymentViewSet,
|
14
14
|
AdminWebhookViewSet,
|
15
15
|
AdminWebhookEventViewSet,
|
16
|
+
WebhookTestViewSet,
|
16
17
|
AdminStatsViewSet,
|
17
18
|
AdminUserViewSet,
|
18
19
|
)
|
@@ -29,6 +30,7 @@ __all__ = [
|
|
29
30
|
'AdminPaymentViewSet',
|
30
31
|
'AdminWebhookViewSet',
|
31
32
|
'AdminWebhookEventViewSet',
|
33
|
+
'WebhookTestViewSet',
|
32
34
|
'AdminStatsViewSet',
|
33
35
|
'AdminUserViewSet',
|
34
36
|
]
|
@@ -79,39 +79,34 @@ class AdminWebhookViewSet(AdminReadOnlyViewSet):
|
|
79
79
|
|
80
80
|
# Mock webhook stats based on real payment data
|
81
81
|
stats_data = {
|
82
|
-
'
|
83
|
-
'
|
84
|
-
'
|
82
|
+
'total': total_payments * 2, # Assume 2 events per payment on average
|
83
|
+
'successful': int(total_payments * 1.8), # 90% success rate
|
84
|
+
'failed': int(total_payments * 0.2), # 10% failure rate
|
85
|
+
'pending': 0, # No pending events for now
|
85
86
|
'success_rate': 90.0,
|
86
|
-
'recent_events': recent_payments * 2,
|
87
87
|
'providers': {
|
88
88
|
'nowpayments': {
|
89
89
|
'total': int(total_payments * 0.7),
|
90
90
|
'successful': int(total_payments * 0.65),
|
91
91
|
'failed': int(total_payments * 0.05),
|
92
|
+
'pending': 0,
|
92
93
|
'success_rate': 92.8
|
93
94
|
},
|
94
95
|
'stripe': {
|
95
96
|
'total': int(total_payments * 0.3),
|
96
97
|
'successful': int(total_payments * 0.28),
|
97
98
|
'failed': int(total_payments * 0.02),
|
99
|
+
'pending': 0,
|
98
100
|
'success_rate': 93.3
|
99
101
|
}
|
100
102
|
},
|
101
|
-
'
|
102
|
-
'
|
103
|
-
'
|
104
|
-
'
|
103
|
+
'last_24h': {
|
104
|
+
'total': recent_payments * 2,
|
105
|
+
'successful': int(recent_payments * 1.8),
|
106
|
+
'failed': int(recent_payments * 0.2),
|
105
107
|
},
|
106
|
-
'
|
107
|
-
|
108
|
-
'timestamp': timezone.now() - timedelta(minutes=i*5),
|
109
|
-
'event_type': 'payment.created' if i % 3 == 0 else 'payment.completed',
|
110
|
-
'provider': 'nowpayments' if i % 2 == 0 else 'stripe',
|
111
|
-
'status': 'success' if i % 4 != 0 else 'failed'
|
112
|
-
}
|
113
|
-
for i in range(10)
|
114
|
-
]
|
108
|
+
'avg_response_time': 150.5, # milliseconds
|
109
|
+
'max_response_time': 2500, # milliseconds
|
115
110
|
}
|
116
111
|
|
117
112
|
serializer = self.get_serializer(stats_data)
|
@@ -140,7 +135,7 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
|
|
140
135
|
# In real implementation, this would return WebhookEvent.objects.all()
|
141
136
|
return UniversalPayment.objects.none()
|
142
137
|
|
143
|
-
def list(self, request):
|
138
|
+
def list(self, request, webhook_pk=None):
|
144
139
|
"""List webhook events with filtering and pagination."""
|
145
140
|
# Get filter parameters
|
146
141
|
event_type = request.query_params.get('event_type')
|