django-cfg 1.3.1__py3-none-any.whl → 1.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
  3. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
  4. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
  5. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
  6. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
  7. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
  8. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
  9. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
  10. django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
  11. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
  12. django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
  13. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
  14. django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
  23. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
  24. django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
  25. django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
  26. django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
  27. django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
  28. django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
  29. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
  30. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
  31. django_cfg/apps/payments/admin_interface/views/base.py +114 -0
  32. django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
  33. django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
  34. django_cfg/apps/payments/config/helpers.py +2 -2
  35. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +429 -0
  36. django_cfg/apps/payments/management/commands/currency_stats.py +443 -0
  37. django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
  38. django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
  39. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  40. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  41. django_cfg/apps/payments/middleware/api_access.py +35 -34
  42. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  43. django_cfg/apps/payments/models/balance.py +5 -2
  44. django_cfg/apps/payments/models/managers/api_key_managers.py +6 -2
  45. django_cfg/apps/payments/models/managers/balance_managers.py +3 -3
  46. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  47. django_cfg/apps/payments/models/managers/subscription_managers.py +3 -3
  48. django_cfg/apps/payments/models/subscriptions.py +0 -24
  49. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  50. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  51. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  52. django_cfg/apps/payments/services/cache_service/interfaces.py +32 -0
  53. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  54. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  55. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  56. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  57. django_cfg/apps/payments/services/core/payment_service.py +49 -22
  58. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  59. django_cfg/apps/payments/services/providers/registry.py +20 -0
  60. django_cfg/apps/payments/signals/api_key_signals.py +2 -2
  61. django_cfg/apps/payments/signals/balance_signals.py +8 -5
  62. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  63. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  64. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  65. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  66. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  67. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  68. django_cfg/apps/payments/urls.py +4 -0
  69. django_cfg/apps/payments/urls_admin.py +37 -18
  70. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  71. django_cfg/apps/payments/views/api/base.py +1 -0
  72. django_cfg/apps/payments/views/api/currencies.py +2 -2
  73. django_cfg/apps/payments/views/api/payments.py +11 -5
  74. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  75. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  76. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  77. django_cfg/apps/payments/views/overview/services.py +439 -0
  78. django_cfg/apps/payments/views/overview/urls.py +27 -0
  79. django_cfg/apps/payments/views/overview/views.py +231 -0
  80. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  81. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  82. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  83. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  84. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  85. django_cfg/apps/urls.py +2 -1
  86. django_cfg/core/config.py +25 -15
  87. django_cfg/core/generation.py +12 -12
  88. django_cfg/core/integration/display/startup.py +1 -1
  89. django_cfg/core/validation.py +4 -4
  90. django_cfg/management/commands/show_config.py +2 -2
  91. django_cfg/management/commands/tree.py +1 -3
  92. django_cfg/middleware/__init__.py +2 -0
  93. django_cfg/middleware/static_nocache.py +55 -0
  94. django_cfg/models/payments.py +13 -15
  95. django_cfg/models/security.py +15 -0
  96. django_cfg/modules/django_ngrok.py +6 -0
  97. django_cfg/modules/django_unfold/dashboard.py +1 -3
  98. django_cfg/utils/smart_defaults.py +51 -5
  99. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
  100. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/RECORD +111 -69
  101. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  102. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  103. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  104. django_cfg/apps/payments/services/cache/cache_service.py +0 -235
  105. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  106. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  107. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  108. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  109. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  110. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  111. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  112. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  113. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
  114. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
  115. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,23 +1,34 @@
1
1
  """
2
- Template views for the Universal Payment System v2.0.
2
+ Admin Interface Views for Universal Payment System v2.0.
3
3
 
4
- Django template views for dashboard and management interfaces.
4
+ DRF ViewSets and template views for admin dashboard and management interfaces.
5
5
  """
6
6
 
