django-cfg 1.2.22__py3-none-any.whl → 1.2.25__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 (125) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/knowbase/tasks/archive_tasks.py +6 -6
  3. django_cfg/apps/knowbase/tasks/document_processing.py +3 -3
  4. django_cfg/apps/knowbase/tasks/external_data_tasks.py +2 -2
  5. django_cfg/apps/knowbase/tasks/maintenance.py +3 -3
  6. django_cfg/apps/payments/admin/__init__.py +23 -0
  7. django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
  8. django_cfg/apps/payments/admin/balance_admin.py +434 -0
  9. django_cfg/apps/payments/admin/currencies_admin.py +186 -0
  10. django_cfg/apps/payments/admin/filters.py +259 -0
  11. django_cfg/apps/payments/admin/payments_admin.py +142 -0
  12. django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
  13. django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
  14. django_cfg/apps/payments/config/__init__.py +65 -0
  15. django_cfg/apps/payments/config/module.py +70 -0
  16. django_cfg/apps/payments/config/providers.py +115 -0
  17. django_cfg/apps/payments/config/settings.py +96 -0
  18. django_cfg/apps/payments/config/utils.py +52 -0
  19. django_cfg/apps/payments/decorators.py +291 -0
  20. django_cfg/apps/payments/management/__init__.py +3 -0
  21. django_cfg/apps/payments/management/commands/README.md +178 -0
  22. django_cfg/apps/payments/management/commands/__init__.py +3 -0
  23. django_cfg/apps/payments/management/commands/currency_stats.py +323 -0
  24. django_cfg/apps/payments/management/commands/populate_currencies.py +246 -0
  25. django_cfg/apps/payments/management/commands/update_currencies.py +336 -0
  26. django_cfg/apps/payments/managers/currency_manager.py +65 -14
  27. django_cfg/apps/payments/middleware/api_access.py +294 -0
  28. django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
  29. django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
  30. django_cfg/apps/payments/migrations/0001_initial.py +125 -11
  31. django_cfg/apps/payments/models/__init__.py +18 -0
  32. django_cfg/apps/payments/models/api_keys.py +2 -2
  33. django_cfg/apps/payments/models/balance.py +2 -2
  34. django_cfg/apps/payments/models/base.py +16 -0
  35. django_cfg/apps/payments/models/events.py +2 -2
  36. django_cfg/apps/payments/models/payments.py +112 -2
  37. django_cfg/apps/payments/models/subscriptions.py +2 -2
  38. django_cfg/apps/payments/services/__init__.py +64 -7
  39. django_cfg/apps/payments/services/billing/__init__.py +8 -0
  40. django_cfg/apps/payments/services/cache/__init__.py +15 -0
  41. django_cfg/apps/payments/services/cache/base.py +30 -0
  42. django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
  43. django_cfg/apps/payments/services/core/__init__.py +17 -0
  44. django_cfg/apps/payments/services/core/balance_service.py +447 -0
  45. django_cfg/apps/payments/services/core/fallback_service.py +432 -0
  46. django_cfg/apps/payments/services/core/payment_service.py +576 -0
  47. django_cfg/apps/payments/services/core/subscription_service.py +614 -0
  48. django_cfg/apps/payments/services/internal_types.py +297 -0
  49. django_cfg/apps/payments/services/middleware/__init__.py +8 -0
  50. django_cfg/apps/payments/services/monitoring/__init__.py +22 -0
  51. django_cfg/apps/payments/services/monitoring/api_schemas.py +222 -0
  52. django_cfg/apps/payments/services/monitoring/provider_health.py +372 -0
  53. django_cfg/apps/payments/services/providers/__init__.py +22 -0
  54. django_cfg/apps/payments/services/providers/base.py +137 -0
  55. django_cfg/apps/payments/services/providers/cryptapi.py +273 -0
  56. django_cfg/apps/payments/services/providers/cryptomus.py +310 -0
  57. django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
  58. django_cfg/apps/payments/services/providers/registry.py +103 -0
  59. django_cfg/apps/payments/services/security/__init__.py +34 -0
  60. django_cfg/apps/payments/services/security/error_handler.py +637 -0
  61. django_cfg/apps/payments/services/security/payment_notifications.py +342 -0
  62. django_cfg/apps/payments/services/security/webhook_validator.py +475 -0
  63. django_cfg/apps/payments/services/validators/__init__.py +8 -0
  64. django_cfg/apps/payments/signals/__init__.py +13 -0
  65. django_cfg/apps/payments/signals/api_key_signals.py +160 -0
  66. django_cfg/apps/payments/signals/payment_signals.py +128 -0
  67. django_cfg/apps/payments/signals/subscription_signals.py +196 -0
  68. django_cfg/apps/payments/tasks/__init__.py +12 -0
  69. django_cfg/apps/payments/tasks/webhook_processing.py +177 -0
  70. django_cfg/apps/payments/urls.py +5 -5
  71. django_cfg/apps/payments/utils/__init__.py +45 -0
  72. django_cfg/apps/payments/utils/billing_utils.py +342 -0
  73. django_cfg/apps/payments/utils/config_utils.py +245 -0
  74. django_cfg/apps/payments/utils/middleware_utils.py +228 -0
  75. django_cfg/apps/payments/utils/validation_utils.py +94 -0
  76. django_cfg/apps/payments/views/payment_views.py +40 -2
  77. django_cfg/apps/payments/views/webhook_views.py +266 -0
  78. django_cfg/apps/payments/viewsets.py +65 -0
  79. django_cfg/apps/support/signals.py +16 -4
  80. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  81. django_cfg/cli/README.md +2 -2
  82. django_cfg/cli/commands/create_project.py +1 -1
  83. django_cfg/cli/commands/info.py +1 -1
  84. django_cfg/cli/main.py +1 -1
  85. django_cfg/cli/utils.py +5 -5
  86. django_cfg/core/config.py +18 -4
  87. django_cfg/models/payments.py +546 -0
  88. django_cfg/models/revolution.py +1 -1
  89. django_cfg/models/tasks.py +51 -2
  90. django_cfg/modules/base.py +12 -6
  91. django_cfg/modules/django_currency/README.md +104 -269
  92. django_cfg/modules/django_currency/__init__.py +99 -41
  93. django_cfg/modules/django_currency/clients/__init__.py +11 -0
  94. django_cfg/modules/django_currency/clients/coingecko_client.py +257 -0
  95. django_cfg/modules/django_currency/clients/yfinance_client.py +246 -0
  96. django_cfg/modules/django_currency/core/__init__.py +42 -0
  97. django_cfg/modules/django_currency/core/converter.py +169 -0
  98. django_cfg/modules/django_currency/core/exceptions.py +28 -0
  99. django_cfg/modules/django_currency/core/models.py +54 -0
  100. django_cfg/modules/django_currency/database/__init__.py +25 -0
  101. django_cfg/modules/django_currency/database/database_loader.py +507 -0
  102. django_cfg/modules/django_currency/utils/__init__.py +9 -0
  103. django_cfg/modules/django_currency/utils/cache.py +92 -0
  104. django_cfg/modules/django_email.py +42 -4
  105. django_cfg/modules/django_unfold/dashboard.py +20 -0
  106. django_cfg/registry/core.py +10 -0
  107. django_cfg/template_archive/__init__.py +0 -0
  108. django_cfg/template_archive/django_sample.zip +0 -0
  109. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/METADATA +11 -6
  110. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/RECORD +113 -50
  111. django_cfg/apps/agents/examples/__init__.py +0 -3
  112. django_cfg/apps/agents/examples/simple_example.py +0 -161
  113. django_cfg/apps/knowbase/examples/__init__.py +0 -3
  114. django_cfg/apps/knowbase/examples/external_data_usage.py +0 -191
  115. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +0 -199
  116. django_cfg/apps/payments/services/base.py +0 -68
  117. django_cfg/apps/payments/services/nowpayments.py +0 -78
  118. django_cfg/apps/payments/services/providers.py +0 -77
  119. django_cfg/apps/payments/services/redis_service.py +0 -215
  120. django_cfg/modules/django_currency/cache.py +0 -430
  121. django_cfg/modules/django_currency/converter.py +0 -324
  122. django_cfg/modules/django_currency/service.py +0 -277
  123. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/WHEEL +0 -0
  124. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/entry_points.txt +0 -0
  125. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.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
+ ]