django-cfg 1.2.29__py3-none-any.whl → 1.3.1__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/api/health/views.py +4 -2
- django_cfg/apps/knowbase/config/settings.py +16 -15
- django_cfg/apps/payments/README.md +326 -0
- django_cfg/apps/payments/admin/__init__.py +20 -9
- django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
- django_cfg/apps/payments/admin/balance_admin.py +592 -297
- django_cfg/apps/payments/admin/currencies_admin.py +600 -108
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +470 -64
- django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
- django_cfg/apps/payments/admin_interface/__init__.py +18 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
- django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
- django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
- django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
- django_cfg/apps/payments/apps.py +34 -9
- django_cfg/apps/payments/config/__init__.py +28 -51
- django_cfg/apps/payments/config/constance/__init__.py +22 -0
- django_cfg/apps/payments/config/constance/config_service.py +123 -0
- django_cfg/apps/payments/config/constance/fields.py +69 -0
- django_cfg/apps/payments/config/constance/settings.py +160 -0
- django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
- django_cfg/apps/payments/config/helpers.py +130 -0
- django_cfg/apps/payments/management/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
- django_cfg/apps/payments/middleware/__init__.py +3 -1
- django_cfg/apps/payments/middleware/api_access.py +329 -222
- django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
- django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +16 -20
- django_cfg/apps/payments/models/api_keys.py +121 -43
- django_cfg/apps/payments/models/balance.py +150 -115
- django_cfg/apps/payments/models/base.py +68 -15
- django_cfg/apps/payments/models/currencies.py +207 -67
- django_cfg/apps/payments/models/managers/__init__.py +44 -0
- django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
- django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
- django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
- django_cfg/apps/payments/models/payments.py +235 -284
- django_cfg/apps/payments/models/subscriptions.py +257 -177
- django_cfg/apps/payments/models/tariffs.py +147 -40
- django_cfg/apps/payments/services/__init__.py +209 -56
- django_cfg/apps/payments/services/cache/__init__.py +6 -6
- django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
- django_cfg/apps/payments/services/core/__init__.py +10 -6
- django_cfg/apps/payments/services/core/balance_service.py +435 -360
- django_cfg/apps/payments/services/core/base.py +166 -0
- django_cfg/apps/payments/services/core/currency_service.py +478 -0
- django_cfg/apps/payments/services/core/payment_service.py +344 -468
- django_cfg/apps/payments/services/core/subscription_service.py +425 -484
- django_cfg/apps/payments/services/core/webhook_service.py +410 -0
- django_cfg/apps/payments/services/integrations/__init__.py +29 -0
- django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
- django_cfg/apps/payments/services/providers/__init__.py +9 -14
- django_cfg/apps/payments/services/providers/base.py +232 -71
- django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
- django_cfg/apps/payments/services/providers/registry.py +429 -80
- django_cfg/apps/payments/services/types/__init__.py +78 -0
- django_cfg/apps/payments/services/types/data.py +177 -0
- django_cfg/apps/payments/services/types/requests.py +150 -0
- django_cfg/apps/payments/services/types/responses.py +156 -0
- django_cfg/apps/payments/services/types/webhooks.py +232 -0
- django_cfg/apps/payments/signals/__init__.py +33 -8
- django_cfg/apps/payments/signals/api_key_signals.py +211 -130
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +129 -98
- django_cfg/apps/payments/signals/subscription_signals.py +195 -143
- django_cfg/apps/payments/static/payments/css/components.css +380 -0
- django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
- django_cfg/apps/payments/static/payments/js/components.js +545 -0
- django_cfg/apps/payments/static/payments/js/utils.js +412 -0
- django_cfg/apps/payments/templatetags/__init__.py +1 -1
- django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
- django_cfg/apps/payments/urls.py +46 -47
- django_cfg/apps/payments/urls_admin.py +49 -0
- django_cfg/apps/payments/views/api/__init__.py +101 -0
- django_cfg/apps/payments/views/api/api_keys.py +387 -0
- django_cfg/apps/payments/views/api/balances.py +381 -0
- django_cfg/apps/payments/views/api/base.py +298 -0
- django_cfg/apps/payments/views/api/currencies.py +402 -0
- django_cfg/apps/payments/views/api/payments.py +415 -0
- django_cfg/apps/payments/views/api/subscriptions.py +475 -0
- django_cfg/apps/payments/views/api/webhooks.py +476 -0
- django_cfg/apps/payments/views/serializers/__init__.py +99 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
- django_cfg/apps/payments/views/serializers/balances.py +300 -0
- django_cfg/apps/payments/views/serializers/currencies.py +335 -0
- django_cfg/apps/payments/views/serializers/payments.py +387 -0
- django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
- django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
- django_cfg/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +75 -4
- django_cfg/core/generation.py +25 -4
- django_cfg/core/integration/README.md +363 -0
- django_cfg/core/integration/__init__.py +47 -0
- django_cfg/core/integration/commands_collector.py +239 -0
- django_cfg/core/integration/display/__init__.py +15 -0
- django_cfg/core/integration/display/base.py +157 -0
- django_cfg/core/integration/display/ngrok.py +164 -0
- django_cfg/core/integration/display/startup.py +815 -0
- django_cfg/core/integration/url_integration.py +123 -0
- django_cfg/core/integration/version_checker.py +160 -0
- django_cfg/management/commands/auto_generate.py +4 -0
- django_cfg/management/commands/check_settings.py +6 -0
- django_cfg/management/commands/clear_constance.py +5 -2
- django_cfg/management/commands/create_token.py +6 -0
- django_cfg/management/commands/list_urls.py +6 -0
- django_cfg/management/commands/migrate_all.py +6 -0
- django_cfg/management/commands/migrator.py +3 -0
- django_cfg/management/commands/rundramatiq.py +6 -0
- django_cfg/management/commands/runserver_ngrok.py +51 -29
- django_cfg/management/commands/script.py +6 -0
- django_cfg/management/commands/show_config.py +12 -2
- django_cfg/management/commands/show_urls.py +4 -0
- django_cfg/management/commands/superuser.py +6 -0
- django_cfg/management/commands/task_clear.py +4 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -0
- django_cfg/management/commands/test_telegram.py +6 -0
- django_cfg/management/commands/test_twilio.py +6 -0
- django_cfg/management/commands/tree.py +6 -0
- django_cfg/management/commands/validate_config.py +155 -149
- django_cfg/models/constance.py +31 -11
- django_cfg/models/payments.py +175 -498
- django_cfg/modules/django_currency/__init__.py +16 -11
- django_cfg/modules/django_currency/clients/__init__.py +4 -4
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
- django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
- django_cfg/modules/django_currency/core/__init__.py +1 -7
- django_cfg/modules/django_currency/core/converter.py +18 -23
- django_cfg/modules/django_currency/core/models.py +122 -11
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +190 -309
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +65 -12
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/admin/components/action_grid.html +9 -9
- django_cfg/templates/admin/components/metric_card.html +5 -5
- django_cfg/templates/admin/components/status_badge.html +2 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
- django_cfg/templates/admin/snippets/components/system_health.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
- django_cfg/apps/payments/__init__.py +0 -8
- django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
- django_cfg/apps/payments/config/module.py +0 -70
- django_cfg/apps/payments/config/providers.py +0 -105
- django_cfg/apps/payments/config/settings.py +0 -96
- django_cfg/apps/payments/config/utils.py +0 -52
- django_cfg/apps/payments/decorators.py +0 -291
- django_cfg/apps/payments/management/commands/README.md +0 -178
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
- django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
- django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
- django_cfg/apps/payments/managers/__init__.py +0 -22
- django_cfg/apps/payments/managers/api_key_manager.py +0 -35
- django_cfg/apps/payments/managers/balance_manager.py +0 -361
- django_cfg/apps/payments/managers/currency_manager.py +0 -83
- django_cfg/apps/payments/managers/payment_manager.py +0 -44
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -56
- django_cfg/apps/payments/serializers/api_keys.py +0 -51
- django_cfg/apps/payments/serializers/balance.py +0 -59
- django_cfg/apps/payments/serializers/currencies.py +0 -55
- django_cfg/apps/payments/serializers/payments.py +0 -62
- django_cfg/apps/payments/serializers/subscriptions.py +0 -71
- django_cfg/apps/payments/serializers/tariffs.py +0 -56
- django_cfg/apps/payments/services/billing/__init__.py +0 -8
- django_cfg/apps/payments/services/cache/base.py +0 -30
- django_cfg/apps/payments/services/core/fallback_service.py +0 -432
- django_cfg/apps/payments/services/internal_types.py +0 -297
- django_cfg/apps/payments/services/middleware/__init__.py +0 -8
- django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
- django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
- django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -637
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- django_cfg/apps/payments/static/payments/css/payments.css +0 -340
- django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
- django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
- django_cfg/apps/payments/static/payments/js/theme.js +0 -86
- django_cfg/apps/payments/tasks/__init__.py +0 -12
- django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
- django_cfg/apps/payments/templates/payments/base.html +0 -182
- django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/urls_templates.py +0 -52
- django_cfg/apps/payments/utils/__init__.py +0 -45
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -245
- django_cfg/apps/payments/utils/middleware_utils.py +0 -228
- django_cfg/apps/payments/utils/validation_utils.py +0 -94
- django_cfg/apps/payments/views/__init__.py +0 -62
- django_cfg/apps/payments/views/api_key_views.py +0 -164
- django_cfg/apps/payments/views/balance_views.py +0 -75
- django_cfg/apps/payments/views/currency_views.py +0 -111
- django_cfg/apps/payments/views/payment_views.py +0 -149
- django_cfg/apps/payments/views/subscription_views.py +0 -135
- django_cfg/apps/payments/views/tariff_views.py +0 -131
- django_cfg/apps/payments/views/templates/__init__.py +0 -25
- django_cfg/apps/payments/views/templates/ajax.py +0 -312
- django_cfg/apps/payments/views/templates/base.py +0 -204
- django_cfg/apps/payments/views/templates/dashboard.py +0 -60
- django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
- django_cfg/apps/payments/views/templates/payment_management.py +0 -164
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -240
- django_cfg/apps/payments/views/templates/utils.py +0 -181
- django_cfg/apps/payments/views/webhook_views.py +0 -266
- django_cfg/apps/payments/viewsets.py +0 -65
- django_cfg/core/integration.py +0 -160
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- django_cfg/template_archive/.gitignore +0 -1
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/urls.py +0 -33
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,259 +1,366 @@
|
|
1
1
|
"""
|
2
|
-
Custom admin filters for
|
2
|
+
Custom admin filters for the Universal Payment System v2.0.
|
3
|
+
|
4
|
+
Provides advanced filtering capabilities for the Unfold admin interface.
|
3
5
|
"""
|
4
6
|
|
5
7
|
from django.contrib import admin
|
6
|
-
from django.utils import timezone
|
7
8
|
from django.utils.translation import gettext_lazy as _
|
8
|
-
from django.
|
9
|
+
from django.utils import timezone
|
10
|
+
from django.db.models import Q, Count
|
9
11
|
from datetime import timedelta
|
12
|
+
from typing import List, Tuple, Optional
|
10
13
|
|
14
|
+
from ..models import Currency, UniversalPayment, UserBalance, Subscription, APIKey
|
11
15
|
|
12
|
-
class PaymentStatusFilter(admin.SimpleListFilter):
|
13
|
-
"""Filter payments by status."""
|
14
|
-
title = _('Payment Status')
|
15
|
-
parameter_name = 'payment_status'
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
class CurrencyTypeFilter(admin.SimpleListFilter):
|
18
|
+
"""Filter currencies by type with enhanced options."""
|
19
|
+
|
20
|
+
title = _('Currency Type')
|
21
|
+
parameter_name = 'currency_type'
|
22
|
+
|
23
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
24
|
+
return [
|
25
|
+
('fiat', _('💰 Fiat Currencies')),
|
26
|
+
('crypto', _('₿ Cryptocurrencies')),
|
27
|
+
('active_fiat', _('💰 Active Fiat')),
|
28
|
+
('active_crypto', _('₿ Active Crypto')),
|
29
|
+
]
|
30
|
+
|
31
|
+
def queryset(self, request, queryset):
|
32
|
+
if self.value() == 'fiat':
|
33
|
+
return queryset.filter(currency_type=Currency.CurrencyType.FIAT)
|
34
|
+
elif self.value() == 'crypto':
|
35
|
+
return queryset.filter(currency_type=Currency.CurrencyType.CRYPTO)
|
36
|
+
elif self.value() == 'active_fiat':
|
37
|
+
return queryset.filter(
|
38
|
+
currency_type=Currency.CurrencyType.FIAT,
|
39
|
+
is_active=True
|
40
|
+
)
|
41
|
+
elif self.value() == 'active_crypto':
|
42
|
+
return queryset.filter(
|
43
|
+
currency_type=Currency.CurrencyType.CRYPTO,
|
44
|
+
is_active=True
|
45
|
+
)
|
46
|
+
return queryset
|
47
|
+
|
26
48
|
|
49
|
+
class CurrencyRateStatusFilter(admin.SimpleListFilter):
|
50
|
+
"""Filter currencies by USD rate status."""
|
51
|
+
|
52
|
+
title = _('Rate Status')
|
53
|
+
parameter_name = 'rate_status'
|
54
|
+
|
55
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
56
|
+
return [
|
57
|
+
('has_rate', _('🟢 Has USD Rate')),
|
58
|
+
('no_rate', _('❌ No USD Rate')),
|
59
|
+
('fresh_rate', _('🟢 Fresh Rate (24h)')),
|
60
|
+
('stale_rate', _('🟠 Stale Rate (>24h)')),
|
61
|
+
('old_rate', _('🔴 Old Rate (>7d)')),
|
62
|
+
]
|
63
|
+
|
27
64
|
def queryset(self, request, queryset):
|
28
|
-
|
29
|
-
|
65
|
+
now = timezone.now()
|
66
|
+
|
67
|
+
if self.value() == 'has_rate':
|
68
|
+
return queryset.filter(provider_configs__usd_rate__isnull=False).distinct()
|
69
|
+
elif self.value() == 'no_rate':
|
70
|
+
return queryset.exclude(provider_configs__usd_rate__isnull=False).distinct()
|
71
|
+
elif self.value() == 'fresh_rate':
|
72
|
+
fresh_threshold = now - timedelta(hours=24)
|
73
|
+
return queryset.filter(
|
74
|
+
provider_configs__rate_updated_at__gte=fresh_threshold
|
75
|
+
).distinct()
|
76
|
+
elif self.value() == 'stale_rate':
|
77
|
+
stale_start = now - timedelta(days=7)
|
78
|
+
stale_end = now - timedelta(hours=24)
|
79
|
+
return queryset.filter(
|
80
|
+
provider_configs__rate_updated_at__range=(stale_start, stale_end)
|
81
|
+
).distinct()
|
82
|
+
elif self.value() == 'old_rate':
|
83
|
+
old_threshold = now - timedelta(days=7)
|
84
|
+
return queryset.filter(
|
85
|
+
provider_configs__rate_updated_at__lt=old_threshold
|
86
|
+
).distinct()
|
30
87
|
return queryset
|
31
88
|
|
32
89
|
|
33
|
-
class
|
34
|
-
"""
|
35
|
-
|
36
|
-
|
90
|
+
class PaymentStatusFilter(admin.SimpleListFilter):
|
91
|
+
"""Enhanced payment status filter with groupings."""
|
92
|
+
|
93
|
+
title = _('Payment Status')
|
94
|
+
parameter_name = 'status'
|
95
|
+
|
96
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
97
|
+
return [
|
98
|
+
('pending', _('⏳ Pending')),
|
99
|
+
('waiting', _('⏰ Waiting for Payment')),
|
100
|
+
('confirming', _('🔄 Confirming')),
|
101
|
+
('completed', _('✅ Completed')),
|
102
|
+
('failed', _('❌ Failed')),
|
103
|
+
('cancelled', _('🚫 Cancelled')),
|
104
|
+
('expired', _('⌛ Expired')),
|
105
|
+
('refunded', _('↩️ Refunded')),
|
106
|
+
('active', _('🟢 Active (Pending/Waiting/Confirming)')),
|
107
|
+
('finished', _('🏁 Finished (Completed/Failed/Cancelled)')),
|
108
|
+
]
|
109
|
+
|
110
|
+
def queryset(self, request, queryset):
|
111
|
+
if self.value() == 'active':
|
112
|
+
return queryset.filter(
|
113
|
+
status__in=[
|
114
|
+
UniversalPayment.PaymentStatus.PENDING,
|
115
|
+
UniversalPayment.PaymentStatus.WAITING_FOR_PAYMENT,
|
116
|
+
UniversalPayment.PaymentStatus.CONFIRMING
|
117
|
+
]
|
118
|
+
)
|
119
|
+
elif self.value() == 'finished':
|
120
|
+
return queryset.filter(
|
121
|
+
status__in=[
|
122
|
+
UniversalPayment.PaymentStatus.COMPLETED,
|
123
|
+
UniversalPayment.PaymentStatus.FAILED,
|
124
|
+
UniversalPayment.PaymentStatus.CANCELLED
|
125
|
+
]
|
126
|
+
)
|
127
|
+
elif self.value():
|
128
|
+
return queryset.filter(status=self.value())
|
129
|
+
return queryset
|
37
130
|
|
38
|
-
def lookups(self, request, model_admin):
|
39
|
-
return (
|
40
|
-
('small', _('< $10')),
|
41
|
-
('medium', _('$10 - $100')),
|
42
|
-
('large', _('$100 - $1000')),
|
43
|
-
('enterprise', _('> $1000')),
|
44
|
-
)
|
45
131
|
|
132
|
+
class PaymentAmountFilter(admin.SimpleListFilter):
|
133
|
+
"""Filter payments by USD amount ranges."""
|
134
|
+
|
135
|
+
title = _('Amount (USD)')
|
136
|
+
parameter_name = 'amount_range'
|
137
|
+
|
138
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
139
|
+
return [
|
140
|
+
('micro', _('💵 Micro ($0.01 - $1)')),
|
141
|
+
('small', _('💵 Small ($1 - $10)')),
|
142
|
+
('medium', _('💰 Medium ($10 - $100)')),
|
143
|
+
('large', _('💰 Large ($100 - $1,000)')),
|
144
|
+
('huge', _('💎 Huge ($1,000+)')),
|
145
|
+
]
|
146
|
+
|
46
147
|
def queryset(self, request, queryset):
|
47
|
-
if self.value() == '
|
48
|
-
return queryset.filter(amount_usd__lt=
|
148
|
+
if self.value() == 'micro':
|
149
|
+
return queryset.filter(amount_usd__gte=0.01, amount_usd__lt=1)
|
150
|
+
elif self.value() == 'small':
|
151
|
+
return queryset.filter(amount_usd__gte=1, amount_usd__lt=10)
|
49
152
|
elif self.value() == 'medium':
|
50
153
|
return queryset.filter(amount_usd__gte=10, amount_usd__lt=100)
|
51
154
|
elif self.value() == 'large':
|
52
155
|
return queryset.filter(amount_usd__gte=100, amount_usd__lt=1000)
|
53
|
-
elif self.value() == '
|
156
|
+
elif self.value() == 'huge':
|
54
157
|
return queryset.filter(amount_usd__gte=1000)
|
55
158
|
return queryset
|
56
159
|
|
57
160
|
|
58
|
-
class
|
59
|
-
"""Filter
|
60
|
-
|
61
|
-
|
161
|
+
class UserEmailFilter(admin.SimpleListFilter):
|
162
|
+
"""Filter by user email domain."""
|
163
|
+
|
164
|
+
title = _('User Email Domain')
|
165
|
+
parameter_name = 'email_domain'
|
166
|
+
|
167
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
168
|
+
# Get top email domains from the database
|
169
|
+
from django.contrib.auth import get_user_model
|
170
|
+
User = get_user_model()
|
171
|
+
|
172
|
+
try:
|
173
|
+
# This is a simplified approach - in production you might want to cache this
|
174
|
+
domains = []
|
175
|
+
common_domains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com']
|
176
|
+
|
177
|
+
for domain in common_domains:
|
178
|
+
count = User.objects.filter(email__icontains=f'@{domain}').count()
|
179
|
+
if count > 0:
|
180
|
+
domains.append((domain, f'@{domain} ({count})'))
|
181
|
+
|
182
|
+
return domains
|
183
|
+
except Exception:
|
184
|
+
return [
|
185
|
+
('gmail.com', '@gmail.com'),
|
186
|
+
('yahoo.com', '@yahoo.com'),
|
187
|
+
('outlook.com', '@outlook.com'),
|
188
|
+
]
|
189
|
+
|
190
|
+
def queryset(self, request, queryset):
|
191
|
+
if self.value():
|
192
|
+
return queryset.filter(user__email__icontains=f'@{self.value()}')
|
193
|
+
return queryset
|
62
194
|
|
63
|
-
def lookups(self, request, model_admin):
|
64
|
-
return (
|
65
|
-
('active', _('Active')),
|
66
|
-
('inactive', _('Inactive')),
|
67
|
-
('cancelled', _('Cancelled')),
|
68
|
-
('expired', _('Expired')),
|
69
|
-
('trial', _('Trial')),
|
70
|
-
)
|
71
195
|
|
196
|
+
class RecentActivityFilter(admin.SimpleListFilter):
|
197
|
+
"""Filter by recent activity timeframes."""
|
198
|
+
|
199
|
+
title = _('Recent Activity')
|
200
|
+
parameter_name = 'recent_activity'
|
201
|
+
|
202
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
203
|
+
return [
|
204
|
+
('1h', _('🕐 Last Hour')),
|
205
|
+
('24h', _('📅 Last 24 Hours')),
|
206
|
+
('7d', _('📅 Last 7 Days')),
|
207
|
+
('30d', _('📅 Last 30 Days')),
|
208
|
+
('today', _('📅 Today')),
|
209
|
+
('yesterday', _('📅 Yesterday')),
|
210
|
+
]
|
211
|
+
|
72
212
|
def queryset(self, request, queryset):
|
73
|
-
|
74
|
-
|
213
|
+
now = timezone.now()
|
214
|
+
|
215
|
+
if self.value() == '1h':
|
216
|
+
threshold = now - timedelta(hours=1)
|
217
|
+
return queryset.filter(created_at__gte=threshold)
|
218
|
+
elif self.value() == '24h':
|
219
|
+
threshold = now - timedelta(hours=24)
|
220
|
+
return queryset.filter(created_at__gte=threshold)
|
221
|
+
elif self.value() == '7d':
|
222
|
+
threshold = now - timedelta(days=7)
|
223
|
+
return queryset.filter(created_at__gte=threshold)
|
224
|
+
elif self.value() == '30d':
|
225
|
+
threshold = now - timedelta(days=30)
|
226
|
+
return queryset.filter(created_at__gte=threshold)
|
227
|
+
elif self.value() == 'today':
|
228
|
+
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
229
|
+
return queryset.filter(created_at__gte=today_start)
|
230
|
+
elif self.value() == 'yesterday':
|
231
|
+
yesterday_start = now.replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=1)
|
232
|
+
yesterday_end = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
233
|
+
return queryset.filter(created_at__range=(yesterday_start, yesterday_end))
|
234
|
+
return queryset
|
235
|
+
|
236
|
+
|
237
|
+
class BalanceRangeFilter(admin.SimpleListFilter):
|
238
|
+
"""Filter user balances by amount ranges."""
|
239
|
+
|
240
|
+
title = _('Balance Range (USD)')
|
241
|
+
parameter_name = 'balance_range'
|
242
|
+
|
243
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
244
|
+
return [
|
245
|
+
('zero', _('💸 Zero Balance')),
|
246
|
+
('low', _('🪙 Low ($0.01 - $10)')),
|
247
|
+
('medium', _('💰 Medium ($10 - $100)')),
|
248
|
+
('high', _('💎 High ($100 - $1,000)')),
|
249
|
+
('whale', _('🐋 Whale ($1,000+)')),
|
250
|
+
('negative', _('⚠️ Negative Balance')),
|
251
|
+
]
|
252
|
+
|
253
|
+
def queryset(self, request, queryset):
|
254
|
+
if self.value() == 'zero':
|
255
|
+
return queryset.filter(balance_usd=0)
|
256
|
+
elif self.value() == 'low':
|
257
|
+
return queryset.filter(balance_usd__gt=0, balance_usd__lte=10)
|
258
|
+
elif self.value() == 'medium':
|
259
|
+
return queryset.filter(balance_usd__gt=10, balance_usd__lte=100)
|
260
|
+
elif self.value() == 'high':
|
261
|
+
return queryset.filter(balance_usd__gt=100, balance_usd__lte=1000)
|
262
|
+
elif self.value() == 'whale':
|
263
|
+
return queryset.filter(balance_usd__gt=1000)
|
264
|
+
elif self.value() == 'negative':
|
265
|
+
return queryset.filter(balance_usd__lt=0)
|
75
266
|
return queryset
|
76
267
|
|
77
268
|
|
78
269
|
class SubscriptionTierFilter(admin.SimpleListFilter):
|
79
270
|
"""Filter subscriptions by tier."""
|
271
|
+
|
80
272
|
title = _('Subscription Tier')
|
81
|
-
parameter_name = '
|
82
|
-
|
83
|
-
def lookups(self, request, model_admin):
|
84
|
-
return
|
85
|
-
('
|
86
|
-
('
|
87
|
-
('
|
88
|
-
|
89
|
-
|
273
|
+
parameter_name = 'tier'
|
274
|
+
|
275
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
276
|
+
return [
|
277
|
+
('free', _('🆓 Free Tier')),
|
278
|
+
('basic', _('🥉 Basic Tier')),
|
279
|
+
('premium', _('🥈 Premium Tier')),
|
280
|
+
('enterprise', _('🥇 Enterprise Tier')),
|
281
|
+
]
|
282
|
+
|
90
283
|
def queryset(self, request, queryset):
|
91
284
|
if self.value():
|
92
285
|
return queryset.filter(tier=self.value())
|
93
286
|
return queryset
|
94
287
|
|
95
288
|
|
96
|
-
class
|
97
|
-
"""Filter subscriptions by
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
('
|
105
|
-
('
|
106
|
-
('
|
107
|
-
|
108
|
-
|
289
|
+
class SubscriptionStatusFilter(admin.SimpleListFilter):
|
290
|
+
"""Filter subscriptions by status with enhanced groupings."""
|
291
|
+
|
292
|
+
title = _('Subscription Status')
|
293
|
+
parameter_name = 'subscription_status'
|
294
|
+
|
295
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
296
|
+
return [
|
297
|
+
('active', _('✅ Active')),
|
298
|
+
('expired', _('⌛ Expired')),
|
299
|
+
('cancelled', _('🚫 Cancelled')),
|
300
|
+
('suspended', _('⏸️ Suspended')),
|
301
|
+
('expiring_soon', _('⚠️ Expiring Soon (7 days)')),
|
302
|
+
('recently_expired', _('🔴 Recently Expired (7 days)')),
|
303
|
+
]
|
304
|
+
|
109
305
|
def queryset(self, request, queryset):
|
110
|
-
|
111
|
-
|
112
|
-
|
306
|
+
now = timezone.now()
|
307
|
+
|
308
|
+
if self.value() == 'expiring_soon':
|
309
|
+
threshold = now + timedelta(days=7)
|
113
310
|
return queryset.filter(
|
114
|
-
|
115
|
-
|
311
|
+
status=Subscription.SubscriptionStatus.ACTIVE,
|
312
|
+
expires_at__lte=threshold,
|
313
|
+
expires_at__gt=now
|
116
314
|
)
|
117
|
-
elif self.value() == '
|
315
|
+
elif self.value() == 'recently_expired':
|
316
|
+
threshold = now - timedelta(days=7)
|
118
317
|
return queryset.filter(
|
119
|
-
|
120
|
-
|
318
|
+
status=Subscription.SubscriptionStatus.EXPIRED,
|
319
|
+
expires_at__gte=threshold
|
121
320
|
)
|
122
|
-
elif self.value()
|
123
|
-
return queryset.filter(
|
321
|
+
elif self.value():
|
322
|
+
return queryset.filter(status=self.value())
|
124
323
|
return queryset
|
125
324
|
|
126
325
|
|
127
326
|
class APIKeyStatusFilter(admin.SimpleListFilter):
|
128
|
-
"""Filter API keys by status."""
|
327
|
+
"""Filter API keys by status and activity."""
|
328
|
+
|
129
329
|
title = _('API Key Status')
|
130
330
|
parameter_name = 'api_key_status'
|
131
|
-
|
132
|
-
def lookups(self, request, model_admin):
|
133
|
-
return
|
134
|
-
('active', _('Active')),
|
135
|
-
('inactive', _('Inactive')),
|
136
|
-
('expired', _('Expired')),
|
137
|
-
('
|
138
|
-
('
|
139
|
-
|
140
|
-
|
331
|
+
|
332
|
+
def lookups(self, request, model_admin) -> List[Tuple[str, str]]:
|
333
|
+
return [
|
334
|
+
('active', _('✅ Active')),
|
335
|
+
('inactive', _('❌ Inactive')),
|
336
|
+
('expired', _('⌛ Expired')),
|
337
|
+
('expiring_soon', _('⚠️ Expiring Soon (7 days)')),
|
338
|
+
('never_used', _('🆕 Never Used')),
|
339
|
+
('recently_used', _('🔥 Recently Used (24h)')),
|
340
|
+
('high_usage', _('📈 High Usage (>1000 requests)')),
|
341
|
+
]
|
342
|
+
|
141
343
|
def queryset(self, request, queryset):
|
142
344
|
now = timezone.now()
|
345
|
+
|
143
346
|
if self.value() == 'active':
|
144
347
|
return queryset.filter(is_active=True, expires_at__gt=now)
|
145
348
|
elif self.value() == 'inactive':
|
146
349
|
return queryset.filter(is_active=False)
|
147
350
|
elif self.value() == 'expired':
|
148
351
|
return queryset.filter(expires_at__lte=now)
|
149
|
-
elif self.value() == '
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
('fiat', _('Fiat Currency')),
|
164
|
-
('crypto', _('Cryptocurrency')),
|
165
|
-
)
|
166
|
-
|
167
|
-
def queryset(self, request, queryset):
|
168
|
-
if self.value():
|
169
|
-
return queryset.filter(currency_type=self.value())
|
170
|
-
return queryset
|
171
|
-
|
172
|
-
|
173
|
-
class TransactionTypeFilter(admin.SimpleListFilter):
|
174
|
-
"""Filter transactions by type."""
|
175
|
-
title = _('Transaction Type')
|
176
|
-
parameter_name = 'transaction_type'
|
177
|
-
|
178
|
-
def lookups(self, request, model_admin):
|
179
|
-
return (
|
180
|
-
('credit', _('Credit')),
|
181
|
-
('debit', _('Debit')),
|
182
|
-
('refund', _('Refund')),
|
183
|
-
('withdrawal', _('Withdrawal')),
|
184
|
-
)
|
185
|
-
|
186
|
-
def queryset(self, request, queryset):
|
187
|
-
if self.value():
|
188
|
-
return queryset.filter(transaction_type=self.value())
|
189
|
-
return queryset
|
190
|
-
|
191
|
-
|
192
|
-
class RecentActivityFilter(admin.SimpleListFilter):
|
193
|
-
"""Filter by recent activity."""
|
194
|
-
title = _('Recent Activity')
|
195
|
-
parameter_name = 'recent_activity'
|
196
|
-
|
197
|
-
def lookups(self, request, model_admin):
|
198
|
-
return (
|
199
|
-
('1hour', _('Last Hour')),
|
200
|
-
('24hours', _('Last 24 Hours')),
|
201
|
-
('7days', _('Last 7 Days')),
|
202
|
-
('30days', _('Last 30 Days')),
|
203
|
-
)
|
204
|
-
|
205
|
-
def queryset(self, request, queryset):
|
206
|
-
now = timezone.now()
|
207
|
-
if self.value() == '1hour':
|
208
|
-
return queryset.filter(created_at__gte=now - timedelta(hours=1))
|
209
|
-
elif self.value() == '24hours':
|
210
|
-
return queryset.filter(created_at__gte=now - timedelta(hours=24))
|
211
|
-
elif self.value() == '7days':
|
212
|
-
return queryset.filter(created_at__gte=now - timedelta(days=7))
|
213
|
-
elif self.value() == '30days':
|
214
|
-
return queryset.filter(created_at__gte=now - timedelta(days=30))
|
215
|
-
return queryset
|
216
|
-
|
217
|
-
|
218
|
-
class UserEmailFilter(admin.SimpleListFilter):
|
219
|
-
"""Filter by user email using text input."""
|
220
|
-
title = _('User Email')
|
221
|
-
parameter_name = 'user_email'
|
222
|
-
|
223
|
-
def lookups(self, request, model_admin):
|
224
|
-
"""Return empty lookups to show text input."""
|
225
|
-
return ()
|
226
|
-
|
227
|
-
def queryset(self, request, queryset):
|
228
|
-
"""Filter queryset based on user email input."""
|
229
|
-
if self.value():
|
230
|
-
return queryset.filter(user__email__icontains=self.value())
|
231
|
-
return queryset
|
232
|
-
|
233
|
-
|
234
|
-
class BalanceRangeFilter(admin.SimpleListFilter):
|
235
|
-
"""Filter user balances by amount ranges."""
|
236
|
-
title = _('Balance Amount')
|
237
|
-
parameter_name = 'balance_amount'
|
238
|
-
|
239
|
-
def lookups(self, request, model_admin):
|
240
|
-
return (
|
241
|
-
('zero', _('$0')),
|
242
|
-
('low', _('$0.01 - $10')),
|
243
|
-
('medium', _('$10 - $100')),
|
244
|
-
('high', _('$100 - $1000')),
|
245
|
-
('enterprise', _('> $1000')),
|
246
|
-
)
|
247
|
-
|
248
|
-
def queryset(self, request, queryset):
|
249
|
-
if self.value() == 'zero':
|
250
|
-
return queryset.filter(amount_usd=0)
|
251
|
-
elif self.value() == 'low':
|
252
|
-
return queryset.filter(amount_usd__gt=0, amount_usd__lte=10)
|
253
|
-
elif self.value() == 'medium':
|
254
|
-
return queryset.filter(amount_usd__gt=10, amount_usd__lte=100)
|
255
|
-
elif self.value() == 'high':
|
256
|
-
return queryset.filter(amount_usd__gt=100, amount_usd__lte=1000)
|
257
|
-
elif self.value() == 'enterprise':
|
258
|
-
return queryset.filter(amount_usd__gt=1000)
|
352
|
+
elif self.value() == 'expiring_soon':
|
353
|
+
threshold = now + timedelta(days=7)
|
354
|
+
return queryset.filter(
|
355
|
+
is_active=True,
|
356
|
+
expires_at__lte=threshold,
|
357
|
+
expires_at__gt=now
|
358
|
+
)
|
359
|
+
elif self.value() == 'never_used':
|
360
|
+
return queryset.filter(last_used_at__isnull=True)
|
361
|
+
elif self.value() == 'recently_used':
|
362
|
+
threshold = now - timedelta(hours=24)
|
363
|
+
return queryset.filter(last_used_at__gte=threshold)
|
364
|
+
elif self.value() == 'high_usage':
|
365
|
+
return queryset.filter(total_requests__gte=1000)
|
259
366
|
return queryset
|