7
- from .webhook_dashboard import WebhookDashboardView
8
- from .payment_views import (
9
- PaymentFormView,
10
- PaymentStatusView,
11
- PaymentListView,
12
- PaymentDashboardView,
13
- CurrencyConverterView,
7
+ # Template Views
8
+ from .dashboard import PaymentDashboardView, WebhookDashboardView
9
+ from .forms import PaymentFormView, PaymentDetailView, PaymentListView
10
+
11
+ # API ViewSets
12
+ from .api import (
13
+ AdminPaymentViewSet,
14
+ AdminWebhookViewSet,
15
+ AdminWebhookEventViewSet,
16
+ AdminStatsViewSet,
17
+ AdminUserViewSet,
14
18
  )
15
19
 
16
20
  __all__ = [
21
+ # Template Views
22
+ 'PaymentDashboardView',
17
23
  'WebhookDashboardView',
18
- 'PaymentFormView',
19
- 'PaymentStatusView',
24
+ 'PaymentFormView',
25
+ 'PaymentDetailView',
20
26
  'PaymentListView',
21
- 'PaymentDashboardView',
22
- 'CurrencyConverterView',
23
- ]
27
+
28
+ # API ViewSets
29
+ 'AdminPaymentViewSet',
30
+ 'AdminWebhookViewSet',
31
+ 'AdminWebhookEventViewSet',
32
+ 'AdminStatsViewSet',
33
+ 'AdminUserViewSet',
34
+ ]
@@ -0,0 +1,20 @@
1
+ """
2
+ Admin Interface API ViewSets.
3
+
4
+ DRF ViewSets for admin dashboard with nested routing.
5
+ """
6
+
7
+ from .payments import AdminPaymentViewSet
8
+ from .webhook_admin import AdminWebhookViewSet, AdminWebhookEventViewSet
9
+ from .webhook_public import WebhookTestViewSet
10
+ from .stats import AdminStatsViewSet
11
+ from .users import AdminUserViewSet
12
+
13
+ __all__ = [
14
+ 'AdminPaymentViewSet',
15
+ 'AdminWebhookViewSet',
16
+ 'AdminWebhookEventViewSet',
17
+ 'WebhookTestViewSet',
18
+ 'AdminStatsViewSet',
19
+ 'AdminUserViewSet',
20
+ ]
@@ -0,0 +1,191 @@
1
+ """
2
+ Admin Payment ViewSets.
3
+
4
+ DRF ViewSets for payment management in admin interface.
5
+ """
6
+
7
+ from rest_framework import viewsets, status
8
+ from rest_framework.decorators import action
9
+ from rest_framework.response import Response
10
+ from django_filters.rest_framework import DjangoFilterBackend
11
+ from rest_framework.filters import SearchFilter, OrderingFilter
12
+ from django.db.models import Count, Sum, Avg, Q
13
+ from django.utils import timezone
14
+ from datetime import timedelta
15
+
16
+ from django_cfg.apps.payments.admin_interface.views.base import AdminBaseViewSet
17
+ from django_cfg.apps.payments.models import UniversalPayment
18
+ from django_cfg.apps.payments.admin_interface.serializers import (
19
+ AdminPaymentListSerializer,
20
+ AdminPaymentDetailSerializer,
21
+ AdminPaymentCreateSerializer,
22
+ AdminPaymentUpdateSerializer,
23
+ AdminPaymentStatsSerializer,
24
+ )
25
+ from django_cfg.modules.django_logger import get_logger
26
+
27
+ logger = get_logger("admin_payment_api")
28
+
29
+
30
+ class AdminPaymentViewSet(AdminBaseViewSet):
31
+ """
32
+ Admin ViewSet for payment management.
33
+
34
+ Provides full CRUD operations for payments with admin-specific features.
35
+ """
36
+
37
+ queryset = UniversalPayment.objects.select_related('user').order_by('-created_at')
38
+ serializer_class = AdminPaymentDetailSerializer
39
+
40
+ serializer_classes = {
41
+ 'list': AdminPaymentListSerializer,
42
+ 'create': AdminPaymentCreateSerializer,
43
+ 'update': AdminPaymentUpdateSerializer,
44
+ 'partial_update': AdminPaymentUpdateSerializer,
45
+ 'stats': AdminPaymentStatsSerializer,
46
+ }
47
+
48
+ filterset_fields = ['status', 'provider', 'currency__code', 'user']
49
+ search_fields = ['internal_payment_id', 'transaction_hash', 'description', 'user__username', 'user__email']
50
+ ordering_fields = ['created_at', 'amount_usd', 'status']
51
+
52
+ def get_queryset(self):
53
+ """Optimized queryset for admin interface."""
54
+ queryset = super().get_queryset()
55
+
56
+ # Add filters based on query params
57
+ status_filter = self.request.query_params.get('status')
58
+ if status_filter:
59
+ queryset = queryset.filter(status=status_filter)
60
+
61
+ provider_filter = self.request.query_params.get('provider')
62
+ if provider_filter:
63
+ queryset = queryset.filter(provider=provider_filter)
64
+
65
+ # Date range filter
66
+ date_from = self.request.query_params.get('date_from')
67
+ date_to = self.request.query_params.get('date_to')
68
+ if date_from:
69
+ queryset = queryset.filter(created_at__gte=date_from)
70
+ if date_to:
71
+ queryset = queryset.filter(created_at__lte=date_to)
72
+
73
+ return queryset
74
+
75
+ @action(detail=False, methods=['get'])
76
+ def stats(self, request):
77
+ """Get comprehensive payment statistics."""
78
+ queryset = self.get_queryset()
79
+
80
+ # Basic stats
81
+ total_payments = queryset.count()
82
+ total_amount = queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0
83
+
84
+ # Status breakdown
85
+ status_stats = queryset.values('status').annotate(count=Count('id'))
86
+ successful = sum(s['count'] for s in status_stats if s['status'] in ['completed', 'confirmed'])
87
+ failed = sum(s['count'] for s in status_stats if s['status'] == 'failed')
88
+ pending = sum(s['count'] for s in status_stats if s['status'] in ['pending', 'confirming'])
89
+
90
+ success_rate = (successful / total_payments * 100) if total_payments > 0 else 0
91
+
92
+ # Provider breakdown
93
+ provider_stats = {}
94
+ for provider_data in queryset.values('provider').annotate(
95
+ count=Count('id'),
96
+ total_amount=Sum('amount_usd')
97
+ ):
98
+ provider_stats[provider_data['provider']] = {
99
+ 'count': provider_data['count'],
100
+ 'total_amount': provider_data['total_amount'] or 0,
101
+ }
102
+
103
+ # Currency breakdown
104
+ currency_stats = {}
105
+ for currency_data in queryset.values('currency_code').annotate(
106
+ count=Count('id'),
107
+ total_amount=Sum('amount_usd')
108
+ ):
109
+ currency_stats[currency_data['currency_code']] = {
110
+ 'count': currency_data['count'],
111
+ 'total_amount': currency_data['total_amount'] or 0,
112
+ }
113
+
114
+ # Time-based stats
115
+ now = timezone.now()
116
+ last_24h = queryset.filter(created_at__gte=now - timedelta(hours=24)).aggregate(
117
+ count=Count('id'),
118
+ amount=Sum('amount_usd')
119
+ )
120
+ last_7d = queryset.filter(created_at__gte=now - timedelta(days=7)).aggregate(
121
+ count=Count('id'),
122
+ amount=Sum('amount_usd')
123
+ )
124
+ last_30d = queryset.filter(created_at__gte=now - timedelta(days=30)).aggregate(
125
+ count=Count('id'),
126
+ amount=Sum('amount_usd')
127
+ )
128
+
129
+ stats_data = {
130
+ 'total_payments': total_payments,
131
+ 'total_amount_usd': total_amount,
132
+ 'successful_payments': successful,
133
+ 'failed_payments': failed,
134
+ 'pending_payments': pending,
135
+ 'success_rate': round(success_rate, 2),
136
+ 'by_provider': provider_stats,
137
+ 'by_currency': currency_stats,
138
+ 'last_24h': {
139
+ 'count': last_24h['count'] or 0,
140
+ 'amount': last_24h['amount'] or 0,
141
+ },
142
+ 'last_7d': {
143
+ 'count': last_7d['count'] or 0,
144
+ 'amount': last_7d['amount'] or 0,
145
+ },
146
+ 'last_30d': {
147
+ 'count': last_30d['count'] or 0,
148
+ 'amount': last_30d['amount'] or 0,
149
+ },
150
+ }
151
+
152
+ serializer = self.get_serializer(stats_data)
153
+ return Response(serializer.data)
154
+
155
+ @action(detail=True, methods=['post'])
156
+ def cancel(self, request, pk=None):
157
+ """Cancel a payment."""
158
+ payment = self.get_object()
159
+
160
+ if payment.status not in ['pending', 'confirming']:
161
+ return Response(
162
+ {'error': 'Payment cannot be cancelled in current status'},
163
+ status=status.HTTP_400_BAD_REQUEST
164
+ )
165
+
166
+ payment.status = UniversalPayment.PaymentStatus.CANCELLED
167
+ payment.save()
168
+
169
+ logger.info(f"Payment {payment.id} cancelled by admin {request.user.id}")
170
+
171
+ serializer = self.get_serializer(payment)
172
+ return Response(serializer.data)
173
+
174
+ @action(detail=True, methods=['post'])
175
+ def refund(self, request, pk=None):
176
+ """Refund a payment."""
177
+ payment = self.get_object()
178
+
179
+ if payment.status != 'completed':
180
+ return Response(
181
+ {'error': 'Only completed payments can be refunded'},
182
+ status=status.HTTP_400_BAD_REQUEST
183
+ )
184
+
185
+ payment.status = UniversalPayment.PaymentStatus.REFUNDED
186
+ payment.save()
187
+
188
+ logger.info(f"Payment {payment.id} refunded by admin {request.user.id}")
189
+
190
+ serializer = self.get_serializer(payment)
191
+ return Response(serializer.data)
@@ -0,0 +1,206 @@
1
+ """
2
+ Admin Statistics ViewSets.
3
+
4
+ DRF ViewSets for comprehensive statistics in admin interface.
5
+ """
6
+
7
+ from rest_framework import viewsets, status
8
+ from rest_framework.decorators import action
9
+ from rest_framework.response import Response
10
+ from django.db.models import Count, Sum, Avg, Q
11
+ from django.utils import timezone
12
+ from datetime import timedelta
13
+
14
+
15
+ from django_cfg.modules.django_logger import get_logger
16
+ from django_cfg.apps.payments.models import UniversalPayment
17
+ from ..base import AdminReadOnlyViewSet
18
+ from django_cfg.apps.payments.admin_interface.serializers import AdminPaymentStatsSerializer, WebhookStatsSerializer
19
+
20
+ logger = get_logger("admin_stats_api")
21
+
22
+
23
+ class AdminStatsViewSet(AdminReadOnlyViewSet):
24
+ """
25
+ Admin ViewSet for comprehensive system statistics.
26
+
27
+ Provides aggregated statistics across all system components.
28
+ """
29
+
30
+ # No model - this is for aggregated statistics
31
+ serializer_class = AdminPaymentStatsSerializer
32
+
33
+ def list(self, request):
34
+ """Get overview statistics."""
35
+ now = timezone.now()
36
+
37
+ # Payment statistics
38
+ payments_queryset = UniversalPayment.objects.all()
39
+
40
+ payment_stats = {
41
+ 'total_payments': payments_queryset.count(),
42
+ 'total_amount_usd': payments_queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0,
43
+ 'successful_payments': payments_queryset.filter(status='completed').count(),
44
+ 'failed_payments': payments_queryset.filter(status='failed').count(),
45
+ 'pending_payments': payments_queryset.filter(status__in=['pending', 'confirming']).count(),
46
+ }
47
+
48
+ # Calculate success rate
49
+ total = payment_stats['total_payments']
50
+ if total > 0:
51
+ payment_stats['success_rate'] = (payment_stats['successful_payments'] / total) * 100
52
+ else:
53
+ payment_stats['success_rate'] = 0
54
+
55
+ # Time-based payment stats
56
+ payment_stats['last_24h'] = {
57
+ 'count': payments_queryset.filter(created_at__gte=now - timedelta(hours=24)).count(),
58
+ 'amount': payments_queryset.filter(created_at__gte=now - timedelta(hours=24)).aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0,
59
+ }
60
+
61
+ # Mock webhook statistics (replace with real data)
62
+ webhook_stats = {
63
+ 'total_events': 156,
64
+ 'successful_events': 142,
65
+ 'failed_events': 12,
66
+ 'pending_events': 2,
67
+ 'webhook_success_rate': 91.0,
68
+ }
69
+
70
+ # System health metrics
71
+ system_stats = {
72
+ 'active_users_24h': 45, # Mock data
73
+ 'api_requests_24h': 1250, # Mock data
74
+ 'avg_response_time': 245.5, # Mock data
75
+ 'uptime_percentage': 99.9, # Mock data
76
+ }
77
+
78
+ return Response({
79
+ 'payments': payment_stats,
80
+ 'webhooks': webhook_stats,
81
+ 'system': system_stats,
82
+ 'last_updated': now.isoformat(),
83
+ })
84
+
85
+ @action(detail=False, methods=['get'])
86
+ def payments(self, request):
87
+ """Get detailed payment statistics."""
88
+ queryset = UniversalPayment.objects.all()
89
+ now = timezone.now()
90
+
91
+ # Basic stats
92
+ total_payments = queryset.count()
93
+ total_amount = queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0
94
+
95
+ # Status breakdown
96
+ status_stats = queryset.values('status').annotate(count=Count('id'))
97
+ successful = sum(s['count'] for s in status_stats if s['status'] in ['completed', 'confirmed'])
98
+ failed = sum(s['count'] for s in status_stats if s['status'] == 'failed')
99
+ pending = sum(s['count'] for s in status_stats if s['status'] in ['pending', 'confirming'])
100
+
101
+ success_rate = (successful / total_payments * 100) if total_payments > 0 else 0
102
+
103
+ # Provider breakdown
104
+ provider_stats = {}
105
+ for provider_data in queryset.values('provider').annotate(
106
+ count=Count('id'),
107
+ total_amount=Sum('amount_usd'),
108
+ successful=Count('id', filter=Q(status='completed')),
109
+ failed=Count('id', filter=Q(status='failed'))
110
+ ):
111
+ provider = provider_data['provider']
112
+ provider_stats[provider] = {
113
+ 'count': provider_data['count'],
114
+ 'total_amount': provider_data['total_amount'] or 0,
115
+ 'successful': provider_data['successful'],
116
+ 'failed': provider_data['failed'],
117
+ 'success_rate': (provider_data['successful'] / provider_data['count'] * 100) if provider_data['count'] > 0 else 0,
118
+ }
119
+
120
+ # Currency breakdown
121
+ currency_stats = {}
122
+ for currency_data in queryset.values('currency__code').annotate(
123
+ count=Count('id'),
124
+ total_amount=Sum('amount_usd')
125
+ ):
126
+ currency_stats[currency_data['currency__code']] = {
127
+ 'count': currency_data['count'],
128
+ 'total_amount': currency_data['total_amount'] or 0,
129
+ }
130
+
131
+ # Time-based stats
132
+ time_ranges = {
133
+ 'last_24h': now - timedelta(hours=24),
134
+ 'last_7d': now - timedelta(days=7),
135
+ 'last_30d': now - timedelta(days=30),
136
+ }
137
+
138
+ time_stats = {}
139
+ for period, start_date in time_ranges.items():
140
+ period_queryset = queryset.filter(created_at__gte=start_date)
141
+ time_stats[period] = {
142
+ 'count': period_queryset.count(),
143
+ 'amount': period_queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0,
144
+ 'successful': period_queryset.filter(status='completed').count(),
145
+ 'failed': period_queryset.filter(status='failed').count(),
146
+ }
147
+
148
+ stats_data = {
149
+ 'total_payments': total_payments,
150
+ 'total_amount_usd': total_amount,
151
+ 'successful_payments': successful,
152
+ 'failed_payments': failed,
153
+ 'pending_payments': pending,
154
+ 'success_rate': round(success_rate, 2),
155
+ 'by_provider': provider_stats,
156
+ 'by_currency': currency_stats,
157
+ **time_stats,
158
+ }
159
+
160
+ serializer = AdminPaymentStatsSerializer(stats_data)
161
+ return Response(serializer.data)
162
+
163
+ @action(detail=False, methods=['get'])
164
+ def webhooks(self, request):
165
+ """Get detailed webhook statistics."""
166
+ # Mock webhook statistics - replace with real data from webhook event model
167
+ stats_data = {
168
+ 'total': 156,
169
+ 'successful': 142,
170
+ 'failed': 12,
171
+ 'pending': 2,
172
+ 'success_rate': 91.0,
173
+ 'providers': {
174
+ 'nowpayments': {'total': 89, 'successful': 85, 'failed': 4, 'success_rate': 95.5},
175
+ 'stripe': {'total': 45, 'successful': 42, 'failed': 3, 'success_rate': 93.3},
176
+ 'cryptapi': {'total': 22, 'successful': 15, 'failed': 5, 'pending': 2, 'success_rate': 68.2},
177
+ },
178
+ 'last_24h': {
179
+ 'total': 23,
180
+ 'successful': 21,
181
+ 'failed': 2,
182
+ },
183
+ 'avg_response_time': 245.5,
184
+ 'max_response_time': 3000,
185
+ }
186
+
187
+ serializer = WebhookStatsSerializer(stats_data)
188
+ return Response(serializer.data)
189
+
190
+ @action(detail=False, methods=['get'])
191
+ def system(self, request):
192
+ """Get system health and performance statistics."""
193
+ # Mock system statistics - replace with real metrics
194
+ system_data = {
195
+ 'uptime': '99.9%',
196
+ 'active_users_24h': 45,
197
+ 'api_requests_24h': 1250,
198
+ 'avg_response_time_ms': 245.5,
199
+ 'error_rate_24h': 0.8,
200
+ 'database_connections': 12,
201
+ 'memory_usage': '68%',
202
+ 'cpu_usage': '23%',
203
+ 'disk_usage': '45%',
204
+ }
205
+
206
+ return Response(system_data)
@@ -0,0 +1,60 @@
1
+ """
2
+ Admin User ViewSet.
3
+
4
+ Simple ViewSet for user management in admin interface.
5
+ """
6
+
7
+ from rest_framework import viewsets
8
+ from rest_framework.permissions import IsAdminUser
9
+ from rest_framework.response import Response
10
+ from django.contrib.auth import get_user_model
11
+ from django_filters.rest_framework import DjangoFilterBackend
12
+ from rest_framework.filters import SearchFilter, OrderingFilter
13
+
14
+ from django_cfg.apps.payments.admin_interface.views.base import AdminReadOnlyViewSet
15
+ from django_cfg.apps.payments.admin_interface.serializers import AdminUserSerializer
16
+ from django_cfg.modules.django_logger import get_logger
17
+
18
+ logger = get_logger("admin_user_api")
19
+
20
+ User = get_user_model()
21
+
22
+
23
+ class AdminUserViewSet(AdminReadOnlyViewSet):
24
+ """
25
+ Admin ViewSet for user management.
26
+
27
+ Provides read-only access to users for admin interface.
28
+ """
29
+
30
+ queryset = User.objects.filter(is_active=True).order_by('username')
31
+ serializer_class = AdminUserSerializer
32
+ permission_classes = [IsAdminUser]
33
+
34
+ filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
35
+ filterset_fields = ['is_active', 'is_staff', 'is_superuser']
36
+ search_fields = ['username', 'email', 'first_name', 'last_name']
37
+ ordering_fields = ['username', 'email', 'date_joined']
38
+ ordering = ['username'] # Override base class ordering
39
+
40
+ def get_queryset(self):
41
+ """Optimized queryset for admin interface."""
42
+ # Don't slice here - let DRF handle pagination and ordering first
43
+ return super().get_queryset()
44
+
45
+ def list(self, request, *args, **kwargs):
46
+ """Override list to limit results for dropdown."""
47
+ # Get the filtered and ordered queryset from DRF
48
+ queryset = self.filter_queryset(self.get_queryset())
49
+
50
+ # Now apply limit after filtering/ordering
51
+ queryset = queryset[:100] # Limit to first 100 users
52
+
53
+ # Use DRF pagination if needed
54
+ page = self.paginate_queryset(queryset)
55
+ if page is not None:
56
+ serializer = self.get_serializer(page, many=True)
57
+ return self.get_paginated_response(serializer.data)
58
+
59
+ serializer = self.get_serializer(queryset, many=True)
60
+ return Response(serializer.data)