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,336 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Management command to update currency data using django_currency database loader.
|
3
|
-
|
4
|
-
This command automatically populates and updates the payments Currency model
|
5
|
-
with fresh data from external APIs (CoinGecko, YFinance).
|
6
|
-
|
7
|
-
Usage:
|
8
|
-
python manage.py update_currencies
|
9
|
-
python manage.py update_currencies --max-crypto 100 --max-fiat 20
|
10
|
-
python manage.py update_currencies --dry-run
|
11
|
-
python manage.py update_currencies --force-update
|
12
|
-
"""
|
13
|
-
|
14
|
-
import logging
|
15
|
-
from typing import Dict, List, Optional
|
16
|
-
from datetime import datetime, timedelta
|
17
|
-
|
18
|
-
from django.core.management.base import BaseCommand, CommandError
|
19
|
-
from django.db import transaction
|
20
|
-
from django.utils import timezone
|
21
|
-
from django.conf import settings
|
22
|
-
|
23
|
-
from django_cfg.modules.django_currency.database.database_loader import (
|
24
|
-
CurrencyDatabaseLoader,
|
25
|
-
DatabaseLoaderConfig,
|
26
|
-
create_database_loader
|
27
|
-
)
|
28
|
-
from django_cfg.apps.payments.models.currencies import Currency
|
29
|
-
|
30
|
-
logger = logging.getLogger(__name__)
|
31
|
-
|
32
|
-
|
33
|
-
class Command(BaseCommand):
|
34
|
-
"""
|
35
|
-
Management command to update currency data.
|
36
|
-
|
37
|
-
Features:
|
38
|
-
- Automatic detection of new currencies
|
39
|
-
- Rate updates for existing currencies
|
40
|
-
- Dry-run mode for testing
|
41
|
-
- Configurable limits for API calls
|
42
|
-
- Progress reporting
|
43
|
-
- Error handling and rollback
|
44
|
-
"""
|
45
|
-
|
46
|
-
help = 'Update currency data from external APIs (CoinGecko, YFinance)'
|
47
|
-
|
48
|
-
def add_arguments(self, parser):
|
49
|
-
"""Add command line arguments."""
|
50
|
-
parser.add_argument(
|
51
|
-
'--max-crypto',
|
52
|
-
type=int,
|
53
|
-
default=500,
|
54
|
-
help='Maximum number of cryptocurrencies to load (default: 500)'
|
55
|
-
)
|
56
|
-
|
57
|
-
parser.add_argument(
|
58
|
-
'--max-fiat',
|
59
|
-
type=int,
|
60
|
-
default=50,
|
61
|
-
help='Maximum number of fiat currencies to load (default: 50)'
|
62
|
-
)
|
63
|
-
|
64
|
-
parser.add_argument(
|
65
|
-
'--min-market-cap',
|
66
|
-
type=float,
|
67
|
-
default=1000000,
|
68
|
-
help='Minimum market cap in USD for cryptocurrencies (default: 1M)'
|
69
|
-
)
|
70
|
-
|
71
|
-
parser.add_argument(
|
72
|
-
'--dry-run',
|
73
|
-
action='store_true',
|
74
|
-
help='Show what would be updated without making changes'
|
75
|
-
)
|
76
|
-
|
77
|
-
parser.add_argument(
|
78
|
-
'--force-update',
|
79
|
-
action='store_true',
|
80
|
-
help='Force update all currencies even if recently updated'
|
81
|
-
)
|
82
|
-
|
83
|
-
parser.add_argument(
|
84
|
-
'--exclude-stablecoins',
|
85
|
-
action='store_true',
|
86
|
-
help='Exclude stablecoins from cryptocurrency updates'
|
87
|
-
)
|
88
|
-
|
89
|
-
parser.add_argument(
|
90
|
-
'--update-threshold-hours',
|
91
|
-
type=int,
|
92
|
-
default=6,
|
93
|
-
help='Only update currencies older than N hours (default: 6)'
|
94
|
-
)
|
95
|
-
|
96
|
-
parser.add_argument(
|
97
|
-
'--verbose',
|
98
|
-
action='store_true',
|
99
|
-
help='Verbose output with detailed progress'
|
100
|
-
)
|
101
|
-
|
102
|
-
def handle(self, *args, **options):
|
103
|
-
"""Main command handler."""
|
104
|
-
|
105
|
-
# Configure logging
|
106
|
-
log_level = logging.INFO if options['verbose'] else logging.WARNING
|
107
|
-
logging.getLogger('django_cfg.modules.django_currency').setLevel(log_level)
|
108
|
-
|
109
|
-
self.stdout.write(
|
110
|
-
self.style.SUCCESS('🏦 Starting currency database update...')
|
111
|
-
)
|
112
|
-
|
113
|
-
try:
|
114
|
-
# Create database loader with options
|
115
|
-
config = DatabaseLoaderConfig(
|
116
|
-
max_cryptocurrencies=options['max_crypto'],
|
117
|
-
max_fiat_currencies=options['max_fiat'],
|
118
|
-
min_market_cap_usd=options['min_market_cap'],
|
119
|
-
exclude_stablecoins=options['exclude_stablecoins'],
|
120
|
-
coingecko_delay=1.5, # Be respectful to APIs
|
121
|
-
yfinance_delay=0.5
|
122
|
-
)
|
123
|
-
|
124
|
-
loader = CurrencyDatabaseLoader(config)
|
125
|
-
|
126
|
-
# Get statistics
|
127
|
-
stats = loader.get_statistics()
|
128
|
-
self.stdout.write(f"📊 Loader config: {stats['total_currencies']} currencies available")
|
129
|
-
self.stdout.write(f" • {stats['total_fiat_currencies']} fiat currencies")
|
130
|
-
self.stdout.write(f" • {stats['total_cryptocurrencies']} cryptocurrencies")
|
131
|
-
|
132
|
-
# Check existing currencies
|
133
|
-
existing_count = Currency.objects.count()
|
134
|
-
self.stdout.write(f"📋 Current database: {existing_count} currencies")
|
135
|
-
|
136
|
-
# Determine update strategy
|
137
|
-
if options['force_update']:
|
138
|
-
currencies_to_update = Currency.objects.all()
|
139
|
-
self.stdout.write("🔄 Force update mode: updating all currencies")
|
140
|
-
else:
|
141
|
-
threshold = timezone.now() - timedelta(hours=options['update_threshold_hours'])
|
142
|
-
currencies_to_update = Currency.objects.filter(
|
143
|
-
rate_updated_at__lt=threshold
|
144
|
-
) | Currency.objects.filter(rate_updated_at__isnull=True)
|
145
|
-
|
146
|
-
self.stdout.write(
|
147
|
-
f"⏰ Updating currencies older than {options['update_threshold_hours']} hours: "
|
148
|
-
f"{currencies_to_update.count()} currencies"
|
149
|
-
)
|
150
|
-
|
151
|
-
# Load fresh currency data
|
152
|
-
self.stdout.write("🌐 Fetching fresh currency data from APIs...")
|
153
|
-
fresh_currencies = loader.build_currency_database_data()
|
154
|
-
|
155
|
-
if options['dry_run']:
|
156
|
-
self._handle_dry_run(fresh_currencies, currencies_to_update)
|
157
|
-
else:
|
158
|
-
self._handle_update(fresh_currencies, currencies_to_update, options)
|
159
|
-
|
160
|
-
except KeyboardInterrupt:
|
161
|
-
self.stdout.write(
|
162
|
-
self.style.WARNING('\n⚠️ Update interrupted by user')
|
163
|
-
)
|
164
|
-
raise CommandError("Update cancelled by user")
|
165
|
-
|
166
|
-
except Exception as e:
|
167
|
-
logger.exception("Currency update failed")
|
168
|
-
self.stdout.write(
|
169
|
-
self.style.ERROR(f'❌ Currency update failed: {str(e)}')
|
170
|
-
)
|
171
|
-
raise CommandError(f"Update failed: {str(e)}")
|
172
|
-
|
173
|
-
def _handle_dry_run(self, fresh_currencies: List, currencies_to_update):
|
174
|
-
"""Handle dry-run mode - show what would be updated."""
|
175
|
-
self.stdout.write(
|
176
|
-
self.style.WARNING('🧪 DRY RUN MODE - No changes will be made')
|
177
|
-
)
|
178
|
-
|
179
|
-
# Analyze changes
|
180
|
-
existing_codes = set(Currency.objects.values_list('code', flat=True))
|
181
|
-
fresh_codes = {curr.code for curr in fresh_currencies}
|
182
|
-
|
183
|
-
new_currencies = fresh_codes - existing_codes
|
184
|
-
existing_currencies = fresh_codes & existing_codes
|
185
|
-
|
186
|
-
self.stdout.write(f"\n📈 Analysis:")
|
187
|
-
self.stdout.write(f" • Would add {len(new_currencies)} new currencies")
|
188
|
-
self.stdout.write(f" • Would update {len(existing_currencies)} existing currencies")
|
189
|
-
|
190
|
-
if new_currencies:
|
191
|
-
self.stdout.write(f"\n➕ New currencies to add:")
|
192
|
-
for code in sorted(list(new_currencies)[:10]): # Show first 10
|
193
|
-
currency = next(c for c in fresh_currencies if c.code == code)
|
194
|
-
self.stdout.write(f" • {code}: {currency.name} ({currency.currency_type})")
|
195
|
-
if len(new_currencies) > 10:
|
196
|
-
self.stdout.write(f" ... and {len(new_currencies) - 10} more")
|
197
|
-
|
198
|
-
if existing_currencies:
|
199
|
-
self.stdout.write(f"\n🔄 Existing currencies to update:")
|
200
|
-
for code in sorted(list(existing_currencies)[:10]): # Show first 10
|
201
|
-
currency = next(c for c in fresh_currencies if c.code == code)
|
202
|
-
try:
|
203
|
-
existing = Currency.objects.get(code=code)
|
204
|
-
rate_diff = abs(existing.usd_rate - currency.usd_rate)
|
205
|
-
if rate_diff > 0.01: # Significant change
|
206
|
-
change_pct = ((currency.usd_rate - existing.usd_rate) / existing.usd_rate) * 100
|
207
|
-
self.stdout.write(
|
208
|
-
f" • {code}: ${existing.usd_rate:.6f} → ${currency.usd_rate:.6f} "
|
209
|
-
f"({change_pct:+.2f}%)"
|
210
|
-
)
|
211
|
-
except Currency.DoesNotExist:
|
212
|
-
pass
|
213
|
-
|
214
|
-
self.stdout.write(
|
215
|
-
self.style.SUCCESS('\n✅ Dry run completed - use --force-update to apply changes')
|
216
|
-
)
|
217
|
-
|
218
|
-
def _handle_update(self, fresh_currencies: List, currencies_to_update, options: Dict):
|
219
|
-
"""Handle actual database update."""
|
220
|
-
|
221
|
-
updated_count = 0
|
222
|
-
created_count = 0
|
223
|
-
errors = []
|
224
|
-
|
225
|
-
# Create lookup for fresh data
|
226
|
-
fresh_data_map = {curr.code: curr for curr in fresh_currencies}
|
227
|
-
|
228
|
-
try:
|
229
|
-
with transaction.atomic():
|
230
|
-
self.stdout.write("💾 Updating database...")
|
231
|
-
|
232
|
-
# Process currencies
|
233
|
-
for i, fresh_currency in enumerate(fresh_currencies):
|
234
|
-
try:
|
235
|
-
currency, created = Currency.objects.update_or_create(
|
236
|
-
code=fresh_currency.code,
|
237
|
-
defaults={
|
238
|
-
'name': fresh_currency.name,
|
239
|
-
'symbol': fresh_currency.symbol,
|
240
|
-
'currency_type': fresh_currency.currency_type,
|
241
|
-
'decimal_places': fresh_currency.decimal_places,
|
242
|
-
'usd_rate': fresh_currency.usd_rate,
|
243
|
-
'min_payment_amount': fresh_currency.min_payment_amount,
|
244
|
-
'is_active': fresh_currency.is_active,
|
245
|
-
'rate_updated_at': timezone.now()
|
246
|
-
}
|
247
|
-
)
|
248
|
-
|
249
|
-
if created:
|
250
|
-
created_count += 1
|
251
|
-
if options['verbose']:
|
252
|
-
self.stdout.write(f" ➕ Created {fresh_currency.code}")
|
253
|
-
else:
|
254
|
-
updated_count += 1
|
255
|
-
if options['verbose']:
|
256
|
-
self.stdout.write(f" 🔄 Updated {fresh_currency.code}")
|
257
|
-
|
258
|
-
# Progress indicator
|
259
|
-
if (i + 1) % 50 == 0:
|
260
|
-
self.stdout.write(f" Progress: {i + 1}/{len(fresh_currencies)} currencies processed")
|
261
|
-
|
262
|
-
except Exception as e:
|
263
|
-
error_msg = f"Failed to update {fresh_currency.code}: {str(e)}"
|
264
|
-
errors.append(error_msg)
|
265
|
-
logger.error(error_msg)
|
266
|
-
|
267
|
-
# Continue with other currencies unless it's a critical error
|
268
|
-
if len(errors) > 10: # Too many errors
|
269
|
-
raise CommandError(f"Too many errors ({len(errors)}), aborting")
|
270
|
-
|
271
|
-
# Summary
|
272
|
-
total_processed = created_count + updated_count
|
273
|
-
self.stdout.write(f"\n📊 Update Summary:")
|
274
|
-
self.stdout.write(f" ✅ Successfully processed: {total_processed} currencies")
|
275
|
-
self.stdout.write(f" ➕ Created new: {created_count}")
|
276
|
-
self.stdout.write(f" 🔄 Updated existing: {updated_count}")
|
277
|
-
|
278
|
-
if errors:
|
279
|
-
self.stdout.write(f" ⚠️ Errors: {len(errors)}")
|
280
|
-
for error in errors[:5]: # Show first 5 errors
|
281
|
-
self.stdout.write(f" • {error}")
|
282
|
-
if len(errors) > 5:
|
283
|
-
self.stdout.write(f" ... and {len(errors) - 5} more errors")
|
284
|
-
|
285
|
-
# Deactivate currencies not in fresh data (optional)
|
286
|
-
fresh_codes = {curr.code for curr in fresh_currencies}
|
287
|
-
stale_currencies = Currency.objects.filter(is_active=True).exclude(
|
288
|
-
code__in=fresh_codes
|
289
|
-
)
|
290
|
-
|
291
|
-
if stale_currencies.exists():
|
292
|
-
self.stdout.write(f" 📋 Found {stale_currencies.count()} currencies not in fresh data")
|
293
|
-
# Optionally deactivate them
|
294
|
-
# stale_currencies.update(is_active=False)
|
295
|
-
|
296
|
-
self.stdout.write(
|
297
|
-
self.style.SUCCESS('✅ Currency database update completed successfully!')
|
298
|
-
)
|
299
|
-
|
300
|
-
except Exception as e:
|
301
|
-
self.stdout.write(
|
302
|
-
self.style.ERROR(f'❌ Update failed and rolled back: {str(e)}')
|
303
|
-
)
|
304
|
-
raise
|
305
|
-
|
306
|
-
def _show_statistics(self):
|
307
|
-
"""Show current currency statistics."""
|
308
|
-
total = Currency.objects.count()
|
309
|
-
fiat_count = Currency.objects.filter(currency_type=Currency.CurrencyType.FIAT).count()
|
310
|
-
crypto_count = Currency.objects.filter(currency_type=Currency.CurrencyType.CRYPTO).count()
|
311
|
-
active_count = Currency.objects.filter(is_active=True).count()
|
312
|
-
|
313
|
-
# Recent updates
|
314
|
-
recent_threshold = timezone.now() - timedelta(hours=24)
|
315
|
-
recent_updates = Currency.objects.filter(rate_updated_at__gte=recent_threshold).count()
|
316
|
-
|
317
|
-
self.stdout.write(f"\n📊 Current Database Statistics:")
|
318
|
-
self.stdout.write(f" • Total currencies: {total}")
|
319
|
-
self.stdout.write(f" • Fiat currencies: {fiat_count}")
|
320
|
-
self.stdout.write(f" • Cryptocurrencies: {crypto_count}")
|
321
|
-
self.stdout.write(f" • Active currencies: {active_count}")
|
322
|
-
self.stdout.write(f" • Updated in last 24h: {recent_updates}")
|
323
|
-
|
324
|
-
# Show some examples
|
325
|
-
recent_currencies = Currency.objects.filter(
|
326
|
-
rate_updated_at__gte=recent_threshold
|
327
|
-
).order_by('-rate_updated_at')[:5]
|
328
|
-
|
329
|
-
if recent_currencies:
|
330
|
-
self.stdout.write(f"\n🕒 Recently updated currencies:")
|
331
|
-
for currency in recent_currencies:
|
332
|
-
age = timezone.now() - currency.rate_updated_at
|
333
|
-
hours_ago = int(age.total_seconds() / 3600)
|
334
|
-
self.stdout.write(
|
335
|
-
f" • {currency.code}: ${currency.usd_rate:.6f} ({hours_ago}h ago)"
|
336
|
-
)
|
@@ -1,22 +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, CurrencyNetworkManager
|
11
|
-
|
12
|
-
__all__ = [
|
13
|
-
'UniversalPaymentManager',
|
14
|
-
'UserBalanceManager',
|
15
|
-
'SubscriptionManager',
|
16
|
-
'EndpointGroupManager',
|
17
|
-
'TariffManager',
|
18
|
-
'TariffEndpointGroupManager',
|
19
|
-
'APIKeyManager',
|
20
|
-
'CurrencyManager',
|
21
|
-
'CurrencyNetworkManager',
|
22
|
-
]
|
@@ -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
|