django-cfg 1.2.23__py3-none-any.whl → 1.2.27__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/knowbase/tasks/archive_tasks.py +6 -6
- django_cfg/apps/knowbase/tasks/document_processing.py +3 -3
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +2 -2
- django_cfg/apps/knowbase/tasks/maintenance.py +3 -3
- django_cfg/apps/payments/config/__init__.py +15 -37
- django_cfg/apps/payments/config/module.py +30 -122
- django_cfg/apps/payments/config/providers.py +28 -16
- django_cfg/apps/payments/config/settings.py +53 -93
- django_cfg/apps/payments/config/utils.py +10 -156
- django_cfg/apps/payments/management/__init__.py +3 -0
- django_cfg/apps/payments/management/commands/README.md +178 -0
- django_cfg/apps/payments/management/commands/__init__.py +3 -0
- django_cfg/apps/payments/management/commands/currency_stats.py +323 -0
- django_cfg/apps/payments/management/commands/populate_currencies.py +246 -0
- django_cfg/apps/payments/management/commands/update_currencies.py +336 -0
- django_cfg/apps/payments/managers/currency_manager.py +65 -14
- django_cfg/apps/payments/middleware/api_access.py +33 -0
- django_cfg/apps/payments/migrations/0001_initial.py +94 -1
- django_cfg/apps/payments/models/payments.py +110 -0
- django_cfg/apps/payments/services/__init__.py +7 -1
- django_cfg/apps/payments/services/core/balance_service.py +14 -16
- django_cfg/apps/payments/services/core/fallback_service.py +432 -0
- django_cfg/apps/payments/services/core/payment_service.py +212 -29
- django_cfg/apps/payments/services/core/subscription_service.py +15 -17
- django_cfg/apps/payments/services/internal_types.py +31 -0
- django_cfg/apps/payments/services/monitoring/__init__.py +22 -0
- django_cfg/apps/payments/services/monitoring/api_schemas.py +222 -0
- django_cfg/apps/payments/services/monitoring/provider_health.py +372 -0
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/cryptapi.py +14 -3
- django_cfg/apps/payments/services/providers/cryptomus.py +310 -0
- django_cfg/apps/payments/services/providers/registry.py +4 -0
- django_cfg/apps/payments/services/security/__init__.py +34 -0
- django_cfg/apps/payments/services/security/error_handler.py +637 -0
- django_cfg/apps/payments/services/security/payment_notifications.py +342 -0
- django_cfg/apps/payments/services/security/webhook_validator.py +475 -0
- django_cfg/apps/payments/signals/api_key_signals.py +10 -0
- django_cfg/apps/payments/signals/payment_signals.py +3 -2
- django_cfg/apps/payments/tasks/__init__.py +12 -0
- django_cfg/apps/payments/tasks/webhook_processing.py +177 -0
- django_cfg/apps/payments/utils/__init__.py +7 -4
- django_cfg/apps/payments/utils/billing_utils.py +342 -0
- django_cfg/apps/payments/utils/config_utils.py +2 -0
- django_cfg/apps/payments/views/payment_views.py +40 -2
- django_cfg/apps/payments/views/webhook_views.py +266 -0
- django_cfg/apps/payments/viewsets.py +65 -0
- django_cfg/cli/README.md +2 -2
- django_cfg/cli/commands/create_project.py +1 -1
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/cli/main.py +1 -1
- django_cfg/cli/utils.py +5 -5
- django_cfg/core/config.py +18 -4
- django_cfg/models/payments.py +547 -0
- django_cfg/models/tasks.py +51 -2
- django_cfg/modules/base.py +11 -5
- django_cfg/modules/django_currency/README.md +104 -269
- django_cfg/modules/django_currency/__init__.py +99 -41
- django_cfg/modules/django_currency/clients/__init__.py +11 -0
- django_cfg/modules/django_currency/clients/coingecko_client.py +257 -0
- django_cfg/modules/django_currency/clients/yfinance_client.py +246 -0
- django_cfg/modules/django_currency/core/__init__.py +42 -0
- django_cfg/modules/django_currency/core/converter.py +169 -0
- django_cfg/modules/django_currency/core/exceptions.py +28 -0
- django_cfg/modules/django_currency/core/models.py +54 -0
- django_cfg/modules/django_currency/database/__init__.py +25 -0
- django_cfg/modules/django_currency/database/database_loader.py +507 -0
- django_cfg/modules/django_currency/utils/__init__.py +9 -0
- django_cfg/modules/django_currency/utils/cache.py +92 -0
- django_cfg/registry/core.py +10 -0
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.2.23.dist-info → django_cfg-1.2.27.dist-info}/METADATA +10 -6
- {django_cfg-1.2.23.dist-info → django_cfg-1.2.27.dist-info}/RECORD +77 -51
- django_cfg/apps/agents/examples/__init__.py +0 -3
- django_cfg/apps/agents/examples/simple_example.py +0 -161
- django_cfg/apps/knowbase/examples/__init__.py +0 -3
- django_cfg/apps/knowbase/examples/external_data_usage.py +0 -191
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +0 -199
- django_cfg/modules/django_currency/cache.py +0 -430
- django_cfg/modules/django_currency/converter.py +0 -324
- django_cfg/modules/django_currency/service.py +0 -277
- {django_cfg-1.2.23.dist-info → django_cfg-1.2.27.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.23.dist-info → django_cfg-1.2.27.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.23.dist-info → django_cfg-1.2.27.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
"""
|
2
|
+
Main currency converter with intelligent routing.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from .models import Rate, ConversionRequest, ConversionResult, SupportedCurrencies, YFinanceCurrencies, CoinGeckoCurrencies
|
9
|
+
from .exceptions import ConversionError, CurrencyNotFoundError
|
10
|
+
from ..clients import YFinanceClient, CoinGeckoClient
|
11
|
+
from ..utils.cache import CacheManager
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class CurrencyConverter:
|
17
|
+
"""Main currency converter with provider routing."""
|
18
|
+
|
19
|
+
def __init__(self, cache_ttl: int = 300):
|
20
|
+
"""
|
21
|
+
Initialize converter.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
cache_ttl: Cache TTL in seconds
|
25
|
+
"""
|
26
|
+
self.yfinance = YFinanceClient()
|
27
|
+
self.coingecko = CoinGeckoClient()
|
28
|
+
self.cache = CacheManager(ttl=cache_ttl)
|
29
|
+
|
30
|
+
def convert(self, amount: float, from_currency: str, to_currency: str) -> ConversionResult:
|
31
|
+
"""
|
32
|
+
Convert amount from one currency to another.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
amount: Amount to convert
|
36
|
+
from_currency: Source currency
|
37
|
+
to_currency: Target currency
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
ConversionResult with converted amount and rate info
|
41
|
+
|
42
|
+
Raises:
|
43
|
+
ConversionError: If conversion fails
|
44
|
+
"""
|
45
|
+
try:
|
46
|
+
# Validate input
|
47
|
+
request = ConversionRequest(
|
48
|
+
amount=amount,
|
49
|
+
from_currency=from_currency.upper(),
|
50
|
+
to_currency=to_currency.upper()
|
51
|
+
)
|
52
|
+
|
53
|
+
# Same currency check
|
54
|
+
if request.from_currency == request.to_currency:
|
55
|
+
rate = Rate(
|
56
|
+
source="internal",
|
57
|
+
base_currency=request.from_currency,
|
58
|
+
quote_currency=request.to_currency,
|
59
|
+
rate=1.0
|
60
|
+
)
|
61
|
+
return ConversionResult(
|
62
|
+
request=request,
|
63
|
+
result=amount,
|
64
|
+
rate=rate
|
65
|
+
)
|
66
|
+
|
67
|
+
# Get exchange rate
|
68
|
+
rate = self._get_rate(request.from_currency, request.to_currency)
|
69
|
+
|
70
|
+
# Calculate result
|
71
|
+
result = amount * rate.rate
|
72
|
+
|
73
|
+
return ConversionResult(
|
74
|
+
request=request,
|
75
|
+
result=result,
|
76
|
+
rate=rate
|
77
|
+
)
|
78
|
+
|
79
|
+
except Exception as e:
|
80
|
+
logger.error(f"Conversion failed: {e}")
|
81
|
+
raise ConversionError(f"Failed to convert {amount} {from_currency} to {to_currency}: {e}")
|
82
|
+
|
83
|
+
def _get_rate(self, base: str, quote: str) -> Rate:
|
84
|
+
"""
|
85
|
+
Get exchange rate using provider routing.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
base: Base currency
|
89
|
+
quote: Quote currency
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Rate object
|
93
|
+
|
94
|
+
Raises:
|
95
|
+
CurrencyNotFoundError: If no provider supports the pair
|
96
|
+
"""
|
97
|
+
# Try cache first
|
98
|
+
for source in ["yfinance", "coingecko"]:
|
99
|
+
cached_rate = self.cache.get_rate(base, quote, source)
|
100
|
+
if cached_rate:
|
101
|
+
return cached_rate
|
102
|
+
|
103
|
+
# Try YFinance first (good for fiat and major crypto)
|
104
|
+
if self.yfinance.supports_pair(base, quote):
|
105
|
+
try:
|
106
|
+
rate = self.yfinance.fetch_rate(base, quote)
|
107
|
+
self.cache.set_rate(rate)
|
108
|
+
return rate
|
109
|
+
except Exception as e:
|
110
|
+
logger.warning(f"YFinance failed for {base}/{quote}: {e}")
|
111
|
+
|
112
|
+
# Try CoinGecko (good for crypto)
|
113
|
+
if self.coingecko.supports_pair(base, quote):
|
114
|
+
try:
|
115
|
+
rate = self.coingecko.fetch_rate(base, quote)
|
116
|
+
self.cache.set_rate(rate)
|
117
|
+
return rate
|
118
|
+
except Exception as e:
|
119
|
+
logger.warning(f"CoinGecko failed for {base}/{quote}: {e}")
|
120
|
+
|
121
|
+
# Try indirect conversion via USD
|
122
|
+
if base != "USD" and quote != "USD":
|
123
|
+
try:
|
124
|
+
return self._indirect_conversion(base, quote)
|
125
|
+
except Exception as e:
|
126
|
+
logger.warning(f"Indirect conversion failed for {base}/{quote}: {e}")
|
127
|
+
|
128
|
+
raise CurrencyNotFoundError(f"No provider supports {base}/{quote}")
|
129
|
+
|
130
|
+
def _indirect_conversion(self, base: str, quote: str) -> Rate:
|
131
|
+
"""
|
132
|
+
Perform indirect conversion via USD.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
base: Base currency
|
136
|
+
quote: Quote currency
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Rate object with combined rate
|
140
|
+
"""
|
141
|
+
logger.debug(f"Attempting indirect conversion {base} -> USD -> {quote}")
|
142
|
+
|
143
|
+
# Get base/USD rate
|
144
|
+
base_usd_rate = self._get_rate(base, "USD")
|
145
|
+
|
146
|
+
# Get USD/quote rate
|
147
|
+
usd_quote_rate = self._get_rate("USD", quote)
|
148
|
+
|
149
|
+
# Calculate combined rate
|
150
|
+
combined_rate = base_usd_rate.rate * usd_quote_rate.rate
|
151
|
+
|
152
|
+
return Rate(
|
153
|
+
source=f"{base_usd_rate.source}+{usd_quote_rate.source}",
|
154
|
+
base_currency=base,
|
155
|
+
quote_currency=quote,
|
156
|
+
rate=combined_rate
|
157
|
+
)
|
158
|
+
|
159
|
+
def get_supported_currencies(self) -> SupportedCurrencies:
|
160
|
+
"""Get list of supported currencies by provider."""
|
161
|
+
return SupportedCurrencies(
|
162
|
+
yfinance=YFinanceCurrencies(
|
163
|
+
fiat=list(self.yfinance.get_fiat_currencies())
|
164
|
+
),
|
165
|
+
coingecko=CoinGeckoCurrencies(
|
166
|
+
crypto=list(self.coingecko.get_crypto_ids().keys()),
|
167
|
+
vs_currencies=list(self.coingecko.get_vs_currencies())
|
168
|
+
)
|
169
|
+
)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
Custom exceptions for currency conversion.
|
3
|
+
"""
|
4
|
+
|
5
|
+
|
6
|
+
class CurrencyError(Exception):
|
7
|
+
"""Base currency conversion error."""
|
8
|
+
pass
|
9
|
+
|
10
|
+
|
11
|
+
class CurrencyNotFoundError(CurrencyError):
|
12
|
+
"""Currency not supported by any provider."""
|
13
|
+
pass
|
14
|
+
|
15
|
+
|
16
|
+
class RateFetchError(CurrencyError):
|
17
|
+
"""Failed to fetch exchange rate."""
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class CacheError(CurrencyError):
|
22
|
+
"""Cache operation failed."""
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class ConversionError(CurrencyError):
|
27
|
+
"""Currency conversion failed."""
|
28
|
+
pass
|
@@ -0,0 +1,54 @@
|
|
1
|
+
"""
|
2
|
+
Simple data models for currency conversion.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
from typing import Optional, List, Set
|
7
|
+
from pydantic import BaseModel, Field
|
8
|
+
|
9
|
+
|
10
|
+
class Rate(BaseModel):
|
11
|
+
"""Currency exchange rate model."""
|
12
|
+
|
13
|
+
source: str = Field(description="Data source (yfinance, coingecko)")
|
14
|
+
base_currency: str = Field(description="Base currency code")
|
15
|
+
quote_currency: str = Field(description="Quote currency code")
|
16
|
+
rate: float = Field(description="Exchange rate")
|
17
|
+
timestamp: datetime = Field(default_factory=datetime.now, description="Rate timestamp")
|
18
|
+
|
19
|
+
|
20
|
+
class ConversionRequest(BaseModel):
|
21
|
+
"""Currency conversion request model."""
|
22
|
+
|
23
|
+
amount: float = Field(gt=0, description="Amount to convert")
|
24
|
+
from_currency: str = Field(description="Source currency code")
|
25
|
+
to_currency: str = Field(description="Target currency code")
|
26
|
+
|
27
|
+
|
28
|
+
class ConversionResult(BaseModel):
|
29
|
+
"""Currency conversion result model."""
|
30
|
+
|
31
|
+
request: ConversionRequest = Field(description="Original request")
|
32
|
+
result: float = Field(description="Converted amount")
|
33
|
+
rate: Rate = Field(description="Exchange rate used")
|
34
|
+
path: Optional[str] = Field(default=None, description="Conversion path if indirect")
|
35
|
+
|
36
|
+
|
37
|
+
class YFinanceCurrencies(BaseModel):
|
38
|
+
"""YFinance supported currencies model."""
|
39
|
+
|
40
|
+
fiat: List[str] = Field(description="Supported fiat currencies")
|
41
|
+
|
42
|
+
|
43
|
+
class CoinGeckoCurrencies(BaseModel):
|
44
|
+
"""CoinGecko supported currencies model."""
|
45
|
+
|
46
|
+
crypto: List[str] = Field(description="Supported cryptocurrencies")
|
47
|
+
vs_currencies: List[str] = Field(description="Supported quote currencies")
|
48
|
+
|
49
|
+
|
50
|
+
class SupportedCurrencies(BaseModel):
|
51
|
+
"""All supported currencies model."""
|
52
|
+
|
53
|
+
yfinance: YFinanceCurrencies = Field(description="YFinance currencies")
|
54
|
+
coingecko: CoinGeckoCurrencies = Field(description="CoinGecko currencies")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
"""
|
2
|
+
Database utilities for currency management.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .database_loader import (
|
6
|
+
CurrencyDatabaseLoader,
|
7
|
+
DatabaseLoaderConfig,
|
8
|
+
CoinGeckoCoinInfo,
|
9
|
+
YFinanceCurrencyInfo,
|
10
|
+
CurrencyRateInfo,
|
11
|
+
RateLimiter,
|
12
|
+
create_database_loader,
|
13
|
+
load_currencies_to_database_format
|
14
|
+
)
|
15
|
+
|
16
|
+
__all__ = [
|
17
|
+
'CurrencyDatabaseLoader',
|
18
|
+
'DatabaseLoaderConfig',
|
19
|
+
'CoinGeckoCoinInfo',
|
20
|
+
'YFinanceCurrencyInfo',
|
21
|
+
'CurrencyRateInfo',
|
22
|
+
'RateLimiter',
|
23
|
+
'create_database_loader',
|
24
|
+
'load_currencies_to_database_format'
|
25
|
+
]
|