django-cfg 1.2.31__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 -10
- 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 +526 -222
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +465 -70
- 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 +303 -151
- django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
- 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 +342 -152
- django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +13 -18
- 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 +172 -148
- 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 -285
- 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 +346 -467
- django_cfg/apps/payments/services/core/subscription_service.py +425 -481
- 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 +234 -174
- django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
- django_cfg/apps/payments/services/providers/registry.py +367 -301
- 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 +210 -129
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +128 -103
- django_cfg/apps/payments/signals/subscription_signals.py +194 -142
- 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 +45 -48
- django_cfg/apps/payments/urls_admin.py +33 -42
- 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/config.py +1 -1
- django_cfg/core/config.py +40 -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 -492
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +64 -16
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
- 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 -146
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
- django_cfg/apps/payments/managers/__init__.py +0 -23
- 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 -306
- django_cfg/apps/payments/managers/payment_manager.py +0 -192
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -57
- 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 -63
- 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 -461
- 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 -76
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
- django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
- django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
- django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
- django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
- django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -635
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
- 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/admin/payments/currency/change_list.html +0 -50
- 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 -43
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
- django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
- django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
- django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
- django_cfg/apps/payments/templates/payments/stats.html +0 -261
- django_cfg/apps/payments/templates/payments/test.html +0 -213
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/utils/__init__.py +0 -43
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -239
- 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 -63
- 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 -122
- 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 -451
- django_cfg/apps/payments/views/templates/base.py +0 -212
- 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 -158
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -244
- 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 -66
- django_cfg/core/integration.py +0 -160
- 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.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,304 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Management command to show currency database statistics.
|
3
|
-
|
4
|
-
Usage:
|
5
|
-
python manage.py currency_stats
|
6
|
-
python manage.py currency_stats --detailed
|
7
|
-
python manage.py currency_stats --top 10
|
8
|
-
python manage.py currency_stats --check-rates
|
9
|
-
"""
|
10
|
-
|
11
|
-
from datetime import datetime, timedelta
|
12
|
-
from typing import List
|
13
|
-
|
14
|
-
from django.core.management.base import BaseCommand
|
15
|
-
from django.utils import timezone
|
16
|
-
from django.db.models import Q, Count, Avg
|
17
|
-
|
18
|
-
from django_cfg.apps.payments.models.currencies import Currency
|
19
|
-
|
20
|
-
|
21
|
-
class Command(BaseCommand):
|
22
|
-
"""
|
23
|
-
Display currency database statistics and health information.
|
24
|
-
"""
|
25
|
-
|
26
|
-
help = 'Show currency database statistics'
|
27
|
-
|
28
|
-
def add_arguments(self, parser):
|
29
|
-
"""Add command line arguments."""
|
30
|
-
parser.add_argument(
|
31
|
-
'--detailed',
|
32
|
-
action='store_true',
|
33
|
-
help='Show detailed statistics'
|
34
|
-
)
|
35
|
-
|
36
|
-
parser.add_argument(
|
37
|
-
'--top',
|
38
|
-
type=int,
|
39
|
-
default=5,
|
40
|
-
help='Number of top currencies to show (default: 5)'
|
41
|
-
)
|
42
|
-
|
43
|
-
parser.add_argument(
|
44
|
-
'--check-rates',
|
45
|
-
action='store_true',
|
46
|
-
help='Check for outdated exchange rates'
|
47
|
-
)
|
48
|
-
|
49
|
-
parser.add_argument(
|
50
|
-
'--export-csv',
|
51
|
-
type=str,
|
52
|
-
help='Export currency data to CSV file'
|
53
|
-
)
|
54
|
-
|
55
|
-
def handle(self, *args, **options):
|
56
|
-
"""Main command handler."""
|
57
|
-
|
58
|
-
self.stdout.write(
|
59
|
-
self.style.SUCCESS('📊 Currency Database Statistics')
|
60
|
-
)
|
61
|
-
self.stdout.write('=' * 50)
|
62
|
-
|
63
|
-
self._show_basic_stats(options)
|
64
|
-
|
65
|
-
if options['detailed']:
|
66
|
-
self._show_detailed_stats(options)
|
67
|
-
|
68
|
-
if options['check_rates']:
|
69
|
-
self._check_rate_freshness()
|
70
|
-
|
71
|
-
if options['export_csv']:
|
72
|
-
self._export_to_csv(options['export_csv'])
|
73
|
-
|
74
|
-
def _show_basic_stats(self, options):
|
75
|
-
"""Show basic currency statistics."""
|
76
|
-
|
77
|
-
# Basic counts
|
78
|
-
total = Currency.objects.count()
|
79
|
-
active = Currency.objects.count()
|
80
|
-
inactive = total - active
|
81
|
-
|
82
|
-
fiat_count = Currency.objects.filter(currency_type=Currency.CurrencyType.FIAT).count()
|
83
|
-
crypto_count = Currency.objects.filter(currency_type=Currency.CurrencyType.CRYPTO).count()
|
84
|
-
|
85
|
-
active_fiat = Currency.objects.filter(
|
86
|
-
currency_type=Currency.CurrencyType.FIAT,
|
87
|
-
).count()
|
88
|
-
active_crypto = Currency.objects.filter(
|
89
|
-
currency_type=Currency.CurrencyType.CRYPTO,
|
90
|
-
).count()
|
91
|
-
|
92
|
-
self.stdout.write(f"\n📈 Overview:")
|
93
|
-
self.stdout.write(f" Total currencies: {total}")
|
94
|
-
self.stdout.write(f" Active: {active} | Inactive: {inactive}")
|
95
|
-
self.stdout.write(f" Fiat: {fiat_count} ({active_fiat} active)")
|
96
|
-
self.stdout.write(f" Crypto: {crypto_count} ({active_crypto} active)")
|
97
|
-
|
98
|
-
# Rate update status
|
99
|
-
now = timezone.now()
|
100
|
-
|
101
|
-
# Recent (last 24h)
|
102
|
-
recent_threshold = now - timedelta(hours=24)
|
103
|
-
recent_updates = Currency.objects.filter(
|
104
|
-
rate_updated_at__gte=recent_threshold
|
105
|
-
).count()
|
106
|
-
|
107
|
-
# Outdated (older than 7 days)
|
108
|
-
outdated_threshold = now - timedelta(days=7)
|
109
|
-
outdated = Currency.objects.filter(
|
110
|
-
Q(rate_updated_at__lt=outdated_threshold) | Q(rate_updated_at__isnull=True)
|
111
|
-
).count()
|
112
|
-
|
113
|
-
self.stdout.write(f"\n🕒 Rate Updates:")
|
114
|
-
self.stdout.write(f" Updated in last 24h: {recent_updates}")
|
115
|
-
self.stdout.write(f" Outdated (>7 days): {outdated}")
|
116
|
-
|
117
|
-
# Top cryptocurrencies by USD value
|
118
|
-
top_crypto = Currency.objects.filter(
|
119
|
-
currency_type=Currency.CurrencyType.CRYPTO,
|
120
|
-
).order_by('-usd_rate')[:options['top']]
|
121
|
-
|
122
|
-
if top_crypto:
|
123
|
-
self.stdout.write(f"\n🚀 Top {options['top']} Cryptocurrencies by USD Rate:")
|
124
|
-
for i, currency in enumerate(top_crypto, 1):
|
125
|
-
age = self._get_rate_age(currency)
|
126
|
-
self.stdout.write(
|
127
|
-
f" {i}. {currency.code}: ${currency.usd_rate:,.6f} {age}"
|
128
|
-
)
|
129
|
-
|
130
|
-
# Major fiat currencies
|
131
|
-
major_fiat = Currency.objects.filter(
|
132
|
-
currency_type=Currency.CurrencyType.FIAT,
|
133
|
-
code__in=['USD', 'EUR', 'GBP', 'JPY', 'CNY'],
|
134
|
-
).order_by('code')
|
135
|
-
|
136
|
-
if major_fiat:
|
137
|
-
self.stdout.write(f"\n💵 Major Fiat Currencies:")
|
138
|
-
for currency in major_fiat:
|
139
|
-
age = self._get_rate_age(currency)
|
140
|
-
self.stdout.write(
|
141
|
-
f" • {currency.code}: {currency.name} = ${currency.usd_rate:.6f} {age}"
|
142
|
-
)
|
143
|
-
|
144
|
-
def _show_detailed_stats(self, options):
|
145
|
-
"""Show detailed statistics."""
|
146
|
-
|
147
|
-
self.stdout.write(f"\n📊 Detailed Statistics:")
|
148
|
-
|
149
|
-
# Decimal places distribution
|
150
|
-
decimal_stats = Currency.objects.values('decimal_places').annotate(
|
151
|
-
count=Count('decimal_places')
|
152
|
-
).order_by('decimal_places')
|
153
|
-
|
154
|
-
self.stdout.write(f"\n🔢 Decimal Places Distribution:")
|
155
|
-
for stat in decimal_stats:
|
156
|
-
self.stdout.write(f" {stat['decimal_places']} places: {stat['count']} currencies")
|
157
|
-
|
158
|
-
# Average rates by type
|
159
|
-
crypto_avg = Currency.objects.filter(
|
160
|
-
currency_type=Currency.CurrencyType.CRYPTO,
|
161
|
-
).aggregate(avg_rate=Avg('usd_rate'))['avg_rate']
|
162
|
-
|
163
|
-
fiat_avg = Currency.objects.filter(
|
164
|
-
currency_type=Currency.CurrencyType.FIAT,
|
165
|
-
).aggregate(avg_rate=Avg('usd_rate'))['avg_rate']
|
166
|
-
|
167
|
-
self.stdout.write(f"\n📊 Average USD Rates:")
|
168
|
-
if crypto_avg:
|
169
|
-
self.stdout.write(f" Cryptocurrencies: ${crypto_avg:.6f}")
|
170
|
-
if fiat_avg:
|
171
|
-
self.stdout.write(f" Fiat currencies: ${fiat_avg:.6f}")
|
172
|
-
|
173
|
-
# Min payment amounts
|
174
|
-
# Note: min_payment_amount field was removed - now handled at provider level
|
175
|
-
self.stdout.write(f"\n💰 Payment amounts now managed at provider level (ProviderCurrency)")
|
176
|
-
|
177
|
-
# Rate freshness distribution
|
178
|
-
now = timezone.now()
|
179
|
-
thresholds = [
|
180
|
-
('Last hour', timedelta(hours=1)),
|
181
|
-
('Last 24 hours', timedelta(hours=24)),
|
182
|
-
('Last week', timedelta(days=7)),
|
183
|
-
('Last month', timedelta(days=30)),
|
184
|
-
]
|
185
|
-
|
186
|
-
self.stdout.write(f"\n⏰ Rate Update Distribution:")
|
187
|
-
previous_count = 0
|
188
|
-
for label, delta in thresholds:
|
189
|
-
threshold = now - delta
|
190
|
-
count = Currency.objects.filter(rate_updated_at__gte=threshold).count()
|
191
|
-
new_in_period = count - previous_count
|
192
|
-
self.stdout.write(f" {label}: {new_in_period} new updates ({count} total)")
|
193
|
-
previous_count = count
|
194
|
-
|
195
|
-
# Never updated
|
196
|
-
never_updated = Currency.objects.filter(rate_updated_at__isnull=True).count()
|
197
|
-
if never_updated > 0:
|
198
|
-
self.stdout.write(f" Never updated: {never_updated} currencies")
|
199
|
-
|
200
|
-
def _check_rate_freshness(self):
|
201
|
-
"""Check for outdated exchange rates."""
|
202
|
-
|
203
|
-
self.stdout.write(f"\n🔍 Rate Freshness Check:")
|
204
|
-
|
205
|
-
now = timezone.now()
|
206
|
-
|
207
|
-
# Very outdated (>30 days)
|
208
|
-
very_old_threshold = now - timedelta(days=30)
|
209
|
-
very_old = Currency.objects.filter(
|
210
|
-
Q(rate_updated_at__lt=very_old_threshold) | Q(rate_updated_at__isnull=True),
|
211
|
-
)
|
212
|
-
|
213
|
-
if very_old.exists():
|
214
|
-
self.stdout.write(
|
215
|
-
self.style.ERROR(f" ❌ {very_old.count()} currencies with very old rates (>30 days)")
|
216
|
-
)
|
217
|
-
for currency in very_old[:5]:
|
218
|
-
age = self._get_rate_age(currency)
|
219
|
-
self.stdout.write(f" • {currency.code}: {age}")
|
220
|
-
if very_old.count() > 5:
|
221
|
-
self.stdout.write(f" ... and {very_old.count() - 5} more")
|
222
|
-
|
223
|
-
# Moderately outdated (7-30 days)
|
224
|
-
old_threshold = now - timedelta(days=7)
|
225
|
-
old_currencies = Currency.objects.filter(
|
226
|
-
rate_updated_at__lt=old_threshold,
|
227
|
-
rate_updated_at__gte=very_old_threshold,
|
228
|
-
)
|
229
|
-
|
230
|
-
if old_currencies.exists():
|
231
|
-
self.stdout.write(
|
232
|
-
self.style.WARNING(f" ⚠️ {old_currencies.count()} currencies with old rates (7-30 days)")
|
233
|
-
)
|
234
|
-
|
235
|
-
# Fresh rates (last 24h)
|
236
|
-
fresh_threshold = now - timedelta(hours=24)
|
237
|
-
fresh = Currency.objects.filter(
|
238
|
-
rate_updated_at__gte=fresh_threshold,
|
239
|
-
).count()
|
240
|
-
|
241
|
-
if fresh > 0:
|
242
|
-
self.stdout.write(
|
243
|
-
self.style.SUCCESS(f" ✅ {fresh} currencies with fresh rates (<24h)")
|
244
|
-
)
|
245
|
-
|
246
|
-
# Recommendations
|
247
|
-
total_active = Currency.objects.count()
|
248
|
-
if very_old.count() > 0:
|
249
|
-
self.stdout.write(f"\n💡 Recommendations:")
|
250
|
-
self.stdout.write(f" • Run: python manage.py update_currencies --force-update")
|
251
|
-
self.stdout.write(f" • Consider deactivating currencies with very old rates")
|
252
|
-
|
253
|
-
def _get_rate_age(self, currency) -> str:
|
254
|
-
"""Get human-readable age of currency rate."""
|
255
|
-
if not currency.rate_updated_at:
|
256
|
-
return "(never updated)"
|
257
|
-
|
258
|
-
age = timezone.now() - currency.rate_updated_at
|
259
|
-
|
260
|
-
if age.days > 30:
|
261
|
-
return f"({age.days} days ago)"
|
262
|
-
elif age.days > 0:
|
263
|
-
return f"({age.days}d ago)"
|
264
|
-
elif age.seconds > 3600:
|
265
|
-
hours = age.seconds // 3600
|
266
|
-
return f"({hours}h ago)"
|
267
|
-
else:
|
268
|
-
minutes = age.seconds // 60
|
269
|
-
return f"({minutes}m ago)"
|
270
|
-
|
271
|
-
def _export_to_csv(self, filename: str):
|
272
|
-
"""Export currency data to CSV file."""
|
273
|
-
import csv
|
274
|
-
|
275
|
-
self.stdout.write(f"\n📁 Exporting to {filename}...")
|
276
|
-
|
277
|
-
try:
|
278
|
-
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
|
279
|
-
writer = csv.writer(csvfile)
|
280
|
-
|
281
|
-
# Header
|
282
|
-
writer.writerow([
|
283
|
-
'code', 'name', 'currency_type', 'usd_rate', 'rate_updated_at'
|
284
|
-
])
|
285
|
-
|
286
|
-
# Data
|
287
|
-
currencies = Currency.objects.all().order_by('code')
|
288
|
-
for currency in currencies:
|
289
|
-
writer.writerow([
|
290
|
-
currency.code,
|
291
|
-
currency.name,
|
292
|
-
currency.currency_type,
|
293
|
-
currency.usd_rate,
|
294
|
-
currency.rate_updated_at.isoformat() if currency.rate_updated_at else None
|
295
|
-
])
|
296
|
-
|
297
|
-
self.stdout.write(
|
298
|
-
self.style.SUCCESS(f" ✅ Exported {currencies.count()} currencies to {filename}")
|
299
|
-
)
|
300
|
-
|
301
|
-
except Exception as e:
|
302
|
-
self.stdout.write(
|
303
|
-
self.style.ERROR(f" ❌ Export failed: {str(e)}")
|
304
|
-
)
|
@@ -1,23 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Django model managers for universal payments.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from .payment_manager import UniversalPaymentManager
|
6
|
-
from .balance_manager import UserBalanceManager
|
7
|
-
from .subscription_manager import SubscriptionManager, EndpointGroupManager
|
8
|
-
from .tariff_manager import TariffManager, TariffEndpointGroupManager
|
9
|
-
from .api_key_manager import APIKeyManager
|
10
|
-
from .currency_manager import CurrencyManager, NetworkManager, ProviderCurrencyManager
|
11
|
-
|
12
|
-
__all__ = [
|
13
|
-
'UniversalPaymentManager',
|
14
|
-
'UserBalanceManager',
|
15
|
-
'SubscriptionManager',
|
16
|
-
'EndpointGroupManager',
|
17
|
-
'TariffManager',
|
18
|
-
'TariffEndpointGroupManager',
|
19
|
-
'APIKeyManager',
|
20
|
-
'CurrencyManager',
|
21
|
-
'NetworkManager',
|
22
|
-
'ProviderCurrencyManager',
|
23
|
-
]
|
@@ -1,35 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
API key managers.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from django.db import models
|
6
|
-
from django.utils import timezone
|
7
|
-
|
8
|
-
|
9
|
-
class APIKeyManager(models.Manager):
|
10
|
-
"""Manager for APIKey model."""
|
11
|
-
|
12
|
-
def get_active_keys(self, user=None):
|
13
|
-
"""Get active API keys."""
|
14
|
-
queryset = self.filter(is_active=True)
|
15
|
-
if user:
|
16
|
-
queryset = queryset.filter(user=user)
|
17
|
-
return queryset
|
18
|
-
|
19
|
-
def get_expired_keys(self):
|
20
|
-
"""Get expired API keys."""
|
21
|
-
return self.filter(
|
22
|
-
expires_at__lte=timezone.now()
|
23
|
-
)
|
24
|
-
|
25
|
-
def get_valid_keys(self, user=None):
|
26
|
-
"""Get valid (active and not expired) API keys."""
|
27
|
-
now = timezone.now()
|
28
|
-
queryset = self.filter(
|
29
|
-
is_active=True
|
30
|
-
).filter(
|
31
|
-
models.Q(expires_at__isnull=True) | models.Q(expires_at__gt=now)
|
32
|
-
)
|
33
|
-
if user:
|
34
|
-
queryset = queryset.filter(user=user)
|
35
|
-
return queryset
|