django-cfg 1.4.21__py3-none-any.whl → 1.4.24__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 +4 -4
- django_cfg/apps/accounts/__init__.py +1 -1
- django_cfg/apps/accounts/__models.py +30 -29
- django_cfg/apps/accounts/admin/__init__.py +14 -3
- django_cfg/apps/accounts/admin/activity_admin.py +19 -19
- django_cfg/apps/accounts/admin/filters.py +7 -6
- django_cfg/apps/accounts/admin/group_admin.py +10 -16
- django_cfg/apps/accounts/admin/inlines.py +21 -19
- django_cfg/apps/accounts/admin/otp_admin.py +12 -12
- django_cfg/apps/accounts/admin/registration_admin.py +24 -24
- django_cfg/apps/accounts/admin/resources.py +49 -48
- django_cfg/apps/accounts/admin/twilio_admin.py +37 -37
- django_cfg/apps/accounts/admin/user_admin.py +45 -41
- django_cfg/apps/accounts/management/commands/otp_test.py +11 -11
- django_cfg/apps/accounts/managers/__init__.py +1 -1
- django_cfg/apps/accounts/managers/user_manager.py +6 -7
- django_cfg/apps/accounts/migrations/0001_initial.py +3 -2
- django_cfg/apps/accounts/models/__init__.py +15 -16
- django_cfg/apps/accounts/models/activity.py +5 -5
- django_cfg/apps/accounts/models/integrations.py +15 -15
- django_cfg/apps/accounts/models/registration.py +3 -2
- django_cfg/apps/accounts/models/user.py +3 -2
- django_cfg/apps/accounts/serializers/__init__.py +10 -3
- django_cfg/apps/accounts/serializers/otp.py +5 -4
- django_cfg/apps/accounts/serializers/profile.py +4 -5
- django_cfg/apps/accounts/serializers/webhook.py +13 -13
- django_cfg/apps/accounts/services/activity_service.py +13 -12
- django_cfg/apps/accounts/services/otp_service.py +16 -14
- django_cfg/apps/accounts/signals.py +18 -16
- django_cfg/apps/accounts/urls.py +10 -5
- django_cfg/apps/accounts/utils/notifications.py +22 -22
- django_cfg/apps/accounts/views/__init__.py +1 -1
- django_cfg/apps/accounts/views/otp.py +15 -14
- django_cfg/apps/accounts/views/profile.py +14 -10
- django_cfg/apps/accounts/views/webhook.py +46 -48
- django_cfg/apps/agents/__init__.py +3 -3
- django_cfg/apps/agents/admin/__init__.py +1 -1
- django_cfg/apps/agents/admin/execution_admin.py +68 -72
- django_cfg/apps/agents/admin/registry_admin.py +50 -54
- django_cfg/apps/agents/admin/toolsets_admin.py +63 -67
- django_cfg/apps/agents/apps.py +3 -3
- django_cfg/apps/agents/core/__init__.py +4 -4
- django_cfg/apps/agents/core/dependencies.py +24 -23
- django_cfg/apps/agents/core/django_agent.py +41 -39
- django_cfg/apps/agents/core/exceptions.py +12 -12
- django_cfg/apps/agents/core/models.py +16 -15
- django_cfg/apps/agents/core/orchestrator.py +58 -58
- django_cfg/apps/agents/examples/simple_example.py +24 -19
- django_cfg/apps/agents/integration/__init__.py +1 -1
- django_cfg/apps/agents/integration/middleware.py +12 -11
- django_cfg/apps/agents/integration/registry.py +52 -49
- django_cfg/apps/agents/integration/signals.py +5 -5
- django_cfg/apps/agents/management/commands/create_agent.py +39 -38
- django_cfg/apps/agents/management/commands/orchestrator_status.py +40 -39
- django_cfg/apps/agents/managers/__init__.py +8 -3
- django_cfg/apps/agents/managers/execution.py +42 -41
- django_cfg/apps/agents/managers/registry.py +40 -39
- django_cfg/apps/agents/managers/toolsets.py +87 -86
- django_cfg/apps/agents/migrations/0001_initial.py +2 -1
- django_cfg/apps/agents/models/__init__.py +2 -2
- django_cfg/apps/agents/models/execution.py +43 -42
- django_cfg/apps/agents/models/registry.py +46 -45
- django_cfg/apps/agents/models/toolsets.py +63 -62
- django_cfg/apps/agents/patterns/__init__.py +5 -5
- django_cfg/apps/agents/patterns/content_agents.py +17 -16
- django_cfg/apps/agents/toolsets/__init__.py +3 -3
- django_cfg/apps/agents/toolsets/cache_toolset.py +56 -55
- django_cfg/apps/agents/toolsets/django_toolset.py +43 -42
- django_cfg/apps/agents/toolsets/file_toolset.py +64 -63
- django_cfg/apps/agents/toolsets/orm_toolset.py +75 -74
- django_cfg/apps/agents/urls.py +3 -2
- django_cfg/apps/api/commands/urls.py +1 -0
- django_cfg/apps/api/commands/views.py +23 -26
- django_cfg/apps/api/endpoints/checker.py +5 -4
- django_cfg/apps/api/endpoints/drf_views.py +2 -2
- django_cfg/apps/api/endpoints/tests.py +6 -5
- django_cfg/apps/api/endpoints/urls.py +2 -1
- django_cfg/apps/api/endpoints/views.py +1 -0
- django_cfg/apps/api/health/drf_views.py +6 -6
- django_cfg/apps/api/health/urls.py +2 -1
- django_cfg/apps/api/health/views.py +41 -41
- django_cfg/{modules/django_ipc_client → apps/ipc}/__init__.py +6 -6
- django_cfg/apps/ipc/apps.py +28 -0
- django_cfg/apps/ipc/serializers/__init__.py +19 -0
- django_cfg/apps/ipc/serializers/serializers.py +229 -0
- django_cfg/apps/ipc/services/__init__.py +7 -0
- django_cfg/apps/ipc/services/client/__init__.py +23 -0
- django_cfg/{modules/django_ipc_client → apps/ipc/services/client}/client.py +7 -6
- django_cfg/{modules/django_ipc_client → apps/ipc/services/client}/exceptions.py +1 -1
- django_cfg/{modules/django_ipc_client/dashboard → apps/ipc/services}/monitor.py +36 -7
- django_cfg/{modules/django_ipc_client/dashboard/static/django_ipc_dashboard/js/dashboard.js → apps/ipc/static/django_cfg_ipc/js/dashboard.mjs} +131 -63
- django_cfg/{modules/django_ipc_client/dashboard/templates/django_ipc_dashboard → apps/ipc/templates/django_cfg_ipc}/base.html +5 -10
- django_cfg/apps/ipc/templates/django_cfg_ipc/dashboard.html +202 -0
- django_cfg/apps/ipc/urls.py +21 -0
- django_cfg/apps/ipc/urls_admin.py +20 -0
- django_cfg/apps/ipc/views/__init__.py +8 -0
- django_cfg/apps/ipc/views/dashboard.py +15 -0
- django_cfg/apps/ipc/views/viewsets.py +245 -0
- django_cfg/apps/knowbase/admin/__init__.py +2 -2
- django_cfg/apps/knowbase/admin/actions/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/actions/visibility_actions.py +2 -1
- django_cfg/apps/knowbase/admin/archive_admin.py +81 -84
- django_cfg/apps/knowbase/admin/chat_admin.py +70 -72
- django_cfg/apps/knowbase/admin/document_admin.py +10 -11
- django_cfg/apps/knowbase/admin/external_data_admin.py +69 -71
- django_cfg/apps/knowbase/admin/helpers/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/helpers/configs.py +2 -2
- django_cfg/apps/knowbase/admin/helpers/statistics.py +1 -1
- django_cfg/apps/knowbase/apps.py +13 -13
- django_cfg/apps/knowbase/config/__init__.py +7 -6
- django_cfg/apps/knowbase/config/constance_fields.py +14 -12
- django_cfg/apps/knowbase/config/constance_settings.py +32 -31
- django_cfg/apps/knowbase/config/settings.py +28 -28
- django_cfg/apps/knowbase/examples/external_data_usage.py +35 -32
- django_cfg/apps/knowbase/management/commands/knowbase_stats.py +33 -32
- django_cfg/apps/knowbase/management/commands/setup_knowbase.py +11 -13
- django_cfg/apps/knowbase/managers/__init__.py +2 -2
- django_cfg/apps/knowbase/managers/archive.py +86 -85
- django_cfg/apps/knowbase/managers/base.py +5 -5
- django_cfg/apps/knowbase/managers/chat.py +29 -28
- django_cfg/apps/knowbase/managers/document.py +39 -39
- django_cfg/apps/knowbase/managers/external_data.py +74 -73
- django_cfg/apps/knowbase/migrations/0001_initial.py +2 -1
- django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +2 -1
- django_cfg/apps/knowbase/mixins/__init__.py +4 -4
- django_cfg/apps/knowbase/mixins/config/__init__.py +1 -1
- django_cfg/apps/knowbase/mixins/config/meta_config.py +1 -1
- django_cfg/apps/knowbase/mixins/config.py +19 -18
- django_cfg/apps/knowbase/mixins/creator.py +7 -7
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +29 -28
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +6 -5
- django_cfg/apps/knowbase/mixins/generators/__init__.py +1 -1
- django_cfg/apps/knowbase/mixins/generators/content_generator.py +2 -2
- django_cfg/apps/knowbase/mixins/service.py +47 -45
- django_cfg/apps/knowbase/models/__init__.py +7 -7
- django_cfg/apps/knowbase/models/archive.py +72 -72
- django_cfg/apps/knowbase/models/base.py +12 -13
- django_cfg/apps/knowbase/models/chat.py +20 -19
- django_cfg/apps/knowbase/models/document.py +37 -35
- django_cfg/apps/knowbase/models/external_data.py +41 -42
- django_cfg/apps/knowbase/serializers/__init__.py +7 -7
- django_cfg/apps/knowbase/serializers/archive_serializers.py +50 -42
- django_cfg/apps/knowbase/serializers/chat_serializers.py +16 -15
- django_cfg/apps/knowbase/serializers/document_serializers.py +13 -12
- django_cfg/apps/knowbase/serializers/external_data_serializers.py +31 -31
- django_cfg/apps/knowbase/serializers/public_serializers.py +10 -9
- django_cfg/apps/knowbase/services/__init__.py +7 -7
- django_cfg/apps/knowbase/services/archive/__init__.py +7 -7
- django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +1 -1
- django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +1 -1
- django_cfg/apps/knowbase/services/archive/archive_service.py +109 -112
- django_cfg/apps/knowbase/services/archive/chunking/__init__.py +3 -3
- django_cfg/apps/knowbase/services/archive/chunking/base.py +1 -0
- django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +4 -3
- django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +5 -4
- django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +6 -5
- django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +4 -3
- django_cfg/apps/knowbase/services/archive/chunking_service.py +3 -7
- django_cfg/apps/knowbase/services/archive/context/__init__.py +1 -1
- django_cfg/apps/knowbase/services/archive/context/builders.py +2 -1
- django_cfg/apps/knowbase/services/archive/context/models.py +2 -1
- django_cfg/apps/knowbase/services/archive/exceptions.py +5 -5
- django_cfg/apps/knowbase/services/archive/extraction_service.py +111 -110
- django_cfg/apps/knowbase/services/archive/vectorization_service.py +80 -77
- django_cfg/apps/knowbase/services/base.py +11 -9
- django_cfg/apps/knowbase/services/chat_service.py +40 -39
- django_cfg/apps/knowbase/services/document_service.py +28 -27
- django_cfg/apps/knowbase/services/embedding/__init__.py +9 -9
- django_cfg/apps/knowbase/services/embedding/async_processor.py +38 -40
- django_cfg/apps/knowbase/services/embedding/batch_processor.py +45 -42
- django_cfg/apps/knowbase/services/embedding/batch_result.py +7 -6
- django_cfg/apps/knowbase/services/embedding/models.py +52 -51
- django_cfg/apps/knowbase/services/embedding/processors.py +24 -23
- django_cfg/apps/knowbase/services/embedding/utils.py +17 -17
- django_cfg/apps/knowbase/services/prompt_builder.py +24 -23
- django_cfg/apps/knowbase/services/search_service.py +52 -49
- django_cfg/apps/knowbase/signals/__init__.py +2 -5
- django_cfg/apps/knowbase/signals/archive_signals.py +35 -34
- django_cfg/apps/knowbase/signals/chat_signals.py +6 -5
- django_cfg/apps/knowbase/signals/document_signals.py +22 -22
- django_cfg/apps/knowbase/signals/external_data_signals.py +22 -22
- django_cfg/apps/knowbase/tasks/__init__.py +6 -7
- django_cfg/apps/knowbase/tasks/archive_tasks.py +41 -41
- django_cfg/apps/knowbase/tasks/document_processing.py +49 -44
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +46 -44
- django_cfg/apps/knowbase/tasks/maintenance.py +26 -24
- django_cfg/apps/knowbase/urls.py +3 -2
- django_cfg/apps/knowbase/urls_admin.py +6 -3
- django_cfg/apps/knowbase/urls_system.py +4 -5
- django_cfg/apps/knowbase/utils/chunk_settings.py +22 -20
- django_cfg/apps/knowbase/utils/text_processing.py +76 -75
- django_cfg/apps/knowbase/utils/validation.py +9 -9
- django_cfg/apps/knowbase/views/__init__.py +5 -5
- django_cfg/apps/knowbase/views/archive_views.py +90 -87
- django_cfg/apps/knowbase/views/base.py +9 -12
- django_cfg/apps/knowbase/views/chat_views.py +32 -32
- django_cfg/apps/knowbase/views/document_views.py +27 -28
- django_cfg/apps/knowbase/views/public_views.py +19 -19
- django_cfg/apps/leads/admin/leads_admin.py +41 -45
- django_cfg/apps/leads/admin/resources.py +14 -14
- django_cfg/apps/leads/admin.py +7 -9
- django_cfg/apps/leads/apps.py +1 -1
- django_cfg/apps/leads/models.py +17 -17
- django_cfg/apps/leads/serializers.py +5 -4
- django_cfg/apps/leads/signals.py +7 -7
- django_cfg/apps/leads/tests.py +47 -47
- django_cfg/apps/leads/urls.py +2 -2
- django_cfg/apps/leads/views.py +11 -11
- django_cfg/apps/maintenance/__init__.py +3 -3
- django_cfg/apps/maintenance/admin/__init__.py +1 -1
- django_cfg/apps/maintenance/admin/api_key_admin.py +36 -36
- django_cfg/apps/maintenance/admin/log_admin.py +35 -37
- django_cfg/apps/maintenance/admin/scheduled_admin.py +47 -51
- django_cfg/apps/maintenance/admin/site_admin.py +49 -52
- django_cfg/apps/maintenance/apps.py +2 -2
- django_cfg/apps/maintenance/management/commands/maintenance.py +53 -52
- django_cfg/apps/maintenance/management/commands/process_scheduled_maintenance.py +44 -44
- django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +32 -32
- django_cfg/apps/maintenance/managers/__init__.py +1 -1
- django_cfg/apps/maintenance/managers/cloudflare_site_manager.py +39 -38
- django_cfg/apps/maintenance/managers/maintenance_log_manager.py +32 -31
- django_cfg/apps/maintenance/migrations/0001_initial.py +1 -0
- django_cfg/apps/maintenance/models/__init__.py +2 -2
- django_cfg/apps/maintenance/models/cloudflare_api_key.py +15 -15
- django_cfg/apps/maintenance/models/cloudflare_site.py +24 -22
- django_cfg/apps/maintenance/models/maintenance_log.py +15 -14
- django_cfg/apps/maintenance/models/scheduled_maintenance.py +53 -52
- django_cfg/apps/maintenance/services/__init__.py +3 -3
- django_cfg/apps/maintenance/services/bulk_operations_service.py +68 -68
- django_cfg/apps/maintenance/services/maintenance_service.py +59 -58
- django_cfg/apps/maintenance/services/scheduled_maintenance_service.py +52 -52
- django_cfg/apps/maintenance/services/site_sync_service.py +64 -65
- django_cfg/apps/maintenance/utils/__init__.py +1 -1
- django_cfg/apps/maintenance/utils/retry_utils.py +17 -17
- django_cfg/apps/newsletter/admin/__init__.py +12 -16
- django_cfg/apps/newsletter/admin/newsletter_admin.py +90 -87
- django_cfg/apps/newsletter/admin/resources.py +29 -29
- django_cfg/apps/newsletter/admin.py +39 -32
- django_cfg/apps/newsletter/management/commands/test_newsletter.py +3 -3
- django_cfg/apps/newsletter/managers/__init__.py +1 -1
- django_cfg/apps/newsletter/migrations/0001_initial.py +2 -1
- django_cfg/apps/newsletter/models.py +34 -33
- django_cfg/apps/newsletter/serializers.py +13 -13
- django_cfg/apps/newsletter/services/email_service.py +42 -40
- django_cfg/apps/newsletter/signals.py +4 -3
- django_cfg/apps/newsletter/urls.py +7 -7
- django_cfg/apps/newsletter/views/__init__.py +14 -19
- django_cfg/apps/newsletter/views/campaigns.py +19 -19
- django_cfg/apps/newsletter/views/emails.py +20 -20
- django_cfg/apps/newsletter/views/newsletters.py +5 -5
- django_cfg/apps/newsletter/views/subscriptions.py +24 -24
- django_cfg/apps/newsletter/views/tracking.py +6 -5
- django_cfg/apps/payments/admin/__init__.py +6 -5
- django_cfg/apps/payments/admin/api_keys_admin.py +43 -45
- django_cfg/apps/payments/admin/balance_admin.py +41 -47
- django_cfg/apps/payments/admin/currencies_admin.py +60 -62
- django_cfg/apps/payments/admin/endpoint_groups_admin.py +14 -28
- django_cfg/apps/payments/admin/filters.py +59 -59
- django_cfg/apps/payments/admin/networks_admin.py +36 -50
- django_cfg/apps/payments/admin/payments_admin.py +47 -49
- django_cfg/apps/payments/admin/subscriptions_admin.py +30 -32
- django_cfg/apps/payments/admin/tariffs_admin.py +37 -42
- django_cfg/apps/payments/admin_interface/serializers/__init__.py +12 -13
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +28 -27
- django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +4 -5
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +407 -3
- django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +13 -13
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +5 -1
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +105 -49
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +4 -1
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +16 -8
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +16 -8
- django_cfg/apps/payments/admin_interface/views/__init__.py +8 -9
- django_cfg/apps/payments/admin_interface/views/api/__init__.py +3 -3
- django_cfg/apps/payments/admin_interface/views/api/payments.py +52 -54
- django_cfg/apps/payments/admin_interface/views/api/stats.py +33 -30
- django_cfg/apps/payments/admin_interface/views/api/users.py +10 -11
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +46 -51
- django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +9 -9
- django_cfg/apps/payments/admin_interface/views/base.py +17 -19
- django_cfg/apps/payments/admin_interface/views/dashboard.py +10 -10
- django_cfg/apps/payments/admin_interface/views/forms.py +21 -20
- django_cfg/apps/payments/apps.py +7 -6
- django_cfg/apps/payments/config/__init__.py +4 -4
- django_cfg/apps/payments/config/django_cfg_integration.py +22 -21
- django_cfg/apps/payments/config/helpers.py +14 -12
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +85 -86
- django_cfg/apps/payments/management/commands/currency_stats.py +84 -87
- django_cfg/apps/payments/management/commands/manage_currencies.py +59 -58
- django_cfg/apps/payments/management/commands/manage_providers.py +103 -105
- django_cfg/apps/payments/management/commands/process_pending_payments.py +67 -70
- django_cfg/apps/payments/management/commands/test_providers.py +83 -84
- django_cfg/apps/payments/middleware/__init__.py +1 -1
- django_cfg/apps/payments/middleware/api_access.py +77 -78
- django_cfg/apps/payments/middleware/rate_limiting.py +72 -72
- django_cfg/apps/payments/middleware/usage_tracking.py +66 -64
- django_cfg/apps/payments/migrations/0001_initial.py +2 -1
- django_cfg/apps/payments/models/__init__.py +11 -12
- django_cfg/apps/payments/models/api_keys.py +29 -27
- django_cfg/apps/payments/models/balance.py +38 -38
- django_cfg/apps/payments/models/base.py +12 -11
- django_cfg/apps/payments/models/currencies.py +53 -52
- django_cfg/apps/payments/models/managers/__init__.py +13 -14
- django_cfg/apps/payments/models/managers/api_key_managers.py +55 -53
- django_cfg/apps/payments/models/managers/balance_managers.py +98 -97
- django_cfg/apps/payments/models/managers/currency_managers.py +70 -69
- django_cfg/apps/payments/models/managers/payment_managers.py +113 -111
- django_cfg/apps/payments/models/managers/subscription_managers.py +99 -97
- django_cfg/apps/payments/models/payments.py +167 -73
- django_cfg/apps/payments/models/subscriptions.py +56 -54
- django_cfg/apps/payments/models/tariffs.py +35 -34
- django_cfg/apps/payments/services/__init__.py +33 -36
- django_cfg/apps/payments/services/cache/__init__.py +7 -1
- django_cfg/apps/payments/services/cache_service/__init__.py +22 -20
- django_cfg/apps/payments/services/cache_service/api_key_cache.py +6 -5
- django_cfg/apps/payments/services/cache_service/interfaces.py +5 -5
- django_cfg/apps/payments/services/cache_service/keys.py +8 -8
- django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +8 -7
- django_cfg/apps/payments/services/cache_service/simple_cache.py +17 -14
- django_cfg/apps/payments/services/core/__init__.py +3 -3
- django_cfg/apps/payments/services/core/balance_service.py +69 -65
- django_cfg/apps/payments/services/core/base.py +25 -22
- django_cfg/apps/payments/services/core/currency/__init__.py +1 -1
- django_cfg/apps/payments/services/core/currency/currency_converter.py +2 -0
- django_cfg/apps/payments/services/core/currency_service.py +68 -66
- django_cfg/apps/payments/services/core/operations/__init__.py +1 -1
- django_cfg/apps/payments/services/core/operations/payment_canceller.py +2 -1
- django_cfg/apps/payments/services/core/operations/payment_creator.py +2 -1
- django_cfg/apps/payments/services/core/operations/status_checker.py +2 -1
- django_cfg/apps/payments/services/core/payment_service.py +10 -7
- django_cfg/apps/payments/services/core/providers/provider_client.py +2 -2
- django_cfg/apps/payments/services/core/subscription_service.py +77 -74
- django_cfg/apps/payments/services/core/utils/data_converter.py +1 -0
- django_cfg/apps/payments/services/core/utils/statistics_calculator.py +1 -0
- django_cfg/apps/payments/services/core/webhook_service.py +67 -63
- django_cfg/apps/payments/services/integrations/__init__.py +3 -3
- django_cfg/apps/payments/services/integrations/ngrok_service.py +1 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +3 -2
- django_cfg/apps/payments/services/providers/base.py +59 -57
- django_cfg/apps/payments/services/providers/models/__init__.py +7 -14
- django_cfg/apps/payments/services/providers/models/base.py +15 -15
- django_cfg/apps/payments/services/providers/models/providers.py +13 -12
- django_cfg/apps/payments/services/providers/models/universal.py +6 -5
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +4 -4
- django_cfg/apps/payments/services/providers/nowpayments/config.py +9 -8
- django_cfg/apps/payments/services/providers/nowpayments/models.py +15 -15
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +2 -2
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +26 -26
- django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +3 -2
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +95 -99
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +41 -40
- django_cfg/apps/payments/services/providers/registry.py +65 -63
- django_cfg/apps/payments/services/providers/sync_service.py +50 -50
- django_cfg/apps/payments/services/types/__init__.py +21 -22
- django_cfg/apps/payments/services/types/data.py +14 -13
- django_cfg/apps/payments/services/types/requests.py +21 -22
- django_cfg/apps/payments/services/types/responses.py +16 -15
- django_cfg/apps/payments/services/types/webhooks.py +30 -30
- django_cfg/apps/payments/signals/__init__.py +4 -6
- django_cfg/apps/payments/signals/api_key_signals.py +33 -32
- django_cfg/apps/payments/signals/balance_signals.py +28 -26
- django_cfg/apps/payments/signals/payment_signals.py +29 -28
- django_cfg/apps/payments/signals/subscription_signals.py +39 -38
- django_cfg/apps/payments/static/payments/js/ngrok-status.js +12 -8
- django_cfg/apps/payments/static/payments/js/payment-detail.js +1 -1
- django_cfg/apps/payments/static/payments/js/payment-form.js +3 -3
- django_cfg/apps/payments/static/payments/js/payment-list.js +13 -6
- django_cfg/apps/payments/static/payments/js/webhook-dashboard-mjs.js +241 -0
- django_cfg/apps/payments/tasks/__init__.py +11 -12
- django_cfg/apps/payments/tasks/types.py +10 -9
- django_cfg/apps/payments/tasks/usage_tracking.py +44 -46
- django_cfg/apps/payments/templatetags/payment_tags.py +27 -27
- django_cfg/apps/payments/urls.py +31 -14
- django_cfg/apps/payments/urls_admin.py +10 -10
- django_cfg/apps/payments/views/api/__init__.py +32 -33
- django_cfg/apps/payments/views/api/api_keys.py +62 -62
- django_cfg/apps/payments/views/api/balances.py +63 -64
- django_cfg/apps/payments/views/api/base.py +52 -52
- django_cfg/apps/payments/views/api/currencies.py +75 -63
- django_cfg/apps/payments/views/api/payments.py +73 -74
- django_cfg/apps/payments/views/api/subscriptions.py +71 -72
- django_cfg/apps/payments/views/api/webhooks.py +85 -84
- django_cfg/apps/payments/views/overview/__init__.py +7 -7
- django_cfg/apps/payments/views/overview/serializers.py +13 -14
- django_cfg/apps/payments/views/overview/services.py +66 -67
- django_cfg/apps/payments/views/overview/urls.py +2 -1
- django_cfg/apps/payments/views/overview/views.py +31 -31
- django_cfg/apps/payments/views/serializers/__init__.py +35 -36
- django_cfg/apps/payments/views/serializers/api_keys.py +59 -57
- django_cfg/apps/payments/views/serializers/balances.py +34 -33
- django_cfg/apps/payments/views/serializers/currencies.py +36 -34
- django_cfg/apps/payments/views/serializers/payments.py +48 -47
- django_cfg/apps/payments/views/serializers/subscriptions.py +50 -45
- django_cfg/apps/payments/views/serializers/webhooks.py +17 -16
- django_cfg/apps/support/admin/__init__.py +3 -3
- django_cfg/apps/support/admin/resources.py +26 -26
- django_cfg/apps/support/admin/support_admin.py +44 -48
- django_cfg/apps/support/admin.py +16 -15
- django_cfg/apps/support/apps.py +1 -1
- django_cfg/apps/support/managers/message_manager.py +4 -4
- django_cfg/apps/support/managers/ticket_manager.py +13 -12
- django_cfg/apps/support/migrations/0001_initial.py +2 -1
- django_cfg/apps/support/models.py +3 -1
- django_cfg/apps/support/serializers.py +4 -2
- django_cfg/apps/support/signals.py +12 -10
- django_cfg/apps/support/urls.py +4 -3
- django_cfg/apps/support/utils/support_email_service.py +11 -9
- django_cfg/apps/support/views/__init__.py +3 -3
- django_cfg/apps/support/views/admin.py +9 -9
- django_cfg/apps/support/views/api.py +10 -9
- django_cfg/apps/support/views/chat.py +14 -14
- django_cfg/apps/tasks/admin/tasks_admin.py +65 -74
- django_cfg/apps/tasks/apps.py +2 -2
- django_cfg/apps/tasks/serializers.py +6 -6
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +44 -20
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +7 -5
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +5 -3
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +5 -3
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +5 -3
- django_cfg/apps/tasks/tasks/demo_tasks.py +12 -11
- django_cfg/apps/tasks/templates/tasks/components/tasks_mjs_integration.html +269 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard-improved.html +168 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +21 -2
- django_cfg/apps/tasks/urls.py +3 -2
- django_cfg/apps/tasks/urls_admin.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +49 -52
- django_cfg/apps/tasks/views/api.py +75 -73
- django_cfg/apps/tasks/views/dashboard.py +5 -4
- django_cfg/apps/urls.py +20 -11
- django_cfg/apps.py +6 -5
- django_cfg/cli/commands/create_project.py +7 -6
- django_cfg/cli/commands/info.py +25 -25
- django_cfg/cli/utils.py +27 -27
- django_cfg/config.py +1 -1
- django_cfg/core/__init__.py +8 -8
- django_cfg/core/base/config_model.py +13 -12
- django_cfg/core/builders/apps_builder.py +2 -2
- django_cfg/core/builders/middleware_builder.py +1 -1
- django_cfg/core/builders/security_builder.py +1 -1
- django_cfg/core/config.py +2 -2
- django_cfg/core/environment/detector.py +27 -28
- django_cfg/core/exceptions.py +1 -1
- django_cfg/core/generation/core_generators/settings.py +1 -1
- django_cfg/core/generation/core_generators/static.py +11 -5
- django_cfg/core/generation/core_generators/templates.py +1 -1
- django_cfg/core/generation/data_generators/__init__.py +1 -1
- django_cfg/core/generation/data_generators/cache.py +1 -1
- django_cfg/core/generation/data_generators/database.py +1 -1
- django_cfg/core/generation/generation.py +1 -3
- django_cfg/core/generation/integration_generators/__init__.py +2 -2
- django_cfg/core/generation/integration_generators/api.py +12 -2
- django_cfg/core/generation/integration_generators/sessions.py +1 -1
- django_cfg/core/generation/integration_generators/tailwind.py +1 -1
- django_cfg/core/generation/integration_generators/tasks.py +1 -1
- django_cfg/core/generation/integration_generators/third_party.py +1 -1
- django_cfg/core/generation/orchestrator.py +1 -1
- django_cfg/core/generation/protocols.py +1 -1
- django_cfg/core/generation/utility_generators/__init__.py +1 -1
- django_cfg/core/generation/utility_generators/email.py +1 -1
- django_cfg/core/generation/utility_generators/i18n.py +1 -1
- django_cfg/core/generation/utility_generators/limits.py +1 -1
- django_cfg/core/generation/utility_generators/logging.py +1 -1
- django_cfg/core/generation/utility_generators/security.py +1 -1
- django_cfg/core/generation/utils/helpers.py +1 -1
- django_cfg/core/integration/__init__.py +5 -5
- django_cfg/core/integration/commands_collector.py +38 -39
- django_cfg/core/integration/display/__init__.py +2 -2
- django_cfg/core/integration/display/base.py +30 -30
- django_cfg/core/integration/display/ngrok.py +35 -36
- django_cfg/core/integration/display/startup.py +149 -139
- django_cfg/core/integration/url_integration.py +10 -10
- django_cfg/core/integration/version_checker.py +20 -19
- django_cfg/core/services/config_service.py +4 -4
- django_cfg/core/state/__init__.py +1 -1
- django_cfg/core/state/registry.py +1 -1
- django_cfg/core/types/__init__.py +8 -1
- django_cfg/core/validation.py +36 -39
- django_cfg/management/commands/check_endpoints.py +3 -1
- django_cfg/management/commands/check_settings.py +3 -1
- django_cfg/management/commands/clear_constance.py +3 -1
- django_cfg/management/commands/create_token.py +3 -1
- django_cfg/management/commands/generate_clients.py +3 -1
- django_cfg/management/commands/migrate_all.py +3 -1
- django_cfg/management/commands/rundramatiq.py +3 -1
- django_cfg/management/commands/rundramatiq_simulator.py +3 -1
- django_cfg/management/commands/runserver_ngrok.py +3 -1
- django_cfg/management/commands/show_config.py +3 -1
- django_cfg/management/commands/superuser.py +3 -1
- django_cfg/management/commands/task_clear.py +3 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -1
- django_cfg/management/commands/test_telegram.py +3 -1
- django_cfg/management/commands/test_twilio.py +3 -1
- django_cfg/management/commands/validate_openapi.py +3 -1
- django_cfg/middleware/pagination.py +8 -8
- django_cfg/middleware/public_endpoints.py +24 -23
- django_cfg/middleware/user_activity.py +27 -25
- django_cfg/models/__init__.py +19 -20
- django_cfg/models/api/__init__.py +4 -4
- django_cfg/models/api/config.py +23 -21
- django_cfg/models/api/cors.py +17 -16
- django_cfg/models/api/drf/__init__.py +1 -1
- django_cfg/models/api/drf/config.py +2 -1
- django_cfg/models/api/drf/redoc.py +2 -1
- django_cfg/models/api/drf/spectacular.py +4 -2
- django_cfg/models/api/drf/swagger.py +2 -1
- django_cfg/models/api/jwt.py +37 -36
- django_cfg/models/api/keys.py +13 -12
- django_cfg/models/api/limits.py +31 -30
- django_cfg/models/base/config.py +40 -41
- django_cfg/models/base/module.py +8 -8
- django_cfg/models/django/__init__.py +1 -1
- django_cfg/models/django/constance.py +8 -7
- django_cfg/models/django/environment.py +5 -3
- django_cfg/models/django/openapi.py +6 -16
- django_cfg/models/django/revolution_legacy.py +17 -16
- django_cfg/models/infrastructure/__init__.py +1 -1
- django_cfg/models/infrastructure/cache.py +46 -45
- django_cfg/models/infrastructure/database/config.py +4 -6
- django_cfg/models/infrastructure/database/converters.py +1 -1
- django_cfg/models/infrastructure/database/parsers.py +1 -1
- django_cfg/models/infrastructure/database/validators.py +1 -1
- django_cfg/models/infrastructure/logging.py +59 -57
- django_cfg/models/infrastructure/security.py +26 -24
- django_cfg/models/ngrok/auth.py +2 -1
- django_cfg/models/ngrok/config.py +3 -2
- django_cfg/models/ngrok/tunnel.py +2 -1
- django_cfg/models/payments/__init__.py +1 -1
- django_cfg/models/payments/api_keys.py +3 -1
- django_cfg/models/payments/config.py +4 -1
- django_cfg/models/payments/providers/base.py +2 -1
- django_cfg/models/payments/providers/nowpayments.py +3 -1
- django_cfg/models/services/__init__.py +1 -1
- django_cfg/models/services/base.py +2 -1
- django_cfg/models/services/email.py +28 -26
- django_cfg/models/services/telegram.py +2 -1
- django_cfg/models/tasks/__init__.py +2 -2
- django_cfg/models/tasks/backends.py +4 -3
- django_cfg/models/tasks/config.py +6 -4
- django_cfg/models/tasks/utils.py +3 -3
- django_cfg/modules/base.py +18 -17
- django_cfg/modules/django_admin/__init__.py +14 -15
- django_cfg/modules/django_admin/decorators/__init__.py +1 -1
- django_cfg/modules/django_admin/decorators/actions.py +8 -7
- django_cfg/modules/django_admin/decorators/display.py +9 -7
- django_cfg/modules/django_admin/icons/__init__.py +1 -1
- django_cfg/modules/django_admin/icons/constants.py +27 -27
- django_cfg/modules/django_admin/icons/generate_icons.py +54 -54
- django_cfg/modules/django_admin/management/commands/check_endpoints.py +5 -3
- django_cfg/modules/django_admin/management/commands/check_settings.py +40 -44
- django_cfg/modules/django_admin/management/commands/clear_constance.py +29 -30
- django_cfg/modules/django_admin/management/commands/create_token.py +42 -42
- django_cfg/modules/django_admin/management/commands/list_urls.py +37 -38
- django_cfg/modules/django_admin/management/commands/migrate_all.py +13 -15
- django_cfg/modules/django_admin/management/commands/migrator.py +17 -17
- django_cfg/modules/django_admin/management/commands/script.py +58 -60
- django_cfg/modules/django_admin/management/commands/show_config.py +32 -30
- django_cfg/modules/django_admin/management/commands/show_urls.py +46 -44
- django_cfg/modules/django_admin/management/commands/superuser.py +47 -48
- django_cfg/modules/django_admin/management/commands/tree.py +50 -54
- django_cfg/modules/django_admin/mixins/display_mixin.py +16 -15
- django_cfg/modules/django_admin/mixins/optimization_mixin.py +9 -8
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +25 -24
- django_cfg/modules/django_admin/models/__init__.py +4 -4
- django_cfg/modules/django_admin/models/action_models.py +3 -1
- django_cfg/modules/django_admin/models/badge_models.py +4 -2
- django_cfg/modules/django_admin/models/base.py +3 -3
- django_cfg/modules/django_admin/models/display_models.py +1 -0
- django_cfg/modules/django_admin/utils/badges.py +27 -26
- django_cfg/modules/django_admin/utils/displays.py +49 -49
- django_cfg/modules/django_client/apps.py +21 -3
- django_cfg/modules/django_client/core/__init__.py +9 -10
- django_cfg/modules/django_client/core/archive/manager.py +2 -2
- django_cfg/modules/django_client/core/cli/main.py +1 -3
- django_cfg/modules/django_client/core/config/config.py +3 -1
- django_cfg/modules/django_client/core/config/group.py +1 -0
- django_cfg/modules/django_client/core/config/service.py +2 -1
- django_cfg/modules/django_client/core/generator/__init__.py +1 -1
- django_cfg/modules/django_client/core/generator/base.py +2 -2
- django_cfg/modules/django_client/core/generator/python/async_client_gen.py +1 -1
- django_cfg/modules/django_client/core/generator/python/files_generator.py +1 -1
- django_cfg/modules/django_client/core/generator/python/generator.py +4 -4
- django_cfg/modules/django_client/core/generator/python/models_generator.py +1 -1
- django_cfg/modules/django_client/core/generator/python/operations_generator.py +2 -2
- django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +1 -1
- django_cfg/modules/django_client/core/generator/typescript/client_generator.py +3 -1
- django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +14 -13
- django_cfg/modules/django_client/core/generator/typescript/files_generator.py +1 -0
- django_cfg/modules/django_client/core/generator/typescript/generator.py +6 -6
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +12 -10
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +2 -1
- django_cfg/modules/django_client/core/generator/typescript/naming.py +2 -3
- django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +12 -10
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +3 -2
- django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +14 -6
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +4 -10
- django_cfg/modules/django_client/core/groups/__init__.py +1 -1
- django_cfg/modules/django_client/core/groups/detector.py +2 -1
- django_cfg/modules/django_client/core/groups/manager.py +2 -1
- django_cfg/modules/django_client/core/ir/schema.py +1 -1
- django_cfg/modules/django_client/core/parser/base.py +0 -2
- django_cfg/modules/django_client/core/parser/models/schema.py +1 -1
- django_cfg/modules/django_client/core/validation/__init__.py +1 -1
- django_cfg/modules/django_client/core/validation/fixer.py +1 -2
- django_cfg/modules/django_client/core/validation/reporter.py +2 -2
- django_cfg/modules/django_client/core/validation/safety.py +1 -1
- django_cfg/modules/django_client/management/commands/generate_client.py +14 -11
- django_cfg/modules/django_client/management/commands/validate_openapi.py +4 -6
- django_cfg/modules/django_client/spectacular/__init__.py +1 -1
- django_cfg/modules/django_client/spectacular/async_detection.py +3 -2
- django_cfg/modules/django_client/spectacular/enum_naming.py +1 -1
- django_cfg/modules/django_client/spectacular/schema.py +5 -5
- django_cfg/modules/django_client/system/__init__.py +24 -0
- django_cfg/modules/django_client/system/base_generator.py +123 -0
- django_cfg/modules/django_client/system/generate_mjs_clients.py +174 -0
- django_cfg/modules/django_client/system/mjs_generator.py +219 -0
- django_cfg/modules/django_client/system/schema_parser.py +195 -0
- django_cfg/modules/django_client/system/templates/api_client.js.j2 +87 -0
- django_cfg/modules/django_client/system/templates/app_index.js.j2 +13 -0
- django_cfg/modules/django_client/system/templates/base_client.js.j2 +166 -0
- django_cfg/modules/django_client/system/templates/main_index.js.j2 +80 -0
- django_cfg/modules/django_client/system/templates/types.js.j2 +24 -0
- django_cfg/modules/django_client/urls.py +3 -2
- django_cfg/modules/django_currency/__init__.py +17 -18
- django_cfg/modules/django_currency/clients/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +48 -48
- django_cfg/modules/django_currency/clients/hybrid_client.py +76 -75
- django_cfg/modules/django_currency/core/__init__.py +7 -13
- django_cfg/modules/django_currency/core/converter.py +25 -24
- django_cfg/modules/django_currency/core/models.py +9 -8
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +65 -66
- django_cfg/modules/django_currency/examples/example_database_usage.py +29 -28
- django_cfg/modules/django_currency/utils/cache.py +10 -11
- django_cfg/modules/django_dashboard/__init__.py +4 -5
- django_cfg/modules/django_dashboard/components.py +11 -7
- django_cfg/modules/django_dashboard/debug.py +1 -3
- django_cfg/modules/django_dashboard/management/commands/debug_dashboard.py +3 -3
- django_cfg/modules/django_dashboard/sections/base.py +2 -1
- django_cfg/modules/django_dashboard/sections/commands.py +3 -2
- django_cfg/modules/django_dashboard/sections/documentation.py +8 -6
- django_cfg/modules/django_dashboard/sections/overview.py +13 -9
- django_cfg/modules/django_dashboard/sections/stats.py +2 -2
- django_cfg/modules/django_dashboard/sections/system.py +2 -1
- django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +12 -4
- django_cfg/modules/django_email/management/commands/test_email.py +8 -7
- django_cfg/modules/django_email/service.py +5 -4
- django_cfg/modules/django_health/service.py +46 -44
- django_cfg/modules/django_import_export/__init__.py +7 -3
- django_cfg/modules/django_llm/__init__.py +3 -2
- django_cfg/modules/django_llm/example.py +58 -56
- django_cfg/modules/django_llm/llm/__init__.py +1 -1
- django_cfg/modules/django_llm/llm/cache.py +21 -20
- django_cfg/modules/django_llm/llm/client.py +9 -9
- django_cfg/modules/django_llm/llm/costs.py +14 -14
- django_cfg/modules/django_llm/llm/embeddings/__init__.py +1 -1
- django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +1 -2
- django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +1 -2
- django_cfg/modules/django_llm/llm/extractor.py +8 -8
- django_cfg/modules/django_llm/llm/models.py +5 -5
- django_cfg/modules/django_llm/llm/models_api/models_query.py +2 -2
- django_cfg/modules/django_llm/llm/models_cache.py +91 -92
- django_cfg/modules/django_llm/llm/providers/config_builder.py +1 -1
- django_cfg/modules/django_llm/llm/providers/provider_manager.py +2 -1
- django_cfg/modules/django_llm/llm/requests/cache_manager.py +1 -1
- django_cfg/modules/django_llm/llm/requests/chat_handler.py +2 -2
- django_cfg/modules/django_llm/llm/requests/embedding_handler.py +1 -1
- django_cfg/modules/django_llm/llm/responses/response_builder.py +2 -2
- django_cfg/modules/django_llm/llm/stats/stats_manager.py +1 -1
- django_cfg/modules/django_llm/llm/tokenizer.py +10 -9
- django_cfg/modules/django_llm/translator/__init__.py +1 -1
- django_cfg/modules/django_llm/translator/cache.py +36 -35
- django_cfg/modules/django_llm/translator/detectors/__init__.py +1 -1
- django_cfg/modules/django_llm/translator/detectors/script_detector.py +0 -1
- django_cfg/modules/django_llm/translator/stats/stats_tracker.py +1 -1
- django_cfg/modules/django_llm/translator/translator.py +5 -4
- django_cfg/modules/django_llm/translator/translators/__init__.py +1 -1
- django_cfg/modules/django_llm/translator/translators/json_translator.py +1 -1
- django_cfg/modules/django_llm/translator/utils/__init__.py +1 -1
- django_cfg/modules/django_llm/translator/utils/prompt_builder.py +0 -1
- django_cfg/modules/django_logging/__init__.py +1 -1
- django_cfg/modules/django_logging/django_logger.py +33 -34
- django_cfg/modules/django_logging/logger.py +3 -7
- django_cfg/modules/django_ngrok/__init__.py +7 -7
- django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +33 -30
- django_cfg/modules/django_ngrok/service.py +33 -32
- django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +4 -36
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +1 -1
- django_cfg/modules/django_tasks/__init__.py +5 -5
- django_cfg/modules/django_tasks/dramatiq_setup.py +1 -1
- django_cfg/modules/django_tasks/factory.py +1 -1
- django_cfg/modules/django_tasks/management/commands/rundramatiq.py +39 -40
- django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +79 -80
- django_cfg/modules/django_tasks/management/commands/task_clear.py +34 -34
- django_cfg/modules/django_tasks/management/commands/task_status.py +34 -34
- django_cfg/modules/django_tasks/service.py +4 -3
- django_cfg/modules/django_tasks/settings.py +1 -1
- django_cfg/modules/django_telegram/__init__.py +4 -4
- django_cfg/modules/django_telegram/management/commands/test_telegram.py +4 -5
- django_cfg/modules/django_telegram/service.py +4 -3
- django_cfg/modules/django_telegram/utils.py +1 -1
- django_cfg/modules/django_twilio/__init__.py +15 -16
- django_cfg/modules/django_twilio/_imports.py +1 -1
- django_cfg/modules/django_twilio/base.py +9 -5
- django_cfg/modules/django_twilio/email_otp.py +4 -3
- django_cfg/modules/django_twilio/exceptions.py +36 -36
- django_cfg/modules/django_twilio/management/commands/test_twilio.py +6 -7
- django_cfg/modules/django_twilio/models.py +54 -53
- django_cfg/modules/django_twilio/sendgrid_service.py +70 -72
- django_cfg/modules/django_twilio/simple_service.py +42 -41
- django_cfg/modules/django_twilio/sms.py +1 -0
- django_cfg/modules/django_twilio/twilio_service.py +79 -83
- django_cfg/modules/django_twilio/unified.py +6 -5
- django_cfg/modules/django_twilio/utils.py +2 -3
- django_cfg/modules/django_twilio/whatsapp.py +3 -2
- django_cfg/modules/django_unfold/__init__.py +7 -6
- django_cfg/modules/django_unfold/callbacks/actions.py +6 -5
- django_cfg/modules/django_unfold/callbacks/apizones.py +122 -0
- django_cfg/modules/django_unfold/callbacks/base.py +9 -8
- django_cfg/modules/django_unfold/callbacks/charts.py +36 -37
- django_cfg/modules/django_unfold/callbacks/commands.py +2 -2
- django_cfg/modules/django_unfold/callbacks/main.py +27 -27
- django_cfg/modules/django_unfold/callbacks/statistics.py +12 -12
- django_cfg/modules/django_unfold/callbacks/system.py +5 -5
- django_cfg/modules/django_unfold/callbacks/users.py +4 -4
- django_cfg/modules/django_unfold/dashboard.py +29 -29
- django_cfg/modules/django_unfold/models/__init__.py +23 -8
- django_cfg/modules/django_unfold/models/config.py +84 -81
- django_cfg/modules/django_unfold/models/dashboard.py +20 -19
- django_cfg/modules/django_unfold/models/dropdown.py +6 -4
- django_cfg/modules/django_unfold/models/navigation.py +6 -4
- django_cfg/modules/django_unfold/models/tabs.py +4 -3
- django_cfg/modules/django_unfold/models.py +2 -3
- django_cfg/modules/django_unfold/system_monitor.py +27 -25
- django_cfg/modules/django_unfold/tailwind.py +12 -14
- django_cfg/modules/django_unfold/utils.py +7 -6
- django_cfg/pyproject.toml +1 -1
- django_cfg/registry/__init__.py +3 -3
- django_cfg/registry/core.py +8 -8
- django_cfg/registry/modules.py +2 -2
- django_cfg/registry/services.py +2 -2
- django_cfg/registry/third_party.py +3 -3
- django_cfg/routing/__init__.py +3 -3
- django_cfg/routing/callbacks.py +27 -26
- django_cfg/routing/routers.py +2 -2
- django_cfg/static/js/api/accounts/client.mjs +69 -0
- django_cfg/static/js/api/accounts/index.mjs +13 -0
- django_cfg/static/js/api/base.mjs +166 -0
- django_cfg/static/js/api/index.mjs +100 -0
- django_cfg/static/js/api/ipc/client.mjs +74 -0
- django_cfg/static/js/api/ipc/index.mjs +13 -0
- django_cfg/static/js/api/leads/client.mjs +71 -0
- django_cfg/static/js/api/leads/index.mjs +13 -0
- django_cfg/static/js/api/newsletter/client.mjs +109 -0
- django_cfg/static/js/api/newsletter/index.mjs +13 -0
- django_cfg/static/js/api/payments/client.mjs +1264 -0
- django_cfg/static/js/api/payments/index.mjs +13 -0
- django_cfg/static/js/api/support/client.mjs +84 -0
- django_cfg/static/js/api/support/index.mjs +13 -0
- django_cfg/static/js/api/tasks/client.mjs +74 -0
- django_cfg/static/js/api/tasks/index.mjs +13 -0
- django_cfg/static/js/api/types.mjs +729 -0
- django_cfg/static/js/api-loader.mjs +169 -0
- django_cfg/templates/admin/snippets/zones/zones_table.html +4 -3
- django_cfg/templatetags/django_cfg.py +4 -4
- django_cfg/utils/path_resolution.py +49 -50
- django_cfg/utils/smart_defaults.py +27 -29
- django_cfg/utils/version_check.py +14 -14
- {django_cfg-1.4.21.dist-info → django_cfg-1.4.24.dist-info}/METADATA +1 -1
- django_cfg-1.4.24.dist-info/RECORD +1137 -0
- django_cfg/apps/payments/static/payments/js/api-client.js +0 -408
- django_cfg/modules/django_ipc_client/dashboard/README.md +0 -517
- django_cfg/modules/django_ipc_client/dashboard/UNFOLD_INTEGRATION.md +0 -439
- django_cfg/modules/django_ipc_client/dashboard/__init__.py +0 -11
- django_cfg/modules/django_ipc_client/dashboard/apps.py +0 -22
- django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/dashboard.html +0 -200
- django_cfg/modules/django_ipc_client/dashboard/urls.py +0 -22
- django_cfg/modules/django_ipc_client/dashboard/urls_admin.py +0 -9
- django_cfg/modules/django_ipc_client/dashboard/views.py +0 -251
- django_cfg/modules/django_rpc_old/POETRY.md +0 -344
- django_cfg/modules/django_rpc_old/README.md +0 -397
- django_cfg/modules/django_rpc_old/TESTING.md +0 -358
- django_cfg/modules/django_rpc_old/__init__.py +0 -39
- django_cfg/modules/django_rpc_old/client.py +0 -531
- django_cfg/modules/django_rpc_old/config.py +0 -279
- django_cfg/modules/django_rpc_old/exceptions.py +0 -172
- django_cfg/modules/django_unfold/callbacks/revolution.py +0 -81
- django_cfg-1.4.21.dist-info/RECORD +0 -1111
- /django_cfg/{modules/django_ipc_client → apps/ipc}/README.md +0 -0
- /django_cfg/{modules/django_ipc_client → apps/ipc/services/client}/config.py +0 -0
- {django_cfg-1.4.21.dist-info → django_cfg-1.4.24.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.21.dist-info → django_cfg-1.4.24.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.21.dist-info → django_cfg-1.4.24.dist-info}/licenses/LICENSE +0 -0
@@ -10,15 +10,16 @@ All supported currencies are dynamically fetched and cached.
|
|
10
10
|
"""
|
11
11
|
|
12
12
|
import logging
|
13
|
-
import requests
|
14
|
-
import time
|
15
13
|
import random
|
16
|
-
|
17
|
-
from
|
14
|
+
import time
|
15
|
+
from datetime import datetime
|
16
|
+
from typing import Dict, Set
|
17
|
+
|
18
|
+
import requests
|
18
19
|
from cachetools import TTLCache
|
19
20
|
|
20
|
-
from ..core.models import Rate
|
21
21
|
from ..core.exceptions import RateFetchError
|
22
|
+
from ..core.models import Rate
|
22
23
|
|
23
24
|
logger = logging.getLogger(__name__)
|
24
25
|
|
@@ -30,7 +31,7 @@ class HybridCurrencyClient:
|
|
30
31
|
"""Initialize hybrid client with multiple data sources."""
|
31
32
|
self._rate_cache = TTLCache(maxsize=1000, ttl=cache_ttl)
|
32
33
|
self._session = requests.Session()
|
33
|
-
|
34
|
+
|
34
35
|
# User-Agent rotation for better reliability
|
35
36
|
self._user_agents = [
|
36
37
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
@@ -39,7 +40,7 @@ class HybridCurrencyClient:
|
|
39
40
|
'curl/7.68.0',
|
40
41
|
'python-requests/2.31.0'
|
41
42
|
]
|
42
|
-
|
43
|
+
|
43
44
|
# Data sources configuration (ordered by priority)
|
44
45
|
self._sources = {
|
45
46
|
'fawaz_currency': {
|
@@ -83,7 +84,7 @@ class HybridCurrencyClient:
|
|
83
84
|
'get_supported': self._get_cbr_supported_currencies
|
84
85
|
}
|
85
86
|
}
|
86
|
-
|
87
|
+
|
87
88
|
self._last_request_times = {}
|
88
89
|
self._max_retries = 2
|
89
90
|
|
@@ -95,14 +96,14 @@ class HybridCurrencyClient:
|
|
95
96
|
"""Make HTTP request with exponential backoff retry logic."""
|
96
97
|
source_config = self._sources[source]
|
97
98
|
rate_limit = source_config['rate_limit']
|
98
|
-
|
99
|
+
|
99
100
|
# Rate limiting
|
100
101
|
last_request = self._last_request_times.get(source, 0)
|
101
102
|
time_since_last = time.time() - last_request
|
102
103
|
if time_since_last < rate_limit:
|
103
104
|
sleep_time = rate_limit - time_since_last + random.uniform(0, 0.5)
|
104
105
|
time.sleep(sleep_time)
|
105
|
-
|
106
|
+
|
106
107
|
for attempt in range(self._max_retries + 1):
|
107
108
|
try:
|
108
109
|
request_headers = {
|
@@ -112,10 +113,10 @@ class HybridCurrencyClient:
|
|
112
113
|
}
|
113
114
|
if headers:
|
114
115
|
request_headers.update(headers)
|
115
|
-
|
116
|
+
|
116
117
|
response = self._session.get(url, headers=request_headers, timeout=10)
|
117
118
|
self._last_request_times[source] = time.time()
|
118
|
-
|
119
|
+
|
119
120
|
if response.status_code == 429:
|
120
121
|
if attempt < self._max_retries:
|
121
122
|
backoff = (2 ** attempt) * 3 + random.uniform(1, 2)
|
@@ -124,10 +125,10 @@ class HybridCurrencyClient:
|
|
124
125
|
continue
|
125
126
|
else:
|
126
127
|
raise requests.exceptions.HTTPError(f"429 Too Many Requests from {source}")
|
127
|
-
|
128
|
+
|
128
129
|
response.raise_for_status()
|
129
130
|
return response
|
130
|
-
|
131
|
+
|
131
132
|
except requests.exceptions.RequestException as e:
|
132
133
|
if attempt < self._max_retries:
|
133
134
|
backoff = (2 ** attempt) * 2 + random.uniform(0.5, 1)
|
@@ -136,7 +137,7 @@ class HybridCurrencyClient:
|
|
136
137
|
continue
|
137
138
|
else:
|
138
139
|
raise RateFetchError(f"{source} request failed: {e}")
|
139
|
-
|
140
|
+
|
140
141
|
raise RateFetchError(f"{source}: Failed after {self._max_retries + 1} attempts")
|
141
142
|
|
142
143
|
# ============================================================================
|
@@ -146,26 +147,26 @@ class HybridCurrencyClient:
|
|
146
147
|
def _get_fawaz_supported_currencies(self) -> Set[str]:
|
147
148
|
"""Get list of supported currencies from Fawaz API with caching."""
|
148
149
|
cache_key = "fawaz_supported_currencies"
|
149
|
-
|
150
|
+
|
150
151
|
if cache_key in self._rate_cache:
|
151
152
|
return self._rate_cache[cache_key]
|
152
|
-
|
153
|
+
|
153
154
|
try:
|
154
155
|
url = f"{self._sources['fawaz_currency']['url']}/usd.json"
|
155
156
|
response = self._make_request_with_retry(url, 'fawaz_currency')
|
156
157
|
data = response.json()
|
157
|
-
|
158
|
+
|
158
159
|
if 'usd' in data:
|
159
160
|
supported = set(data['usd'].keys())
|
160
161
|
supported.add('usd') # Add USD itself
|
161
162
|
logger.info(f"Fawaz API supports {len(supported)} currencies")
|
162
|
-
|
163
|
+
|
163
164
|
self._rate_cache[cache_key] = supported
|
164
165
|
return supported
|
165
166
|
else:
|
166
167
|
logger.warning("Fawaz API response format unexpected")
|
167
168
|
return set()
|
168
|
-
|
169
|
+
|
169
170
|
except Exception as e:
|
170
171
|
logger.warning(f"Failed to get Fawaz supported currencies: {e}")
|
171
172
|
# Minimal fallback
|
@@ -186,16 +187,16 @@ class HybridCurrencyClient:
|
|
186
187
|
url = f"{self._sources['fawaz_currency']['url']}/{base_lower}.json"
|
187
188
|
response = self._make_request_with_retry(url, 'fawaz_currency')
|
188
189
|
data = response.json()
|
189
|
-
|
190
|
+
|
190
191
|
if base_lower not in data:
|
191
192
|
raise RateFetchError(f"Fawaz API doesn't have base currency {base}")
|
192
|
-
|
193
|
+
|
193
194
|
rates = data[base_lower]
|
194
195
|
quote_lower = quote.lower()
|
195
|
-
|
196
|
+
|
196
197
|
if quote_lower not in rates:
|
197
198
|
raise RateFetchError(f"Fawaz API doesn't have {base}/{quote} rate")
|
198
|
-
|
199
|
+
|
199
200
|
return Rate(
|
200
201
|
source="fawaz_currency",
|
201
202
|
base_currency=base.upper(),
|
@@ -211,30 +212,30 @@ class HybridCurrencyClient:
|
|
211
212
|
def _get_prebid_supported_currencies(self) -> Set[str]:
|
212
213
|
"""Get list of supported currencies from Prebid Currency API with caching."""
|
213
214
|
cache_key = "prebid_supported_currencies"
|
214
|
-
|
215
|
+
|
215
216
|
if cache_key in self._rate_cache:
|
216
217
|
return self._rate_cache[cache_key]
|
217
|
-
|
218
|
+
|
218
219
|
try:
|
219
220
|
url = self._sources['prebid_currency']['url']
|
220
221
|
response = self._make_request_with_retry(url, 'prebid_currency')
|
221
222
|
data = response.json()
|
222
|
-
|
223
|
+
|
223
224
|
if 'conversions' in data:
|
224
225
|
supported = set()
|
225
226
|
# Prebid has conversions from multiple base currencies
|
226
227
|
for base_currency, rates in data['conversions'].items():
|
227
228
|
supported.add(base_currency.upper())
|
228
229
|
supported.update(rate.upper() for rate in rates.keys())
|
229
|
-
|
230
|
+
|
230
231
|
logger.info(f"Prebid Currency API supports {len(supported)} currencies")
|
231
|
-
|
232
|
+
|
232
233
|
self._rate_cache[cache_key] = supported
|
233
234
|
return supported
|
234
235
|
else:
|
235
236
|
logger.warning("Prebid Currency API response format unexpected")
|
236
237
|
return set()
|
237
|
-
|
238
|
+
|
238
239
|
except Exception as e:
|
239
240
|
logger.warning(f"Failed to get Prebid supported currencies: {e}")
|
240
241
|
# Fallback to major currencies
|
@@ -252,10 +253,10 @@ class HybridCurrencyClient:
|
|
252
253
|
url = self._sources['prebid_currency']['url']
|
253
254
|
response = self._make_request_with_retry(url, 'prebid_currency')
|
254
255
|
data = response.json()
|
255
|
-
|
256
|
+
|
256
257
|
base_upper = base.upper()
|
257
258
|
quote_upper = quote.upper()
|
258
|
-
|
259
|
+
|
259
260
|
# Check if we have direct conversion from base to quote
|
260
261
|
if 'conversions' in data and base_upper in data['conversions']:
|
261
262
|
rates = data['conversions'][base_upper]
|
@@ -267,7 +268,7 @@ class HybridCurrencyClient:
|
|
267
268
|
rate=float(rates[quote_upper]),
|
268
269
|
timestamp=datetime.now()
|
269
270
|
)
|
270
|
-
|
271
|
+
|
271
272
|
# Try reverse conversion (quote to base)
|
272
273
|
if 'conversions' in data and quote_upper in data['conversions']:
|
273
274
|
rates = data['conversions'][quote_upper]
|
@@ -281,7 +282,7 @@ class HybridCurrencyClient:
|
|
281
282
|
rate=1.0 / reverse_rate,
|
282
283
|
timestamp=datetime.now()
|
283
284
|
)
|
284
|
-
|
285
|
+
|
285
286
|
raise RateFetchError(f"Prebid Currency API doesn't have {base}/{quote} rate")
|
286
287
|
|
287
288
|
# ============================================================================
|
@@ -291,26 +292,26 @@ class HybridCurrencyClient:
|
|
291
292
|
def _get_frankfurter_supported_currencies(self) -> Set[str]:
|
292
293
|
"""Get list of supported currencies from Frankfurter API with caching."""
|
293
294
|
cache_key = "frankfurter_supported_currencies"
|
294
|
-
|
295
|
+
|
295
296
|
if cache_key in self._rate_cache:
|
296
297
|
return self._rate_cache[cache_key]
|
297
|
-
|
298
|
+
|
298
299
|
try:
|
299
300
|
url = f"{self._sources['frankfurter']['url']}"
|
300
301
|
response = self._make_request_with_retry(url, 'frankfurter')
|
301
302
|
data = response.json()
|
302
|
-
|
303
|
+
|
303
304
|
if 'rates' in data:
|
304
305
|
supported = set(data['rates'].keys())
|
305
306
|
supported.add('EUR') # Add EUR itself (base currency)
|
306
307
|
logger.info(f"Frankfurter API supports {len(supported)} currencies")
|
307
|
-
|
308
|
+
|
308
309
|
self._rate_cache[cache_key] = supported
|
309
310
|
return supported
|
310
311
|
else:
|
311
312
|
logger.warning("Frankfurter API response format unexpected")
|
312
313
|
return set()
|
313
|
-
|
314
|
+
|
314
315
|
except Exception as e:
|
315
316
|
logger.warning(f"Failed to get Frankfurter supported currencies: {e}")
|
316
317
|
# Fallback to major fiat currencies
|
@@ -328,10 +329,10 @@ class HybridCurrencyClient:
|
|
328
329
|
url = f"{self._sources['frankfurter']['url']}?from={base}&to={quote}"
|
329
330
|
response = self._make_request_with_retry(url, 'frankfurter')
|
330
331
|
data = response.json()
|
331
|
-
|
332
|
+
|
332
333
|
if 'rates' not in data or quote.upper() not in data['rates']:
|
333
334
|
raise RateFetchError(f"Frankfurter doesn't have {base}/{quote} rate")
|
334
|
-
|
335
|
+
|
335
336
|
return Rate(
|
336
337
|
source="frankfurter",
|
337
338
|
base_currency=base.upper(),
|
@@ -347,26 +348,26 @@ class HybridCurrencyClient:
|
|
347
348
|
def _get_exchangerate_supported_currencies(self) -> Set[str]:
|
348
349
|
"""Get list of supported currencies from ExchangeRate-API with caching."""
|
349
350
|
cache_key = "exchangerate_supported_currencies"
|
350
|
-
|
351
|
+
|
351
352
|
if cache_key in self._rate_cache:
|
352
353
|
return self._rate_cache[cache_key]
|
353
|
-
|
354
|
+
|
354
355
|
try:
|
355
356
|
url = f"{self._sources['exchangerate_api']['url']}/USD"
|
356
357
|
response = self._make_request_with_retry(url, 'exchangerate_api')
|
357
358
|
data = response.json()
|
358
|
-
|
359
|
+
|
359
360
|
if data.get('result') == 'success' and 'rates' in data:
|
360
361
|
supported = set(data['rates'].keys())
|
361
362
|
supported.add('USD') # Add USD itself (base currency)
|
362
363
|
logger.info(f"ExchangeRate-API supports {len(supported)} currencies")
|
363
|
-
|
364
|
+
|
364
365
|
self._rate_cache[cache_key] = supported
|
365
366
|
return supported
|
366
367
|
else:
|
367
368
|
logger.warning("ExchangeRate-API response format unexpected")
|
368
369
|
return set()
|
369
|
-
|
370
|
+
|
370
371
|
except Exception as e:
|
371
372
|
logger.warning(f"Failed to get ExchangeRate-API supported currencies: {e}")
|
372
373
|
# Fallback to major currencies
|
@@ -384,14 +385,14 @@ class HybridCurrencyClient:
|
|
384
385
|
url = f"{self._sources['exchangerate_api']['url']}/{base.upper()}"
|
385
386
|
response = self._make_request_with_retry(url, 'exchangerate_api')
|
386
387
|
data = response.json()
|
387
|
-
|
388
|
+
|
388
389
|
if data.get('result') != 'success':
|
389
390
|
raise RateFetchError(f"ExchangeRate-API error: {data.get('error-type', 'Unknown error')}")
|
390
|
-
|
391
|
+
|
391
392
|
rates = data.get('rates', {})
|
392
393
|
if quote.upper() not in rates:
|
393
394
|
raise RateFetchError(f"ExchangeRate-API doesn't have {quote} rate")
|
394
|
-
|
395
|
+
|
395
396
|
return Rate(
|
396
397
|
source="exchangerate_api",
|
397
398
|
base_currency=base.upper(),
|
@@ -407,26 +408,26 @@ class HybridCurrencyClient:
|
|
407
408
|
def _get_cbr_supported_currencies(self) -> Set[str]:
|
408
409
|
"""Get list of supported currencies from CBR API with caching."""
|
409
410
|
cache_key = "cbr_supported_currencies"
|
410
|
-
|
411
|
+
|
411
412
|
if cache_key in self._rate_cache:
|
412
413
|
return self._rate_cache[cache_key]
|
413
|
-
|
414
|
+
|
414
415
|
try:
|
415
416
|
url = self._sources['cbr']['url']
|
416
417
|
response = self._make_request_with_retry(url, 'cbr')
|
417
418
|
data = response.json()
|
418
|
-
|
419
|
+
|
419
420
|
if 'Valute' in data:
|
420
421
|
supported = set(data['Valute'].keys())
|
421
422
|
supported.add('RUB') # Add RUB itself
|
422
423
|
logger.info(f"CBR API supports {len(supported)} currencies")
|
423
|
-
|
424
|
+
|
424
425
|
self._rate_cache[cache_key] = supported
|
425
426
|
return supported
|
426
427
|
else:
|
427
428
|
logger.warning("CBR API response format unexpected")
|
428
429
|
return set()
|
429
|
-
|
430
|
+
|
430
431
|
except Exception as e:
|
431
432
|
logger.warning(f"Failed to get CBR supported currencies: {e}")
|
432
433
|
# Fallback to major currencies that CBR typically supports
|
@@ -438,7 +439,7 @@ class HybridCurrencyClient:
|
|
438
439
|
"""CBR supports conversions to/from RUB."""
|
439
440
|
if 'RUB' not in [base.upper(), quote.upper()]:
|
440
441
|
return False
|
441
|
-
|
442
|
+
|
442
443
|
supported_currencies = self._get_cbr_supported_currencies()
|
443
444
|
return base.upper() in supported_currencies and quote.upper() in supported_currencies
|
444
445
|
|
@@ -447,9 +448,9 @@ class HybridCurrencyClient:
|
|
447
448
|
url = self._sources['cbr']['url']
|
448
449
|
response = self._make_request_with_retry(url, 'cbr')
|
449
450
|
data = response.json()
|
450
|
-
|
451
|
+
|
451
452
|
base, quote = base.upper(), quote.upper()
|
452
|
-
|
453
|
+
|
453
454
|
if base == 'RUB' and quote in data.get('Valute', {}):
|
454
455
|
# RUB to other currency
|
455
456
|
currency_data = data['Valute'][quote]
|
@@ -460,7 +461,7 @@ class HybridCurrencyClient:
|
|
460
461
|
rate_value = currency_data['Value'] / currency_data['Nominal']
|
461
462
|
else:
|
462
463
|
raise RateFetchError(f"CBR doesn't support {base}/{quote}")
|
463
|
-
|
464
|
+
|
464
465
|
return Rate(
|
465
466
|
source="cbr",
|
466
467
|
base_currency=base,
|
@@ -486,56 +487,56 @@ class HybridCurrencyClient:
|
|
486
487
|
"""
|
487
488
|
base, quote = base.upper(), quote.upper()
|
488
489
|
cache_key = f"{base}_{quote}"
|
489
|
-
|
490
|
+
|
490
491
|
# Check cache first
|
491
492
|
if cache_key in self._rate_cache:
|
492
493
|
logger.debug(f"Retrieved rate {base}/{quote} from cache")
|
493
494
|
return self._rate_cache[cache_key]
|
494
|
-
|
495
|
+
|
495
496
|
# Try sources in priority order
|
496
497
|
sources_to_try = []
|
497
498
|
for source_name, config in self._sources.items():
|
498
499
|
if config['supports_check'](base, quote):
|
499
500
|
sources_to_try.append((config['priority'], source_name))
|
500
|
-
|
501
|
+
|
501
502
|
sources_to_try.sort(key=lambda x: x[0]) # Sort by priority
|
502
|
-
|
503
|
+
|
503
504
|
last_error = None
|
504
505
|
for priority, source_name in sources_to_try:
|
505
506
|
try:
|
506
507
|
logger.debug(f"Trying {source_name} for {base}/{quote}")
|
507
|
-
|
508
|
+
|
508
509
|
config = self._sources[source_name]
|
509
510
|
rate = config['fetch_method'](base, quote)
|
510
|
-
|
511
|
+
|
511
512
|
# Cache successful result
|
512
513
|
self._rate_cache[cache_key] = rate
|
513
514
|
logger.info(f"Fetched {base}/{quote} = {rate.rate} from {source_name}")
|
514
515
|
return rate
|
515
|
-
|
516
|
+
|
516
517
|
except Exception as e:
|
517
518
|
logger.warning(f"{source_name} failed for {base}/{quote}: {e}")
|
518
519
|
last_error = e
|
519
520
|
continue
|
520
|
-
|
521
|
+
|
521
522
|
raise RateFetchError(f"All sources failed for {base}/{quote}. Last error: {last_error}")
|
522
523
|
|
523
524
|
def supports_pair(self, base: str, quote: str) -> bool:
|
524
525
|
"""Check if any source supports the currency pair."""
|
525
526
|
base, quote = base.upper(), quote.upper()
|
526
527
|
return any(
|
527
|
-
config['supports_check'](base, quote)
|
528
|
+
config['supports_check'](base, quote)
|
528
529
|
for config in self._sources.values()
|
529
530
|
)
|
530
531
|
|
531
532
|
def get_all_supported_currencies(self) -> Dict[str, str]:
|
532
533
|
"""Get all supported currencies across all sources dynamically."""
|
533
534
|
cache_key = "all_supported_currencies"
|
534
|
-
|
535
|
+
|
535
536
|
# Check cache first
|
536
537
|
if cache_key in self._rate_cache:
|
537
538
|
return self._rate_cache[cache_key]
|
538
|
-
|
539
|
+
|
539
540
|
# Collect all currencies from all sources
|
540
541
|
all_currencies = set()
|
541
542
|
for source_name, config in self._sources.items():
|
@@ -545,7 +546,7 @@ class HybridCurrencyClient:
|
|
545
546
|
logger.debug(f"Added {len(supported)} currencies from {source_name}")
|
546
547
|
except Exception as e:
|
547
548
|
logger.warning(f"Failed to get supported currencies from {source_name}: {e}")
|
548
|
-
|
549
|
+
|
549
550
|
# Currency names mapping
|
550
551
|
currency_names = {
|
551
552
|
# Major Fiat
|
@@ -558,7 +559,7 @@ class HybridCurrencyClient:
|
|
558
559
|
'KRW': 'South Korean Won', 'SGD': 'Singapore Dollar', 'HKD': 'Hong Kong Dollar',
|
559
560
|
'THB': 'Thai Baht', 'MXN': 'Mexican Peso', 'BRL': 'Brazilian Real',
|
560
561
|
'ZAR': 'South African Rand', 'TRY': 'Turkish Lira', 'ILS': 'Israeli Shekel',
|
561
|
-
|
562
|
+
|
562
563
|
# Cryptocurrencies
|
563
564
|
'BTC': 'Bitcoin', 'ETH': 'Ethereum', 'BNB': 'Binance Coin',
|
564
565
|
'XRP': 'Ripple', 'ADA': 'Cardano', 'SOL': 'Solana',
|
@@ -566,12 +567,12 @@ class HybridCurrencyClient:
|
|
566
567
|
'BCH': 'Bitcoin Cash', 'LINK': 'Chainlink', 'UNI': 'Uniswap',
|
567
568
|
'ATOM': 'Cosmos', 'XLM': 'Stellar', 'VET': 'VeChain',
|
568
569
|
'USDT': 'Tether USD', 'USDC': 'USD Coin', 'DAI': 'Dai Stablecoin',
|
569
|
-
|
570
|
+
|
570
571
|
# Precious Metals
|
571
572
|
'XAU': 'Gold Ounce', 'XAG': 'Silver Ounce',
|
572
573
|
'XPT': 'Platinum Ounce', 'XPD': 'Palladium Ounce'
|
573
574
|
}
|
574
|
-
|
575
|
+
|
575
576
|
# Create result with proper names
|
576
577
|
result = {}
|
577
578
|
for currency in sorted(all_currencies):
|
@@ -579,9 +580,9 @@ class HybridCurrencyClient:
|
|
579
580
|
# Test if currency actually works with USD (basic validation)
|
580
581
|
if self.supports_pair(currency_upper, 'USD'):
|
581
582
|
result[currency_upper] = currency_names.get(currency_upper, f"{currency_upper} Currency")
|
582
|
-
|
583
|
+
|
583
584
|
# Cache the result
|
584
585
|
self._rate_cache[cache_key] = result
|
585
586
|
logger.info(f"Collected {len(result)} supported currencies from all sources")
|
586
|
-
|
587
|
+
|
587
588
|
return result
|
@@ -2,35 +2,29 @@
|
|
2
2
|
Core currency conversion functionality.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from .
|
6
|
-
Rate,
|
7
|
-
ConversionRequest,
|
8
|
-
ConversionResult
|
9
|
-
)
|
10
|
-
|
5
|
+
from .converter import CurrencyConverter
|
11
6
|
from .exceptions import (
|
7
|
+
CacheError,
|
8
|
+
ConversionError,
|
12
9
|
CurrencyError,
|
13
10
|
CurrencyNotFoundError,
|
14
11
|
RateFetchError,
|
15
|
-
ConversionError,
|
16
|
-
CacheError
|
17
12
|
)
|
18
|
-
|
19
|
-
from .converter import CurrencyConverter
|
13
|
+
from .models import ConversionRequest, ConversionResult, Rate
|
20
14
|
|
21
15
|
__all__ = [
|
22
16
|
# Models
|
23
17
|
'Rate',
|
24
|
-
'ConversionRequest',
|
18
|
+
'ConversionRequest',
|
25
19
|
'ConversionResult',
|
26
|
-
|
20
|
+
|
27
21
|
# Exceptions
|
28
22
|
'CurrencyError',
|
29
23
|
'CurrencyNotFoundError',
|
30
24
|
'RateFetchError',
|
31
25
|
'ConversionError',
|
32
26
|
'CacheError',
|
33
|
-
|
27
|
+
|
34
28
|
# Main converter
|
35
29
|
'CurrencyConverter'
|
36
30
|
]
|
@@ -3,30 +3,31 @@ Main currency converter with intelligent routing.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Optional
|
7
6
|
|
8
|
-
from .models import Rate, ConversionRequest, ConversionResult
|
9
|
-
from .exceptions import ConversionError, CurrencyNotFoundError
|
10
|
-
from ..clients import HybridCurrencyClient, CoinPaprikaClient
|
11
7
|
from ..utils.cache import CacheManager
|
8
|
+
from .exceptions import ConversionError, CurrencyNotFoundError
|
9
|
+
from .models import ConversionRequest, ConversionResult, Rate
|
12
10
|
|
13
11
|
logger = logging.getLogger(__name__)
|
14
12
|
|
15
13
|
|
16
14
|
class CurrencyConverter:
|
17
15
|
"""Main currency converter with provider routing."""
|
18
|
-
|
16
|
+
|
19
17
|
def __init__(self, cache_ttl: int = 300):
|
20
18
|
"""
|
21
19
|
Initialize converter.
|
22
|
-
|
20
|
+
|
23
21
|
Args:
|
24
22
|
cache_ttl: Cache TTL in seconds
|
25
23
|
"""
|
24
|
+
# Lazy import to avoid circular dependency
|
25
|
+
from ..clients import CoinPaprikaClient, HybridCurrencyClient
|
26
|
+
|
26
27
|
self.hybrid = HybridCurrencyClient(cache_ttl=cache_ttl)
|
27
28
|
self.coinpaprika = CoinPaprikaClient(cache_ttl=cache_ttl)
|
28
29
|
self.cache = CacheManager(ttl=cache_ttl)
|
29
|
-
|
30
|
+
|
30
31
|
def convert(self, amount: float, from_currency: str, to_currency: str) -> ConversionResult:
|
31
32
|
"""
|
32
33
|
Convert amount from one currency to another.
|
@@ -49,7 +50,7 @@ class CurrencyConverter:
|
|
49
50
|
from_currency=from_currency.upper(),
|
50
51
|
to_currency=to_currency.upper()
|
51
52
|
)
|
52
|
-
|
53
|
+
|
53
54
|
# Same currency check
|
54
55
|
if request.from_currency == request.to_currency:
|
55
56
|
rate = Rate(
|
@@ -63,23 +64,23 @@ class CurrencyConverter:
|
|
63
64
|
result=amount,
|
64
65
|
rate=rate
|
65
66
|
)
|
66
|
-
|
67
|
+
|
67
68
|
# Get exchange rate
|
68
69
|
rate = self._get_rate(request.from_currency, request.to_currency)
|
69
|
-
|
70
|
+
|
70
71
|
# Calculate result
|
71
72
|
result = amount * rate.rate
|
72
|
-
|
73
|
+
|
73
74
|
return ConversionResult(
|
74
75
|
request=request,
|
75
76
|
result=result,
|
76
77
|
rate=rate
|
77
78
|
)
|
78
|
-
|
79
|
+
|
79
80
|
except Exception as e:
|
80
81
|
logger.error(f"Conversion failed: {e}")
|
81
82
|
raise ConversionError(f"Failed to convert {amount} {from_currency} to {to_currency}: {e}")
|
82
|
-
|
83
|
+
|
83
84
|
def _get_rate(self, base: str, quote: str) -> Rate:
|
84
85
|
"""
|
85
86
|
Get exchange rate using provider routing.
|
@@ -99,7 +100,7 @@ class CurrencyConverter:
|
|
99
100
|
cached_rate = self.cache.get_rate(base, quote, source)
|
100
101
|
if cached_rate:
|
101
102
|
return cached_rate
|
102
|
-
|
103
|
+
|
103
104
|
# Try Hybrid client first (multiple sources with fallback)
|
104
105
|
if self.hybrid.supports_pair(base, quote):
|
105
106
|
try:
|
@@ -108,7 +109,7 @@ class CurrencyConverter:
|
|
108
109
|
return rate
|
109
110
|
except Exception as e:
|
110
111
|
logger.warning(f"Hybrid client failed for {base}/{quote}: {e}")
|
111
|
-
|
112
|
+
|
112
113
|
# Try CoinPaprika next (excellent for crypto, no rate limits)
|
113
114
|
if self.coinpaprika.supports_pair(base, quote):
|
114
115
|
try:
|
@@ -117,16 +118,16 @@ class CurrencyConverter:
|
|
117
118
|
return rate
|
118
119
|
except Exception as e:
|
119
120
|
logger.warning(f"CoinPaprika failed for {base}/{quote}: {e}")
|
120
|
-
|
121
|
+
|
121
122
|
# Try indirect conversion via USD
|
122
123
|
if base != "USD" and quote != "USD":
|
123
124
|
try:
|
124
125
|
return self._indirect_conversion(base, quote)
|
125
126
|
except Exception as e:
|
126
127
|
logger.warning(f"Indirect conversion failed for {base}/{quote}: {e}")
|
127
|
-
|
128
|
+
|
128
129
|
raise CurrencyNotFoundError(f"No provider supports {base}/{quote}")
|
129
|
-
|
130
|
+
|
130
131
|
def _indirect_conversion(self, base: str, quote: str) -> Rate:
|
131
132
|
"""
|
132
133
|
Perform indirect conversion via USD.
|
@@ -139,23 +140,23 @@ class CurrencyConverter:
|
|
139
140
|
Rate object with combined rate
|
140
141
|
"""
|
141
142
|
logger.debug(f"Attempting indirect conversion {base} -> USD -> {quote}")
|
142
|
-
|
143
|
+
|
143
144
|
# Get base/USD rate
|
144
145
|
base_usd_rate = self._get_rate(base, "USD")
|
145
|
-
|
146
|
-
# Get USD/quote rate
|
146
|
+
|
147
|
+
# Get USD/quote rate
|
147
148
|
usd_quote_rate = self._get_rate("USD", quote)
|
148
|
-
|
149
|
+
|
149
150
|
# Calculate combined rate
|
150
151
|
combined_rate = base_usd_rate.rate * usd_quote_rate.rate
|
151
|
-
|
152
|
+
|
152
153
|
return Rate(
|
153
154
|
source=f"{base_usd_rate.source}+{usd_quote_rate.source}",
|
154
155
|
base_currency=base,
|
155
156
|
quote_currency=quote,
|
156
157
|
rate=combined_rate
|
157
158
|
)
|
158
|
-
|
159
|
+
|
159
160
|
def get_supported_currencies(self) -> dict:
|
160
161
|
"""Get list of supported currencies by provider."""
|
161
162
|
return {
|