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.
Files changed (258) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -9
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +600 -108
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +470 -64
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
  39. django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
  40. django_cfg/apps/payments/middleware/__init__.py +3 -1
  41. django_cfg/apps/payments/middleware/api_access.py +329 -222
  42. django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
  43. django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +16 -20
  46. django_cfg/apps/payments/models/api_keys.py +121 -43
  47. django_cfg/apps/payments/models/balance.py +150 -115
  48. django_cfg/apps/payments/models/base.py +68 -15
  49. django_cfg/apps/payments/models/currencies.py +207 -67
  50. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  51. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  52. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  53. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  54. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  55. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  56. django_cfg/apps/payments/models/payments.py +235 -284
  57. django_cfg/apps/payments/models/subscriptions.py +257 -177
  58. django_cfg/apps/payments/models/tariffs.py +147 -40
  59. django_cfg/apps/payments/services/__init__.py +209 -56
  60. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  61. django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
  62. django_cfg/apps/payments/services/core/__init__.py +10 -6
  63. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  64. django_cfg/apps/payments/services/core/base.py +166 -0
  65. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  66. django_cfg/apps/payments/services/core/payment_service.py +344 -468
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -484
  68. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  69. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  70. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  71. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  72. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  73. django_cfg/apps/payments/services/providers/base.py +232 -71
  74. django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
  75. django_cfg/apps/payments/services/providers/registry.py +429 -80
  76. django_cfg/apps/payments/services/types/__init__.py +78 -0
  77. django_cfg/apps/payments/services/types/data.py +177 -0
  78. django_cfg/apps/payments/services/types/requests.py +150 -0
  79. django_cfg/apps/payments/services/types/responses.py +156 -0
  80. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  81. django_cfg/apps/payments/signals/__init__.py +33 -8
  82. django_cfg/apps/payments/signals/api_key_signals.py +211 -130
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +129 -98
  85. django_cfg/apps/payments/signals/subscription_signals.py +195 -143
  86. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  87. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  88. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  89. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  90. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  91. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  92. django_cfg/apps/payments/urls.py +46 -47
  93. django_cfg/apps/payments/urls_admin.py +49 -0
  94. django_cfg/apps/payments/views/api/__init__.py +101 -0
  95. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  96. django_cfg/apps/payments/views/api/balances.py +381 -0
  97. django_cfg/apps/payments/views/api/base.py +298 -0
  98. django_cfg/apps/payments/views/api/currencies.py +402 -0
  99. django_cfg/apps/payments/views/api/payments.py +415 -0
  100. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  101. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  102. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  103. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  104. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  105. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  106. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  107. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  108. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  109. django_cfg/apps/tasks/urls.py +0 -2
  110. django_cfg/apps/tasks/urls_admin.py +14 -0
  111. django_cfg/apps/urls.py +4 -4
  112. django_cfg/config.py +1 -1
  113. django_cfg/core/config.py +75 -4
  114. django_cfg/core/generation.py +25 -4
  115. django_cfg/core/integration/README.md +363 -0
  116. django_cfg/core/integration/__init__.py +47 -0
  117. django_cfg/core/integration/commands_collector.py +239 -0
  118. django_cfg/core/integration/display/__init__.py +15 -0
  119. django_cfg/core/integration/display/base.py +157 -0
  120. django_cfg/core/integration/display/ngrok.py +164 -0
  121. django_cfg/core/integration/display/startup.py +815 -0
  122. django_cfg/core/integration/url_integration.py +123 -0
  123. django_cfg/core/integration/version_checker.py +160 -0
  124. django_cfg/management/commands/auto_generate.py +4 -0
  125. django_cfg/management/commands/check_settings.py +6 -0
  126. django_cfg/management/commands/clear_constance.py +5 -2
  127. django_cfg/management/commands/create_token.py +6 -0
  128. django_cfg/management/commands/list_urls.py +6 -0
  129. django_cfg/management/commands/migrate_all.py +6 -0
  130. django_cfg/management/commands/migrator.py +3 -0
  131. django_cfg/management/commands/rundramatiq.py +6 -0
  132. django_cfg/management/commands/runserver_ngrok.py +51 -29
  133. django_cfg/management/commands/script.py +6 -0
  134. django_cfg/management/commands/show_config.py +12 -2
  135. django_cfg/management/commands/show_urls.py +4 -0
  136. django_cfg/management/commands/superuser.py +6 -0
  137. django_cfg/management/commands/task_clear.py +4 -1
  138. django_cfg/management/commands/task_status.py +3 -1
  139. django_cfg/management/commands/test_email.py +3 -0
  140. django_cfg/management/commands/test_telegram.py +6 -0
  141. django_cfg/management/commands/test_twilio.py +6 -0
  142. django_cfg/management/commands/tree.py +6 -0
  143. django_cfg/management/commands/validate_config.py +155 -149
  144. django_cfg/models/constance.py +31 -11
  145. django_cfg/models/payments.py +175 -498
  146. django_cfg/modules/django_currency/__init__.py +16 -11
  147. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  148. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  149. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  150. django_cfg/modules/django_currency/core/__init__.py +1 -7
  151. django_cfg/modules/django_currency/core/converter.py +18 -23
  152. django_cfg/modules/django_currency/core/models.py +122 -11
  153. django_cfg/modules/django_currency/database/__init__.py +4 -4
  154. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  155. django_cfg/modules/django_logger.py +160 -146
  156. django_cfg/modules/django_unfold/dashboard.py +65 -12
  157. django_cfg/registry/core.py +1 -0
  158. django_cfg/template_archive/django_sample.zip +0 -0
  159. django_cfg/templates/admin/components/action_grid.html +9 -9
  160. django_cfg/templates/admin/components/metric_card.html +5 -5
  161. django_cfg/templates/admin/components/status_badge.html +2 -2
  162. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  163. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  164. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  165. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  166. django_cfg/utils/smart_defaults.py +222 -571
  167. django_cfg/utils/toolkit.py +51 -11
  168. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
  169. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
  170. django_cfg/apps/payments/__init__.py +0 -8
  171. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  172. django_cfg/apps/payments/config/module.py +0 -70
  173. django_cfg/apps/payments/config/providers.py +0 -105
  174. django_cfg/apps/payments/config/settings.py +0 -96
  175. django_cfg/apps/payments/config/utils.py +0 -52
  176. django_cfg/apps/payments/decorators.py +0 -291
  177. django_cfg/apps/payments/management/commands/README.md +0 -178
  178. django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
  179. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  180. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  181. django_cfg/apps/payments/managers/__init__.py +0 -22
  182. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  183. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  184. django_cfg/apps/payments/managers/currency_manager.py +0 -83
  185. django_cfg/apps/payments/managers/payment_manager.py +0 -44
  186. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  187. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  188. django_cfg/apps/payments/models/events.py +0 -73
  189. django_cfg/apps/payments/serializers/__init__.py +0 -56
  190. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  191. django_cfg/apps/payments/serializers/balance.py +0 -59
  192. django_cfg/apps/payments/serializers/currencies.py +0 -55
  193. django_cfg/apps/payments/serializers/payments.py +0 -62
  194. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  195. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  196. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  197. django_cfg/apps/payments/services/cache/base.py +0 -30
  198. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  199. django_cfg/apps/payments/services/internal_types.py +0 -297
  200. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  201. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  202. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
  203. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  204. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  205. django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
  206. django_cfg/apps/payments/services/security/__init__.py +0 -34
  207. django_cfg/apps/payments/services/security/error_handler.py +0 -637
  208. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  209. django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
  210. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  211. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  212. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  213. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  214. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  215. django_cfg/apps/payments/tasks/__init__.py +0 -12
  216. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  217. django_cfg/apps/payments/templates/payments/base.html +0 -182
  218. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  219. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  220. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
  221. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  222. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
  223. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
  224. django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
  225. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  226. django_cfg/apps/payments/urls_templates.py +0 -52
  227. django_cfg/apps/payments/utils/__init__.py +0 -45
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -245
  230. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  231. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  232. django_cfg/apps/payments/views/__init__.py +0 -62
  233. django_cfg/apps/payments/views/api_key_views.py +0 -164
  234. django_cfg/apps/payments/views/balance_views.py +0 -75
  235. django_cfg/apps/payments/views/currency_views.py +0 -111
  236. django_cfg/apps/payments/views/payment_views.py +0 -149
  237. django_cfg/apps/payments/views/subscription_views.py +0 -135
  238. django_cfg/apps/payments/views/tariff_views.py +0 -131
  239. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  240. django_cfg/apps/payments/views/templates/ajax.py +0 -312
  241. django_cfg/apps/payments/views/templates/base.py +0 -204
  242. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  243. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  244. django_cfg/apps/payments/views/templates/payment_management.py +0 -164
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -240
  247. django_cfg/apps/payments/views/templates/utils.py +0 -181
  248. django_cfg/apps/payments/views/webhook_views.py +0 -266
  249. django_cfg/apps/payments/viewsets.py +0 -65
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  252. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  253. django_cfg/template_archive/.gitignore +0 -1
  254. django_cfg/template_archive/__init__.py +0 -0
  255. django_cfg/urls.py +0 -33
  256. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  257. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  258. {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