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
@@ -0,0 +1,408 @@
|
|
1
|
+
"""
|
2
|
+
Provider management command for Universal Payment System v2.0.
|
3
|
+
|
4
|
+
Manages payment provider synchronization, health checks, and statistics.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.core.management.base import BaseCommand, CommandError
|
8
|
+
from django.db import transaction
|
9
|
+
from django.utils import timezone
|
10
|
+
from datetime import timedelta
|
11
|
+
from typing import List, Optional, Dict
|
12
|
+
import time
|
13
|
+
|
14
|
+
from django_cfg.modules.django_logger import get_logger
|
15
|
+
from django_cfg.apps.payments.models import Currency, Network, ProviderCurrency
|
16
|
+
from django_cfg.apps.payments.services.providers import get_provider_registry
|
17
|
+
|
18
|
+
logger = get_logger("manage_providers")
|
19
|
+
|
20
|
+
|
21
|
+
class Command(BaseCommand):
|
22
|
+
"""
|
23
|
+
Universal provider management command.
|
24
|
+
|
25
|
+
Features:
|
26
|
+
- Provider currency synchronization
|
27
|
+
- Health monitoring and statistics
|
28
|
+
- Selective provider updates
|
29
|
+
- Integration with ProviderRegistry
|
30
|
+
"""
|
31
|
+
|
32
|
+
help = 'Manage payment providers and their currencies'
|
33
|
+
|
34
|
+
def add_arguments(self, parser):
|
35
|
+
"""Add command arguments."""
|
36
|
+
|
37
|
+
# Main operation modes
|
38
|
+
parser.add_argument(
|
39
|
+
'--all',
|
40
|
+
action='store_true',
|
41
|
+
help='Sync all active providers'
|
42
|
+
)
|
43
|
+
|
44
|
+
parser.add_argument(
|
45
|
+
'--provider',
|
46
|
+
type=str,
|
47
|
+
help='Sync specific provider (e.g., nowpayments, cryptomus)'
|
48
|
+
)
|
49
|
+
|
50
|
+
parser.add_argument(
|
51
|
+
'--health-check',
|
52
|
+
action='store_true',
|
53
|
+
help='Perform health check on all providers'
|
54
|
+
)
|
55
|
+
|
56
|
+
parser.add_argument(
|
57
|
+
'--stats',
|
58
|
+
action='store_true',
|
59
|
+
help='Show provider statistics'
|
60
|
+
)
|
61
|
+
|
62
|
+
# Sync options
|
63
|
+
parser.add_argument(
|
64
|
+
'--with-rates',
|
65
|
+
action='store_true',
|
66
|
+
help='Update USD rates after syncing currencies'
|
67
|
+
)
|
68
|
+
|
69
|
+
parser.add_argument(
|
70
|
+
'--currencies',
|
71
|
+
type=str,
|
72
|
+
help='Comma-separated list of currency codes to sync (e.g., BTC,ETH,USDT)'
|
73
|
+
)
|
74
|
+
|
75
|
+
parser.add_argument(
|
76
|
+
'--force-refresh',
|
77
|
+
action='store_true',
|
78
|
+
help='Force refresh even if recently synced'
|
79
|
+
)
|
80
|
+
|
81
|
+
# Behavior options
|
82
|
+
parser.add_argument(
|
83
|
+
'--dry-run',
|
84
|
+
action='store_true',
|
85
|
+
help='Show what would be done without making changes'
|
86
|
+
)
|
87
|
+
|
88
|
+
parser.add_argument(
|
89
|
+
'--verbose',
|
90
|
+
action='store_true',
|
91
|
+
help='Show detailed output'
|
92
|
+
)
|
93
|
+
|
94
|
+
def handle(self, *args, **options):
|
95
|
+
"""Main command handler."""
|
96
|
+
|
97
|
+
start_time = time.time()
|
98
|
+
|
99
|
+
try:
|
100
|
+
self.stdout.write(
|
101
|
+
self.style.SUCCESS('🚀 Starting Provider Management')
|
102
|
+
)
|
103
|
+
|
104
|
+
# Get provider registry
|
105
|
+
registry = get_provider_registry()
|
106
|
+
|
107
|
+
# Determine operation mode
|
108
|
+
if options['health_check']:
|
109
|
+
self._perform_health_check(registry, options)
|
110
|
+
elif options['stats']:
|
111
|
+
self._show_provider_stats(registry, options)
|
112
|
+
elif options['provider']:
|
113
|
+
self._sync_single_provider(registry, options['provider'], options)
|
114
|
+
elif options['all']:
|
115
|
+
self._sync_all_providers(registry, options)
|
116
|
+
else:
|
117
|
+
# Default: show available providers and basic stats
|
118
|
+
self._show_available_providers(registry, options)
|
119
|
+
|
120
|
+
# Show summary
|
121
|
+
elapsed = time.time() - start_time
|
122
|
+
self.stdout.write(
|
123
|
+
self.style.SUCCESS(
|
124
|
+
f'✅ Provider management completed in {elapsed:.1f}s'
|
125
|
+
)
|
126
|
+
)
|
127
|
+
|
128
|
+
except Exception as e:
|
129
|
+
self.stdout.write(
|
130
|
+
self.style.ERROR(f'❌ Provider management failed: {e}')
|
131
|
+
)
|
132
|
+
logger.error(f"Provider management command failed: {e}")
|
133
|
+
raise CommandError(f"Command failed: {e}")
|
134
|
+
|
135
|
+
def _sync_all_providers(self, registry, options):
|
136
|
+
"""Sync all active providers."""
|
137
|
+
|
138
|
+
self.stdout.write("🔄 Syncing all active providers...")
|
139
|
+
|
140
|
+
available_providers = registry.get_available_providers()
|
141
|
+
|
142
|
+
if not available_providers:
|
143
|
+
self.stdout.write("⚠️ No active providers found")
|
144
|
+
return
|
145
|
+
|
146
|
+
total_synced = 0
|
147
|
+
total_errors = 0
|
148
|
+
|
149
|
+
for provider_name in available_providers:
|
150
|
+
try:
|
151
|
+
synced_count = self._sync_provider(registry, provider_name, options)
|
152
|
+
total_synced += synced_count
|
153
|
+
|
154
|
+
except Exception as e:
|
155
|
+
self.stdout.write(f"❌ Failed to sync {provider_name}: {e}")
|
156
|
+
total_errors += 1
|
157
|
+
logger.error(f"Provider sync failed for {provider_name}: {e}")
|
158
|
+
|
159
|
+
self.stdout.write(
|
160
|
+
f"🔄 All providers sync complete: {total_synced} currencies synced, {total_errors} errors"
|
161
|
+
)
|
162
|
+
|
163
|
+
# Update rates if requested
|
164
|
+
if options['with_rates'] and not options['dry_run']:
|
165
|
+
self._update_rates_after_sync(options)
|
166
|
+
|
167
|
+
def _sync_single_provider(self, registry, provider_name: str, options):
|
168
|
+
"""Sync a specific provider."""
|
169
|
+
|
170
|
+
self.stdout.write(f"🔄 Syncing provider: {provider_name}")
|
171
|
+
|
172
|
+
try:
|
173
|
+
synced_count = self._sync_provider(registry, provider_name, options)
|
174
|
+
self.stdout.write(f"✅ {provider_name} sync complete: {synced_count} currencies")
|
175
|
+
|
176
|
+
# Update rates if requested
|
177
|
+
if options['with_rates'] and not options['dry_run']:
|
178
|
+
self._update_rates_after_sync(options)
|
179
|
+
|
180
|
+
except Exception as e:
|
181
|
+
self.stdout.write(f"❌ Failed to sync {provider_name}: {e}")
|
182
|
+
logger.error(f"Provider sync failed for {provider_name}: {e}")
|
183
|
+
raise CommandError(f"Provider sync failed: {e}")
|
184
|
+
|
185
|
+
def _sync_provider(self, registry, provider_name: str, options) -> int:
|
186
|
+
"""Sync currencies for a specific provider."""
|
187
|
+
|
188
|
+
try:
|
189
|
+
provider = registry.get_provider(provider_name)
|
190
|
+
if not provider:
|
191
|
+
raise CommandError(f"Provider '{provider_name}' not available")
|
192
|
+
|
193
|
+
self.stdout.write(f" 📡 Fetching currencies from {provider_name}...")
|
194
|
+
|
195
|
+
if options['dry_run']:
|
196
|
+
self.stdout.write(f" [DRY RUN] Would sync {provider_name} currencies")
|
197
|
+
return 0
|
198
|
+
|
199
|
+
# Use provider's sync method
|
200
|
+
sync_result = provider.sync_currencies_to_db()
|
201
|
+
|
202
|
+
if sync_result.errors:
|
203
|
+
for error in sync_result.errors:
|
204
|
+
self.stdout.write(f" ⚠️ {error}")
|
205
|
+
|
206
|
+
synced_count = sync_result.currencies_created + sync_result.currencies_updated
|
207
|
+
|
208
|
+
self.stdout.write(
|
209
|
+
f" ✅ {provider_name}: {sync_result.currencies_created} created, "
|
210
|
+
f"{sync_result.currencies_updated} updated, "
|
211
|
+
f"{sync_result.provider_currencies_created} provider mappings created"
|
212
|
+
)
|
213
|
+
|
214
|
+
if options['verbose']:
|
215
|
+
self._show_provider_sync_details(sync_result)
|
216
|
+
|
217
|
+
return synced_count
|
218
|
+
|
219
|
+
except Exception as e:
|
220
|
+
logger.error(f"Provider sync failed for {provider_name}: {e}")
|
221
|
+
raise
|
222
|
+
|
223
|
+
def _perform_health_check(self, registry, options):
|
224
|
+
"""Perform health check on all providers."""
|
225
|
+
|
226
|
+
self.stdout.write("🏥 Performing provider health check...")
|
227
|
+
|
228
|
+
available_providers = registry.get_available_providers()
|
229
|
+
|
230
|
+
if not available_providers:
|
231
|
+
self.stdout.write("⚠️ No providers configured")
|
232
|
+
return
|
233
|
+
|
234
|
+
healthy_count = 0
|
235
|
+
unhealthy_count = 0
|
236
|
+
|
237
|
+
for provider_name in available_providers:
|
238
|
+
try:
|
239
|
+
provider = registry.get_provider(provider_name)
|
240
|
+
|
241
|
+
if not provider:
|
242
|
+
self.stdout.write(f" ❌ {provider_name}: Not available")
|
243
|
+
unhealthy_count += 1
|
244
|
+
continue
|
245
|
+
|
246
|
+
# Check if provider is enabled
|
247
|
+
if not provider.is_enabled():
|
248
|
+
self.stdout.write(f" ⏸️ {provider_name}: Disabled")
|
249
|
+
continue
|
250
|
+
|
251
|
+
# Perform basic health check (try to get currencies)
|
252
|
+
start_time = time.time()
|
253
|
+
|
254
|
+
try:
|
255
|
+
currencies = provider.get_parsed_currencies()
|
256
|
+
response_time = time.time() - start_time
|
257
|
+
|
258
|
+
if currencies and len(currencies.currencies) > 0:
|
259
|
+
self.stdout.write(
|
260
|
+
f" ✅ {provider_name}: Healthy "
|
261
|
+
f"({len(currencies.currencies)} currencies, {response_time:.2f}s)"
|
262
|
+
)
|
263
|
+
healthy_count += 1
|
264
|
+
else:
|
265
|
+
self.stdout.write(f" ⚠️ {provider_name}: No currencies returned")
|
266
|
+
unhealthy_count += 1
|
267
|
+
|
268
|
+
except Exception as e:
|
269
|
+
response_time = time.time() - start_time
|
270
|
+
self.stdout.write(
|
271
|
+
f" ❌ {provider_name}: Error ({response_time:.2f}s) - {str(e)[:50]}"
|
272
|
+
)
|
273
|
+
unhealthy_count += 1
|
274
|
+
|
275
|
+
except Exception as e:
|
276
|
+
self.stdout.write(f" ❌ {provider_name}: Critical error - {e}")
|
277
|
+
unhealthy_count += 1
|
278
|
+
|
279
|
+
self.stdout.write(
|
280
|
+
f"🏥 Health check complete: {healthy_count} healthy, {unhealthy_count} unhealthy"
|
281
|
+
)
|
282
|
+
|
283
|
+
def _show_provider_stats(self, registry, options):
|
284
|
+
"""Show detailed provider statistics."""
|
285
|
+
|
286
|
+
self.stdout.write("📊 Provider Statistics:")
|
287
|
+
|
288
|
+
# Registry stats
|
289
|
+
available_providers = registry.get_available_providers()
|
290
|
+
self.stdout.write(f"\n🔧 Registry Status:")
|
291
|
+
self.stdout.write(f" Available providers: {len(available_providers)}")
|
292
|
+
|
293
|
+
for provider_name in available_providers:
|
294
|
+
provider = registry.get_provider(provider_name)
|
295
|
+
status = "✅ Enabled" if provider and provider.is_enabled() else "❌ Disabled"
|
296
|
+
self.stdout.write(f" - {provider_name}: {status}")
|
297
|
+
|
298
|
+
# Database stats
|
299
|
+
self.stdout.write(f"\n💾 Database Statistics:")
|
300
|
+
|
301
|
+
total_provider_currencies = ProviderCurrency.objects.count()
|
302
|
+
enabled_provider_currencies = ProviderCurrency.objects.filter(is_enabled=True).count()
|
303
|
+
|
304
|
+
self.stdout.write(f" Total provider currencies: {total_provider_currencies}")
|
305
|
+
self.stdout.write(f" Enabled: {enabled_provider_currencies}")
|
306
|
+
|
307
|
+
# Stats by provider
|
308
|
+
from django.db.models import Count
|
309
|
+
|
310
|
+
provider_stats = ProviderCurrency.objects.values('provider_name').annotate(
|
311
|
+
total=Count('id'),
|
312
|
+
enabled=Count('id', filter=models.Q(is_enabled=True))
|
313
|
+
).order_by('-total')
|
314
|
+
|
315
|
+
if provider_stats:
|
316
|
+
self.stdout.write(f"\n📈 By Provider:")
|
317
|
+
for stat in provider_stats:
|
318
|
+
self.stdout.write(
|
319
|
+
f" - {stat['provider_name']}: {stat['total']} total, {stat['enabled']} enabled"
|
320
|
+
)
|
321
|
+
|
322
|
+
# Recent activity
|
323
|
+
recent_threshold = timezone.now() - timedelta(hours=24)
|
324
|
+
recent_updates = ProviderCurrency.objects.filter(
|
325
|
+
updated_at__gte=recent_threshold
|
326
|
+
).count()
|
327
|
+
|
328
|
+
self.stdout.write(f"\n🕐 Recent Activity (24h):")
|
329
|
+
self.stdout.write(f" Updated currencies: {recent_updates}")
|
330
|
+
|
331
|
+
# Rate coverage
|
332
|
+
currencies_with_rates = ProviderCurrency.objects.filter(
|
333
|
+
usd_rate__isnull=False
|
334
|
+
).count()
|
335
|
+
|
336
|
+
rate_coverage = (currencies_with_rates / total_provider_currencies * 100) if total_provider_currencies > 0 else 0
|
337
|
+
|
338
|
+
self.stdout.write(f"\n💱 Rate Coverage:")
|
339
|
+
self.stdout.write(f" Currencies with USD rates: {currencies_with_rates} ({rate_coverage:.1f}%)")
|
340
|
+
|
341
|
+
def _show_available_providers(self, registry, options):
|
342
|
+
"""Show available providers and basic info."""
|
343
|
+
|
344
|
+
self.stdout.write("📋 Available Providers:")
|
345
|
+
|
346
|
+
available_providers = registry.get_available_providers()
|
347
|
+
|
348
|
+
if not available_providers:
|
349
|
+
self.stdout.write(" No providers configured")
|
350
|
+
return
|
351
|
+
|
352
|
+
for provider_name in available_providers:
|
353
|
+
try:
|
354
|
+
provider = registry.get_provider(provider_name)
|
355
|
+
|
356
|
+
if provider:
|
357
|
+
status = "✅ Enabled" if provider.is_enabled() else "❌ Disabled"
|
358
|
+
|
359
|
+
# Get currency count from database
|
360
|
+
currency_count = ProviderCurrency.objects.filter(
|
361
|
+
provider_name=provider_name
|
362
|
+
).count()
|
363
|
+
|
364
|
+
self.stdout.write(
|
365
|
+
f" - {provider_name}: {status} ({currency_count} currencies)"
|
366
|
+
)
|
367
|
+
else:
|
368
|
+
self.stdout.write(f" - {provider_name}: ❌ Not available")
|
369
|
+
|
370
|
+
except Exception as e:
|
371
|
+
self.stdout.write(f" - {provider_name}: ❌ Error - {e}")
|
372
|
+
|
373
|
+
self.stdout.write(f"\nUse --all to sync all providers or --provider <name> for specific provider")
|
374
|
+
|
375
|
+
def _show_provider_sync_details(self, sync_result):
|
376
|
+
"""Show detailed sync results."""
|
377
|
+
|
378
|
+
self.stdout.write(" 📋 Sync Details:")
|
379
|
+
self.stdout.write(f" Currencies created: {sync_result.currencies_created}")
|
380
|
+
self.stdout.write(f" Currencies updated: {sync_result.currencies_updated}")
|
381
|
+
self.stdout.write(f" Networks created: {sync_result.networks_created}")
|
382
|
+
self.stdout.write(f" Provider currencies created: {sync_result.provider_currencies_created}")
|
383
|
+
self.stdout.write(f" Provider currencies updated: {sync_result.provider_currencies_updated}")
|
384
|
+
|
385
|
+
if sync_result.errors:
|
386
|
+
self.stdout.write(f" Errors: {len(sync_result.errors)}")
|
387
|
+
|
388
|
+
def _update_rates_after_sync(self, options):
|
389
|
+
"""Update USD rates after provider sync."""
|
390
|
+
|
391
|
+
self.stdout.write("💱 Updating USD rates after sync...")
|
392
|
+
|
393
|
+
try:
|
394
|
+
from django.core.management import call_command
|
395
|
+
|
396
|
+
# Update rates for currencies that were just synced
|
397
|
+
if options['currencies']:
|
398
|
+
currency_codes = options['currencies'].split(',')
|
399
|
+
for code in currency_codes:
|
400
|
+
call_command('manage_currencies', '--currency', code.strip(), '--rates-only')
|
401
|
+
else:
|
402
|
+
call_command('manage_currencies', '--rates-only')
|
403
|
+
|
404
|
+
self.stdout.write("💱 Rate update completed")
|
405
|
+
|
406
|
+
except Exception as e:
|
407
|
+
self.stdout.write(f"⚠️ Rate update failed: {e}")
|
408
|
+
logger.warning(f"Rate update after sync failed: {e}")
|