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
@@ -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}")
@@ -1,5 +1,7 @@
1
1
  """
2
- Middleware for universal payments.
2
+ Middleware for the Universal Payment System v2.0.
3
+
4
+ Enhanced middleware with service layer integration and smart caching.
3
5
  """
4
6
 
5
7
  from .api_access import APIAccessMiddleware