diff --git a/openbb_platform/core/openbb_core/app/static/package_builder.py b/openbb_platform/core/openbb_core/app/static/package_builder.py index ceabbcf7fa0c..94bab6247d0a 100644 --- a/openbb_platform/core/openbb_core/app/static/package_builder.py +++ b/openbb_platform/core/openbb_core/app/static/package_builder.py @@ -5,7 +5,6 @@ import inspect import re import shutil -import sys from dataclasses import Field as DCField from functools import partial from inspect import Parameter, _empty, isclass, signature @@ -377,10 +376,7 @@ def build(cls, path: str) -> str: code += "\nfrom typing import TYPE_CHECKING, ForwardRef, List, Dict, Union, Optional, Literal, Any" code += "\nfrom annotated_types import Ge, Le, Gt, Lt" code += "\nfrom warnings import warn, simplefilter" - if sys.version_info < (3, 9): - code += "\nimport typing_extensions" - else: - code += "\nfrom typing_extensions import Annotated, deprecated" + code += "\nfrom typing_extensions import Annotated, deprecated" # code += "\nfrom openbb_core.app.utils import df_to_basemodel" code += "\nfrom openbb_core.app.static.utils.decorators import exception_handler, validate\n" code += "\nfrom openbb_core.app.static.utils.filters import filter_inputs\n" diff --git a/openbb_platform/core/tests/app/static/test_container.py b/openbb_platform/core/tests/app/static/test_container.py index a19b1331b096..72875dfc0340 100644 --- a/openbb_platform/core/tests/app/static/test_container.py +++ b/openbb_platform/core/tests/app/static/test_container.py @@ -89,7 +89,7 @@ def test_container__check_credentials(container): OpenBBError, escape( "Provider fallback failed." - "\n[Providers]\n * 'x' -> not installed, please install openbb-x\n * 'y' -> not installed, please install openbb-y\n * 'z' -> not installed, please install openbb-z" + "\n[Providers]\n * 'x' -> not installed, please install openbb-x\n * 'y' -> not installed, please install openbb-y\n * 'z' -> not installed, please install openbb-z" # noqa: E501 ), ), ], diff --git a/openbb_platform/dev_install.py b/openbb_platform/dev_install.py index 258d41fa9bd3..a50178dbe2fb 100644 --- a/openbb_platform/dev_install.py +++ b/openbb_platform/dev_install.py @@ -2,6 +2,7 @@ # flake8: noqa: S603 +import shutil import subprocess import sys from pathlib import Path @@ -55,6 +56,7 @@ openbb-alpha-vantage = { path = "./providers/alpha_vantage", optional = true, develop = true } openbb-biztoc = { path = "./providers/biztoc", optional = true, develop = true } openbb-cboe = { path = "./providers/cboe", optional = true, develop = true } +openbb-defillama = { path = "./providers/defillama", optional = true, develop = true } openbb-ecb = { path = "./providers/ecb", optional = true, develop = true } openbb-finra = { path = "./providers/finra", optional = true, develop = true } openbb-finviz = { path = "./providers/finviz", optional = true, develop = true } @@ -139,6 +141,9 @@ def install_platform_local(_extras: bool = False): TEMP_PYPROJECT = dumps(pyproject_toml) try: + if shutil.which("poetry") is None: + raise Exception("poetry not found. Please install poetry first.") + with open(PYPROJECT, "w", encoding="utf-8", newline="\n") as f: f.write(TEMP_PYPROJECT) diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_api.py b/openbb_platform/extensions/crypto/integration/test_crypto_api.py index 96a2fec38064..e9b2bab561d0 100644 --- a/openbb_platform/extensions/crypto/integration/test_crypto_api.py +++ b/openbb_platform/extensions/crypto/integration/test_crypto_api.py @@ -122,5 +122,630 @@ def test_crypto_price_historical(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/crypto/price/historical?{query_str}" result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "chain": "ethereum", + "timestamp": "2024-01-01", + }, + { + "provider": "defillama", + "chain": "ethereum", + "timestamp": "2024-01-01T12:12:12", + }, + { + "provider": "defillama", + "chain": "ethereum", + "timestamp": "1729957601", + }, + { + "provider": "defillama", + "chain": "ethereum", + "timestamp": 1729957601, + }, + ], +) +@pytest.mark.integration +def test_crypto_coins_block(params, headers): + """Test the coins block timestamp endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/coins/block?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "token": "coingecko:ethereum", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "1D", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "4H", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "4m", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "1W", + }, + ], +) +@pytest.mark.integration +def test_crypto_coins_current(params, headers): + """Test the coins current price endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/coins/current?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "token": "coingecko:ethereum", + }, + ], +) +@pytest.mark.integration +def test_crypto_coins_first(params, headers): + """Test the coins first record endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/coins/first?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01", + "look_forward": False, + "period": "24h", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01T12:12:12", + "look_forward": False, + "period": "1W", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "1729957601", + "look_forward": True, + "period": "7D", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": 1729957601, + "look_forward": True, + "period": "24m", + }, + ], +) +@pytest.mark.integration +def test_crypto_coins_change(params, headers): + """Test the coins change endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/coins/change?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": "2024-09-01", + "end_date": None, + "span": 0, + "period": "24h", + "search_width": "2h", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": None, + "end_date": "2024-10-01", + "span": 0, + "period": "24h", + "search_width": "2h", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": 1725129000, + "end_date": None, + "span": 10, + "period": "24h", + "search_width": "2h", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": 1725129000, + "end_date": None, + "span": 100, + "period": "1D", + "search_width": "8h", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": None, + "end_date": 1727721000, + "span": 10, + "period": "1W", + "search_width": "1D", + }, + ], +) +@pytest.mark.integration +def test_crypto_coins_chart(params, headers): + """Test the coins chart endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/coins/chart?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01T12:12:12", + "search_width": "1W", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "1729957601", + "search_width": "7D", + }, + { + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": 1729957601, + "search_width": "4m", + }, + ], +) +@pytest.mark.integration +def test_crypto_coins_historical(params, headers): + """Test the coins historical price endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/coins/historical?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + {"provider": "defillama", "chain": None, "all": True}, + {"provider": "defillama", "chain": "ethereum", "all": False}, + {"provider": "defillama", "chain": "ethereum", "all": True}, + ], +) +@pytest.mark.integration +def test_crypto_fees_overview(params, headers): + """Test the fees overview endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/fees/overview?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama", "protocol": "litecoin"}, + ], +) +@pytest.mark.integration +def test_crypto_fees_summary(params, headers): + """Test the fees summary endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/fees/summary?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + {"provider": "defillama", "chain": None, "all": True}, + {"provider": "defillama", "chain": "ethereum", "all": False}, + {"provider": "defillama", "chain": "ethereum", "all": True}, + ], +) +@pytest.mark.integration +def test_crypto_revenue_overview(params, headers): + """Test the revenue overview endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/revenue/overview?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama", "protocol": "litecoin"}, + ], +) +@pytest.mark.integration +def test_crypto_revenue_summary(params, headers): + """Test the revenue summary endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/revenue/summary?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + {"provider": "defillama", "include_prices": True}, + ], +) +@pytest.mark.integration +def test_crypto_stablecoins_list(params, headers): + """Test the stablecoins list endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/stablecoins/list?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + ], +) +@pytest.mark.integration +def test_crypto_stablecoins_current(params, headers): + """Test the stablecoins current endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/stablecoins/current?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + ], +) +@pytest.mark.integration +def test_crypto_stablecoins_historical(params, headers): + """Test the stablecoins historical endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/stablecoins/historical?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama", "stablecoin": "1"}, + ], +) +@pytest.mark.integration +def test_crypto_stablecoins_distribution(params, headers): + """Test the stablecoins distribution endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/stablecoins/distribution?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + {"provider": "defillama", "stablecoin": "1", "chain": None}, + {"provider": "defillama", "stablecoin": None, "chain": "ethereum"}, + {"provider": "defillama", "stablecoin": "1", "chain": "ethereum"}, + ], +) +@pytest.mark.integration +def test_crypto_stablecoins_charts(params, headers): + """Test the stablecoins charts endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/stablecoins/charts?{query_str}" + result = requests.get(url, headers=headers, timeout=30) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + ], +) +@pytest.mark.integration +def test_crypto_tvl_chains(params, headers): + """Test the TVL chains endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/tvl/chains?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + {"provider": "defillama", "symbol": "uniswap", "symbol_type": "protocol"}, + {"provider": "defillama", "symbol": None, "symbol_type": "chain"}, + {"provider": "defillama", "symbol": "ethereum", "symbol_type": "chain"}, + ], +) +@pytest.mark.integration +def test_crypto_tvl_historical(params, headers): + """Test the TVL historical endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/tvl/historical?{query_str}" + result = requests.get(url, headers=headers, timeout=30) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama", "symbol": "uniswap"}, + ], +) +@pytest.mark.integration +def test_crypto_tvl_current(params, headers): + """Test the TVL current endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/tvl/current?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + { + "provider": "defillama", + "chain": None, + "is_options": False, + "all": True, + "volume_type": "premium", + }, + { + "provider": "defillama", + "chain": None, + "is_options": True, + "all": False, + "volume_type": "premium", + }, + { + "provider": "defillama", + "chain": "ethereum", + "is_options": False, + "all": False, + "volume_type": "premium", + }, + { + "provider": "defillama", + "chain": "ethereum", + "is_options": True, + "all": False, + "volume_type": "premium", + }, + { + "provider": "defillama", + "chain": "ethereum", + "is_options": True, + "all": False, + "volume_type": "notional", + }, + ], +) +@pytest.mark.integration +def test_crypto_volumes_overview(params, headers): + """Test the volumes overview endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/volumes/overview?{query_str}" + result = requests.get(url, headers=headers, timeout=30) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "protocol": "uniswap", + "is_options": False, + "volume_type": "premium", + }, + { + "provider": "defillama", + "protocol": "pancakeswap-options", + "is_options": True, + "volume_type": "premium", + }, + { + "provider": "defillama", + "protocol": "uniswap", + "is_options": False, + "volume_type": "notional", + }, + ], +) +@pytest.mark.integration +def test_crypto_volumes_summary(params, headers): + """Test the volumes summary endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/volumes/summary?{query_str}" + result = requests.get(url, headers=headers, timeout=30) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + {"provider": "defillama"}, + ], +) +@pytest.mark.integration +def test_crypto_yields_pools(params, headers): + """Test the yields pools endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/yields/pools?{query_str}" + result = requests.get(url, headers=headers, timeout=30) + + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "defillama", + "pool_id": "747c1d2a-c668-4682-b9f9-296708a3dd90", + }, + ], +) +@pytest.mark.integration +def test_crypto_yields_historical(params, headers): + """Test the yields historical endpoint.""" + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/crypto/yields/historical?{query_str}" + result = requests.get(url, headers=headers, timeout=30) + assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_python.py b/openbb_platform/extensions/crypto/integration/test_crypto_python.py index 7a3073620ef8..36049564c961 100644 --- a/openbb_platform/extensions/crypto/integration/test_crypto_python.py +++ b/openbb_platform/extensions/crypto/integration/test_crypto_python.py @@ -115,3 +115,323 @@ def test_crypto_price_historical(params, obb): assert result assert isinstance(result, OBBject) assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ({"provider": "defillama", "all": True}), + ({"provider": "defillama", "chain": "ethereum"}), + ({"provider": "defillama", "chain": "ethereum", "all": True}), + ], +) +@pytest.mark.integration +def test_fees_overview(params, obb): + """Test fees overview endpoint.""" + result = obb.crypto.fees.overview(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama", "protocol": "litecoin"}), + ], +) +@pytest.mark.integration +def test_fees_summary(params, obb): + """Test fees summary endpoint.""" + result = obb.crypto.fees.summary(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ], +) +@pytest.mark.integration +def test_tvl_chains(params, obb): + """Test TVL chains endpoint.""" + result = obb.crypto.tvl.chains(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ({"provider": "defillama", "symbol": "uniswap"}), + ({"provider": "defillama", "symbol_type": "chain"}), + ({"provider": "defillama", "symbol": "ethereum", "symbol_type": "chain"}), + ], +) +@pytest.mark.integration +def test_tvl_historical(params, obb): + """Test TVL historical endpoint.""" + result = obb.crypto.tvl.historical(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama", "symbol": "uniswap"}), + ], +) +@pytest.mark.integration +def test_tvl_current(params, obb): + """Test TVL current endpoint.""" + result = obb.crypto.tvl.current(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ], +) +@pytest.mark.integration +def test_yields_pools(params, obb): + """Test yields pools endpoint.""" + result = obb.crypto.yields.pools(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ( + { + "provider": "defillama", + "pool_id": "747c1d2a-c668-4682-b9f9-296708a3dd90", + } + ), + ], +) +@pytest.mark.integration +def test_yields_historical(params, obb): + """Test yields historical endpoint.""" + result = obb.crypto.yields.historical(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ({"provider": "defillama", "include_prices": True}), + ], +) +@pytest.mark.integration +def test_stablecoins_list(params, obb): + """Test stablecoins list endpoint.""" + result = obb.crypto.stablecoins.list(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ], +) +@pytest.mark.integration +def test_stablecoins_current(params, obb): + """Test stablecoins current endpoint.""" + result = obb.crypto.stablecoins.current(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ], +) +@pytest.mark.integration +def test_stablecoins_historical(params, obb): + """Test stablecoins historical endpoint.""" + result = obb.crypto.stablecoins.historical(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama", "stablecoin": "1"}), + ], +) +@pytest.mark.integration +def test_stablecoins_distribution(params, obb): + """Test stablecoins distribution endpoint.""" + result = obb.crypto.stablecoins.distribution(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ({"provider": "defillama", "stablecoin": "1"}), + ({"provider": "defillama", "chain": "ethereum"}), + ({"provider": "defillama", "stablecoin": "1", "chain": "ethereum"}), + ], +) +@pytest.mark.integration +def test_stablecoins_charts(params, obb): + """Test stablecoins charts endpoint.""" + result = obb.crypto.stablecoins.charts(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ( + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "1D", + } + ), + ( + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "4H", + } + ), + ( + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "4m", + } + ), + ( + { + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "1W", + } + ), + ], +) +@pytest.mark.integration +def test_coins_current(params, obb): + """Test coins current endpoint.""" + result = obb.crypto.coins.current(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ({"provider": "defillama", "all": True}), + ({"provider": "defillama", "is_options": True}), + ({"provider": "defillama", "chain": "ethereum"}), + ({"provider": "defillama", "chain": "ethereum", "is_options": True}), + ( + { + "provider": "defillama", + "chain": "ethereum", + "is_options": True, + "volume_type": "notional", + } + ), + ], +) +@pytest.mark.integration +def test_volumes_overview(params, obb): + """Test volumes overview endpoint.""" + result = obb.crypto.volumes.overview(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama", "protocol": "litecoin"}), + ( + { + "provider": "defillama", + "protocol": "pancakeswap-options", + "is_options": True, + } + ), + ({"provider": "defillama", "protocol": "uniswap", "volume_type": "notional"}), + ], +) +@pytest.mark.integration +def test_volumes_summary(params, obb): + """Test volumes summary endpoint.""" + result = obb.crypto.volumes.summary(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama"}), + ({"provider": "defillama", "all": True}), + ({"provider": "defillama", "chain": "ethereum"}), + ({"provider": "defillama", "chain": "ethereum", "all": True}), + ], +) +@pytest.mark.integration +def test_revenue_overview(params, obb): + """Test revenue overview endpoint.""" + result = obb.crypto.revenue.overview(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "defillama", "protocol": "litecoin"}), + ], +) +@pytest.mark.integration +def test_revenue_summary(params, obb): + """Test revenue summary endpoint.""" + result = obb.crypto.revenue.summary(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 diff --git a/openbb_platform/extensions/crypto/openbb_crypto/coins/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/coins/__init__.py new file mode 100644 index 000000000000..c4a110d59f30 --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/coins/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto Coins Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/coins/coins_router.py b/openbb_platform/extensions/crypto/openbb_crypto/coins/coins_router.py new file mode 100644 index 000000000000..0d97877cfa8f --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/coins/coins_router.py @@ -0,0 +1,270 @@ +# pylint: disable=W0613:unused-argument +"""Crypto Coins Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/coins") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="BlockTimestamp", + examples=[ + APIEx( + parameters={ + "provider": "defillama", + "chain": "ethereum", + "timestamp": "2024-01-01", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "chain": "ethereum", + "timestamp": "2024-01-01T12:12:12", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "chain": "ethereum", + "timestamp": "1729957601", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "chain": "ethereum", + "timestamp": 1729957601, + } + ), + ], +) +async def block( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the closest block to a timestamp for a coin.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="CoinsCurrent", + examples=[ + APIEx(parameters={"provider": "defillama", "token": "coingecko:ethereum"}), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "1D", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "4H", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "4m", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "search_width": "1W", + } + ), + ], +) +async def current( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the current price of coin.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="CoinsFirst", + examples=[ + APIEx(parameters={"provider": "defillama", "token": "coingecko:ethereum"}), + ], +) +async def first( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the earliest timestamp price record for a coin.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="CoinsChange", + examples=[ + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01T12:12:12", + "period": "1W", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "1729957601", + "period": "7D", + "look_forward": True, + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": 1729957601, + "period": "24m", + "look_forward": True, + } + ), + ], +) +async def change( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the percentage change in pricer over time for a coin.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="CoinsChart", + examples=[ + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": "2024-09-01", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "end_date": "2024-10-01", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": 1725129000, + "span": 10, + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "start_date": 1725129000, + "span": 100, + "period": "1D", + "search_width": "8h", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "end_date": 1727721000, + "span": 10, + "period": "1W", + "search_width": "1D", + } + ), + ], +) +async def chart( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the price at regular intervals for a coin.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="CoinsHistorical", + examples=[ + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "2024-01-01T12:12:12", + "search_width": "1W", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": "1729957601", + "search_width": "7D", + } + ), + APIEx( + parameters={ + "provider": "defillama", + "token": "coingecko:ethereum", + "timestamp": 1729957601, + "search_width": "4m", + } + ), + ], +) +async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the historical price for a coin.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/extensions/crypto/openbb_crypto/crypto_router.py b/openbb_platform/extensions/crypto/openbb_crypto/crypto_router.py index bc81b7db88c2..6b554d8ddee2 100644 --- a/openbb_platform/extensions/crypto/openbb_crypto/crypto_router.py +++ b/openbb_platform/extensions/crypto/openbb_crypto/crypto_router.py @@ -11,10 +11,24 @@ from openbb_core.app.query import Query from openbb_core.app.router import Router +from openbb_crypto.coins.coins_router import router as coins_router +from openbb_crypto.fees.fees_router import router as fees_router from openbb_crypto.price.price_router import router as price_router +from openbb_crypto.revenue.revenue_router import router as revenue_router +from openbb_crypto.stablecoins.stablecoins_router import router as stablecoins_router +from openbb_crypto.tvl.tvl_router import router as tvl_router +from openbb_crypto.volumes.volumes_router import router as volumes_router +from openbb_crypto.yields.yields_router import router as yields_router router = Router(prefix="", description="Cryptocurrency market data.") router.include_router(price_router) +router.include_router(tvl_router) +router.include_router(yields_router) +router.include_router(fees_router) +router.include_router(revenue_router) +router.include_router(volumes_router) +router.include_router(coins_router) +router.include_router(stablecoins_router) # pylint: disable=unused-argument diff --git a/openbb_platform/extensions/crypto/openbb_crypto/fees/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/fees/__init__.py new file mode 100644 index 000000000000..fafc9f5e650c --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/fees/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto Fees Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/fees/fees_router.py b/openbb_platform/extensions/crypto/openbb_crypto/fees/fees_router.py new file mode 100644 index 000000000000..8c6b1b1410fa --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/fees/fees_router.py @@ -0,0 +1,51 @@ +# pylint: disable=W0613:unused-argument +"""Crypto Fees Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/fees") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="FeesOverview", + examples=[ + APIEx(parameters={"provider": "defillama"}), + APIEx(parameters={"provider": "defillama", "all": True}), + APIEx(parameters={"provider": "defillama", "chain": "ethereum"}), + APIEx(parameters={"provider": "defillama", "chain": "solana", "all": True}), + ], +) +async def overview( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the the latest overview of fees.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="FeesSummary", + examples=[ + APIEx(parameters={"provider": "defillama", "protocol": "litecoin"}), + ], +) +async def summary( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get summary of protocol fees with historical data.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/extensions/crypto/openbb_crypto/revenue/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/revenue/__init__.py new file mode 100644 index 000000000000..5abeac9623da --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/revenue/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto Revenue Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/revenue/revenue_router.py b/openbb_platform/extensions/crypto/openbb_crypto/revenue/revenue_router.py new file mode 100644 index 000000000000..b477974919af --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/revenue/revenue_router.py @@ -0,0 +1,51 @@ +# pylint: disable=W0613:unused-argument +"""Crypto Revenue Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/revenue") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="RevenueOverview", + examples=[ + APIEx(parameters={"provider": "defillama"}), + APIEx(parameters={"provider": "defillama", "all": True}), + APIEx(parameters={"provider": "defillama", "chain": "ethereum"}), + APIEx(parameters={"provider": "defillama", "chain": "solana", "all": True}), + ], +) +async def overview( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the the latest overview of revenue.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="RevenueSummary", + examples=[ + APIEx(parameters={"provider": "defillama", "protocol": "litecoin"}), + ], +) +async def summary( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get summary of protocol revenue with historical data.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/extensions/crypto/openbb_crypto/stablecoins/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/stablecoins/__init__.py new file mode 100644 index 000000000000..6d26a51ec236 --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/stablecoins/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto Stablecoins Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/stablecoins/stablecoins_router.py b/openbb_platform/extensions/crypto/openbb_crypto/stablecoins/stablecoins_router.py new file mode 100644 index 000000000000..9dd9b7a5dbf0 --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/stablecoins/stablecoins_router.py @@ -0,0 +1,102 @@ +# pylint: disable=W0613:unused-argument +"""Crypto Stablecoins Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/stablecoins") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="StablecoinsList", + examples=[ + APIEx(parameters={"provider": "defillama"}), + APIEx(parameters={"provider": "defillama", "include_prices": True}), + ], +) +async def list( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get all stablecoins along with their circulating amounts.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="StablecoinsCurrent", + examples=[ + APIEx(parameters={"provider": "defillama"}), + ], +) +async def current( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get current market cap sum of all stablecoins on each chain.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="StablecoinsHistorical", + examples=[ + APIEx(parameters={"provider": "defillama"}), + ], +) +async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get historical prices of all stablecoins.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="StablecoinsDistribution", + examples=[ + APIEx(parameters={"provider": "defillama", "stablecoin": "1"}), + ], +) +async def distribution( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get historical prices of all stablecoins.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="StablecoinsCharts", + examples=[ + APIEx(parameters={"provider": "defillama"}), + APIEx(parameters={"provider": "defillama", "stablecoin": "1"}), + APIEx(parameters={"provider": "defillama", "chain": "ethereum"}), + APIEx( + parameters={"provider": "defillama", "stablecoin": "1", "chain": "ethereum"} + ), + ], +) +async def charts( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get historical market cap sum of all stablecoins and also in a chain.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/extensions/crypto/openbb_crypto/tvl/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/tvl/__init__.py new file mode 100644 index 000000000000..1895c7e109ed --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/tvl/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto TVL Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/tvl/tvl_router.py b/openbb_platform/extensions/crypto/openbb_crypto/tvl/tvl_router.py new file mode 100644 index 000000000000..14701468527a --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/tvl/tvl_router.py @@ -0,0 +1,73 @@ +# pylint: disable=W0613:unused-argument +"""Crypto TVL Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/tvl") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="TvlChains", + examples=[ + APIEx(parameters={"provider": "defillama"}), + ], +) +async def chains( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the current TVL for all chains.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="TvlHistorical", + examples=[ + APIEx(parameters={"provider": "defillama"}), + APIEx(parameters={"provider": "defillama", "symbol": "uniswap"}), + APIEx(parameters={"provider": "defillama", "symbol_type": "chain"}), + APIEx( + parameters={ + "provider": "defillama", + "symbol": "ethereum", + "symbol_type": "chain", + } + ), + ], +) +async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the historical TVL for a given protocol, chain or all chains.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="TvlCurrent", + examples=[ + APIEx(parameters={"provider": "defillama", "symbol": "uniswap"}), + ], +) +async def current( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the current TVl of a given protocol.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/extensions/crypto/openbb_crypto/volumes/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/volumes/__init__.py new file mode 100644 index 000000000000..37ce8ca238f4 --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/volumes/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto Volumes Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/volumes/volumes_router.py b/openbb_platform/extensions/crypto/openbb_crypto/volumes/volumes_router.py new file mode 100644 index 000000000000..f9333b6f3858 --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/volumes/volumes_router.py @@ -0,0 +1,80 @@ +# pylint: disable=W0613:unused-argument +"""Crypto Volumes Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/volumes") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="VolumesOverview", + examples=[ + APIEx(parameters={"provider": "defillama"}), + APIEx(parameters={"provider": "defillama", "all": True}), + APIEx(parameters={"provider": "defillama", "is_options": True}), + APIEx(parameters={"provider": "defillama", "chain": "ethereum"}), + APIEx( + parameters={ + "provider": "defillama", + "chain": "ethereum", + "is_options": True, + } + ), + APIEx( + parameters={ + "provider": "defillama", + "chain": "ethereum", + "is_options": True, + "volume_type": "notional", + } + ), + ], +) +async def overview( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the the latest overview of dexs or option dexs.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="VolumesSummary", + examples=[ + APIEx(parameters={"provider": "defillama", "protocol": "litecoin"}), + APIEx( + parameters={ + "provider": "defillama", + "protocol": "pancakeswap-options", + "is_options": True, + } + ), + APIEx( + parameters={ + "provider": "defillama", + "protocol": "uniswap", + "volume_type": "notional", + } + ), + ], +) +async def summary( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the the latest overview of dexs or option dexs.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/extensions/crypto/openbb_crypto/yields/__init__.py b/openbb_platform/extensions/crypto/openbb_crypto/yields/__init__.py new file mode 100644 index 000000000000..5e486ff1c001 --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/yields/__init__.py @@ -0,0 +1 @@ +"""OpenBB Crypto Yields Router.""" diff --git a/openbb_platform/extensions/crypto/openbb_crypto/yields/yields_router.py b/openbb_platform/extensions/crypto/openbb_crypto/yields/yields_router.py new file mode 100644 index 000000000000..d99e4304d2bc --- /dev/null +++ b/openbb_platform/extensions/crypto/openbb_crypto/yields/yields_router.py @@ -0,0 +1,53 @@ +# pylint: disable=W0613:unused-argument +"""Crypto Yields Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router + +router = Router(prefix="/yields") + + +# pylint: disable=unused-argument,line-too-long +@router.command( + model="YieldsPools", + examples=[ + APIEx(parameters={"provider": "defillama"}), + ], +) +async def pools( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the the latest data for all pools.""" + return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="YieldsHistorical", + examples=[ + APIEx( + parameters={ + "provider": "defillama", + "pool_id": "747c1d2a-c668-4682-b9f9-296708a3dd90", + } + ), + ], +) +async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the historical TVL for a given protocol, chain or all chains.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/providers/defillama/README.md b/openbb_platform/providers/defillama/README.md new file mode 100644 index 000000000000..10f2fd742e88 --- /dev/null +++ b/openbb_platform/providers/defillama/README.md @@ -0,0 +1,13 @@ +# OpenBB DeFiLlama Provider + +This extension integrates the [DeFiLlama](http://defillama.com) data provider into the OpenBB Platform. + +## Installation + +To install the extension: + +```bash +pip install openbb-defillama +``` + +Documentation available [here](https://docs.openbb.co/platform/developer_guide/contributing). diff --git a/openbb_platform/providers/defillama/__init__.py b/openbb_platform/providers/defillama/__init__.py new file mode 100644 index 000000000000..811827f1eab2 --- /dev/null +++ b/openbb_platform/providers/defillama/__init__.py @@ -0,0 +1 @@ +"""DeFiLlama Provider.""" diff --git a/openbb_platform/providers/defillama/openbb_defillama/__init__.py b/openbb_platform/providers/defillama/openbb_defillama/__init__.py new file mode 100644 index 000000000000..f03287ca056b --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/__init__.py @@ -0,0 +1,65 @@ +"""DeFiLlama provider module.""" + +from openbb_core.provider.abstract.provider import Provider +from openbb_defillama.models.coins_block_timestamp import ( + DeFiLlamaCoinsBlockTimestampFetcher, +) +from openbb_defillama.models.coins_change import DeFiLlamaCoinsChangeFetcher +from openbb_defillama.models.coins_chart import DeFiLlamaCoinsChartFetcher +from openbb_defillama.models.coins_current import DeFiLlamaCoinsCurrentFetcher +from openbb_defillama.models.coins_first import DeFiLlamaCoinsFirstFetcher +from openbb_defillama.models.coins_historical import DeFiLlamaCoinsHistoricalFetcher +from openbb_defillama.models.fees_overview import DeFiLlamaFeesOverviewFetcher +from openbb_defillama.models.fees_summary import DeFiLlamaFeesSummaryFetcher +from openbb_defillama.models.revenue_overview import DeFiLlamaRevenueOverviewFetcher +from openbb_defillama.models.revenue_summary import DeFiLlamaRevenueSummaryFetcher +from openbb_defillama.models.stablecoins_charts import DeFiLlamaStablecoinsChartsFetcher +from openbb_defillama.models.stablecoins_current import ( + DeFiLlamaStablecoinsCurrentFetcher, +) +from openbb_defillama.models.stablecoins_distribution import ( + DeFiLlamaStablecoinsDistributionFetcher, +) +from openbb_defillama.models.stablecoins_historical import ( + DeFiLlamaStablecoinsHistoricalFetcher, +) +from openbb_defillama.models.stablecoins_list import DeFiLlamaStablecoinsListFetcher +from openbb_defillama.models.tvl_chains import DeFiLlamaTvlChainsFetcher +from openbb_defillama.models.tvl_current import DeFiLlamaTvlCurrentFetcher +from openbb_defillama.models.tvl_historical import DeFiLlamaTvlHistoricalFetcher +from openbb_defillama.models.volumes_overview import DeFiLlamaVolumesOverviewFetcher +from openbb_defillama.models.volumes_summary import DeFiLlamaVolumesSummaryFetcher +from openbb_defillama.models.yields_historical import DeFiLlamaYieldsHistoricalFetcher +from openbb_defillama.models.yields_pools import DeFiLlamaYieldsPoolsFetcher + +defillama_provider = Provider( + name="defillama", + website="https://defillama.com", + description="DeFiLlama is the largest TVL aggregator for DeFi (Decentralized Finance).", + credentials=None, + fetcher_dict={ + "BlockTimestamp": DeFiLlamaCoinsBlockTimestampFetcher, + "CoinsChange": DeFiLlamaCoinsChangeFetcher, + "CoinsChart": DeFiLlamaCoinsChartFetcher, + "CoinsCurrent": DeFiLlamaCoinsCurrentFetcher, + "CoinsFirst": DeFiLlamaCoinsFirstFetcher, + "CoinsHistorical": DeFiLlamaCoinsHistoricalFetcher, + "FeesOverview": DeFiLlamaFeesOverviewFetcher, + "FeesSummary": DeFiLlamaFeesSummaryFetcher, + "RevenueOverview": DeFiLlamaRevenueOverviewFetcher, + "RevenueSummary": DeFiLlamaRevenueSummaryFetcher, + "StablecoinsCharts": DeFiLlamaStablecoinsChartsFetcher, + "StablecoinsCurrent": DeFiLlamaStablecoinsCurrentFetcher, + "StablecoinsDistribution": DeFiLlamaStablecoinsDistributionFetcher, + "StablecoinsHistorical": DeFiLlamaStablecoinsHistoricalFetcher, + "StablecoinsList": DeFiLlamaStablecoinsListFetcher, + "TvlChains": DeFiLlamaTvlChainsFetcher, + "TvlCurrent": DeFiLlamaTvlCurrentFetcher, + "TvlHistorical": DeFiLlamaTvlHistoricalFetcher, + "VolumesOverview": DeFiLlamaVolumesOverviewFetcher, + "VolumesSummary": DeFiLlamaVolumesSummaryFetcher, + "YieldsHistorical": DeFiLlamaYieldsHistoricalFetcher, + "YieldsPools": DeFiLlamaYieldsPoolsFetcher, + }, + repr_name="DeFiLlama", +) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/__init__.py b/openbb_platform/providers/defillama/openbb_defillama/models/__init__.py new file mode 100644 index 000000000000..065ebe02ffb3 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/__init__.py @@ -0,0 +1 @@ +"""DeFiLlama models directory.""" diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/coins_block_timestamp.py b/openbb_platform/providers/defillama/openbb_defillama/models/coins_block_timestamp.py new file mode 100644 index 000000000000..2e7b39f1730b --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/coins_block_timestamp.py @@ -0,0 +1,77 @@ +"""DeFiLlama Coins Block Timestamp Model.""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union + +from defillama import coins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaCoinsBlockTimestampQueryParams(QueryParams): + """DeFiLlama Coins Block Timestamp Query. + + Source: https://defillama.com/docs/api + """ + + chain: str = Field(description="The chain to fetch the block timestamp for.") + timestamp: Union[int, str, datetime] = Field( + description="The timestamp to fetch the block for. If a string is provided, it should follow the 'day-first' format." # noqa: E501 + ) + + @field_validator("timestamp", mode="before") + def validate_timestamp(cls, v): + if isinstance(v, (int, float)) or (isinstance(v, str) and v.isdigit()): + return int(v) + elif isinstance(v, str): + try: + return int(datetime.fromisoformat(v).timestamp()) + except ValueError as e: + raise ValueError(f"Invalid timestamp format: {v}") from e + elif isinstance(v, datetime): + return int(v.timestamp()) + else: + raise ValueError(f"Invalid timestamp type: {type(v)}") + + +class DeFiLlamaCoinsBlockTimestampData(Data): + """DeFiLlama Coins Block Timestamp Data.""" + + height: int = Field(description="The block number.") + timestamp: datetime = Field(description="The timestamp of the block.") + + +class DeFiLlamaCoinsBlockTimestampFetcher( + Fetcher[DeFiLlamaCoinsBlockTimestampQueryParams, DeFiLlamaCoinsBlockTimestampData] +): + """DeFiLlama Coins Block Timestamp Fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> DeFiLlamaCoinsBlockTimestampQueryParams: + """Transform query parameters""" + return DeFiLlamaCoinsBlockTimestampQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaCoinsBlockTimestampQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + return coins.get_nearest_block( + chain=query.chain, + timestamp=query.timestamp, + ) + + @staticmethod + def transform_data( + query: DeFiLlamaCoinsBlockTimestampQueryParams, + data: Dict[str, Any], + **kwargs: Any, + ) -> DeFiLlamaCoinsBlockTimestampData: + """Transform the data into the desired format.""" + return DeFiLlamaCoinsBlockTimestampData.model_validate(data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/coins_change.py b/openbb_platform/providers/defillama/openbb_defillama/models/coins_change.py new file mode 100644 index 000000000000..75a3b09b3bf6 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/coins_change.py @@ -0,0 +1,107 @@ +"""DeFiLlama Coins Change Model.""" + +import re +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from defillama import coins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaCoinsChangeQueryParams(QueryParams): + """DeFiLlama Coins Change Query. + + Source: https://defillama.com/docs/api + """ + + token: str = Field(description="The token to fetch data for.") + timestamp: Union[int, str, datetime] = Field( + description="The timestamp to fetch the block for. If a string is provided, it should follow the 'day-first' format." # noqa: E501 + ) + look_forward: bool = Field( + default=False, + description="Whether to look after the provided timestamp or not.", + ) + period: str = Field( + default="24h", + description="Time range to get the current price for. Acceptable format: W, D, H, or M (case insensitive).", # noqa: E501 + ) + + @field_validator("timestamp", mode="before") + def validate_timestamp(cls, v): + if isinstance(v, (int, float)) or (isinstance(v, str) and v.isdigit()): + return int(v) + elif isinstance(v, str): + try: + return int(datetime.fromisoformat(v).timestamp()) + except ValueError as e: + raise ValueError(f"Invalid timestamp format: {v}") from e + elif isinstance(v, datetime): + return int(v.timestamp()) + else: + raise ValueError(f"Invalid timestamp type: {type(v)}") + + @field_validator("period", mode="before") + def validate_search_width(cls, v): + pattern = re.compile(r"^(\d+)[WwDdHhMm]$") + if not pattern.match(v): + raise ValueError( + "search_width must be in the format W, D, H, or M (case insensitive)" + ) + return v.lower() + + +class DeFiLlamaCoinsChangeData(Data): + """DeFiLlama Coins Change Data.""" + + chain: str = Field(description="The chain the token is on.") + address: str = Field(description="The address of the token.") + change: float = Field(description="The percentage change in price.") + + +class DeFiLlamaCoinsChangeFetcher( + Fetcher[DeFiLlamaCoinsChangeQueryParams, List[DeFiLlamaCoinsChangeData]] +): + """DeFiLlama Coins Change Fetcher""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaCoinsChangeQueryParams: + """Transform query parameters.""" + return DeFiLlamaCoinsChangeQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaCoinsChangeQueryParams, + credentials: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + token = [{query.token.split(":")[0]: query.token.split(":")[1]}] + return coins.get_percentage( + tokens=token, + timestamp=query.timestamp, + look_forward=query.look_forward, + period=query.period, + ) + + @staticmethod + def transform_data( + query: DeFiLlamaCoinsChangeQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> List[DeFiLlamaCoinsChangeData]: + """Transform the data into the desired format.""" + + transformed_data: List[Dict[str, Any]] = [] + + transformed_data = [ + { + "chain": k.split(":")[0], + "address": k.split(":")[1], + "change": v, + } + for k, v in data.items() + ] + + return [DeFiLlamaCoinsChangeData.model_validate(d) for d in transformed_data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/coins_chart.py b/openbb_platform/providers/defillama/openbb_defillama/models/coins_chart.py new file mode 100644 index 000000000000..17f69c758498 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/coins_chart.py @@ -0,0 +1,152 @@ +"""DeFiLlama Coins Chart Model.""" + +import re +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_defillama.utils.helpers import get_data +from pydantic import Field, field_validator, model_validator + + +class DeFiLlamaCoinsChartQuery(QueryParams): + """DeFiLlama Coins Chart Query. + + Source: https://defillama.com/docs/api + """ + + token: str = Field(description="The token to get the chart for.") + start_date: Optional[Union[int, str, datetime]] = Field( + default=None, + description="The start date to get the chart for. If a string is provided, it should follow the 'day-first' format.", # noqa: E501 + ) + end_date: Optional[Union[int, str, datetime]] = Field( + default=None, + description="The end date to get the chart for. If a string is provided, it should follow the 'day-first' format.", # noqa: E501 + ) + span: int = Field(default=0, description="The number of data points to return.") + period: str = Field( + default="24h", + description="Duration between data points. Acceptable format: W, D, H, or M (case insensitive).", # noqa: E501 + ) + search_width: str = Field( + default="2h", + description="Time range to get the current price for. Acceptable format: W, D, H, or M (case insensitive).", # noqa: E501 + ) + + @field_validator("start_date", "end_date", mode="before") + def validate_timestamp(cls, v): + if isinstance(v, (int, float)) or (isinstance(v, str) and v.isdigit()): + return int(v) + elif isinstance(v, str): + try: + return int(datetime.fromisoformat(v).timestamp()) + except ValueError as e: + raise ValueError(f"Invalid timestamp format: {v}") from e + elif isinstance(v, datetime): + return int(v.timestamp()) + else: + raise ValueError(f"Invalid timestamp type: {type(v)}") + + @field_validator("period", "search_width", mode="before") + def validate_search_width(cls, v): + pattern = re.compile(r"^(\d+)[WwDdHhMm]$") + if not pattern.match(v): + raise ValueError( + "search_width must be in the format W, D, H, or M (case insensitive)" + ) + return v.lower() + + @model_validator(mode="after") + def check_dates(self): + if self.start_date is not None and self.end_date is not None: + raise ValueError("Only one of start_date or end_date should be provided") + if self.start_date is None and self.end_date is None: + raise ValueError("Either start_date or end_date must be provided") + return self + + +class DeFiLlamaCoinsPricesData(Data): + """DeFiLlama Coins Prices Data.""" + + timestamp: datetime = Field(description="The timestamp of the data.") + price: float = Field(description="The price of the token.") + + @field_validator("timestamp", mode="before") + def validate_timestamp(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaCoinsChartData(Data): + """DeFiLlama Coins Chart Data.""" + + chain: str = Field(description="The chain the token is on.") + address: str = Field(description="The address of the token.") + symbol: str = Field(description="The symbol of the token.") + confidence: float = Field(description="The confidence of the data.") + decimals: Optional[int] = Field( + default=None, + description="Smallest unit of the token that can be traded or transferred", + ) + prices: List[DeFiLlamaCoinsPricesData] = Field( + description="The prices of the token." + ) + + @field_validator("symbol", mode="before") + def validate_symbol(cls, v): + return v.upper() + + @field_validator("prices", mode="before") + def validate_prices(cls, v): + return [DeFiLlamaCoinsPricesData.model_validate(price) for price in v] + + +class DeFiLlamaCoinsChartFetcher( + Fetcher[DeFiLlamaCoinsChartQuery, List[DeFiLlamaCoinsChartData]] +): + """DeFiLlama Coins Chart Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaCoinsChartQuery: + """Transform query parameters.""" + return DeFiLlamaCoinsChartQuery(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaCoinsChartQuery, + credentials: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + date_params = ( + f"start={query.start_date}" if query.start_date else f"end={query.end_date}" + ) + url = ( + f"https://coins.llama.fi/chart/{query.token}?" + f"{date_params}&span={query.span}&period={query.period}&searchWidth={query.search_width}" + ) + data = await get_data(url) + return data.get("coins", {}) + + @staticmethod + def transform_data( + query: DeFiLlamaCoinsChartQuery, data: Dict[str, Any], **kwargs: Any + ) -> List[DeFiLlamaCoinsChartData]: + """Transform the data into the desired format.""" + transformed_data: List[Dict[str, Any]] = [] + + transformed_data = [ + { + "chain": k.split(":")[0], + "address": k.split(":")[1], + "symbol": v["symbol"], + "confidence": v["confidence"], + "decimals": v.get("decimals", None), + "prices": v["prices"], + } + for k, v in data.items() + ] + + return [DeFiLlamaCoinsChartData.model_validate(d) for d in transformed_data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/coins_current.py b/openbb_platform/providers/defillama/openbb_defillama/models/coins_current.py new file mode 100644 index 000000000000..fb5c1caf5d60 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/coins_current.py @@ -0,0 +1,100 @@ +"""DeFiLlama Coins Current Model.""" + +import re +from datetime import datetime +from typing import Any, Dict, List, Optional + +from defillama import coins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaCoinsCurrentQueryParams(QueryParams): + """DeFiLlama Coins Current Query. + + Source: https://defillama.com/docs/api + """ + + token: str = Field( + description="Token to fetch data for. Must be in chain:address format." + ) + search_width: str = Field( + default="6h", + description="Time range to get the current price for. Acceptable format: W, D, H, or M (case insensitive).", # noqa: E501 + ) + + @field_validator("search_width", mode="before") + def validate_search_width(cls, v): + pattern = re.compile(r"^(\d+)[WwDdHhMm]$") + if not pattern.match(v): + raise ValueError( + "search_width must be in the format W, D, H, or M (case insensitive)" + ) + return v.lower() + + +class DeFiLlamaCoinsCurrentData(Data): + """DeFiLlama Coins Current Data.""" + + chain: str = Field(description="The chain the token is on.") + address: str = Field(description="The address of the token.") + price: float = Field(description="The current price of the token.") + symbol: str = Field(description="The token symbol.") + timestamp: datetime = Field(description="The timestamp of the data.") + confidence: float = Field(description="The confidence of the data.") + + @field_validator("symbol", mode="before") + def validate_symbol(cls, v): + return v.upper() + + @field_validator("timestamp", mode="before") + def validate_timestamp(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaCoinsCurrentFetcher( + Fetcher[DeFiLlamaCoinsCurrentQueryParams, List[DeFiLlamaCoinsCurrentData]] +): + """DeFiLlama Coins Current Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaCoinsCurrentQueryParams: + """Transform query parameters""" + return DeFiLlamaCoinsCurrentQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaCoinsCurrentQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + token = [{query.token.split(":")[0]: query.token.split(":")[1]}] + + return coins.get_current_prices( + tokens=token, + search_width=query.search_width, + ) + + @staticmethod + def transform_data( + query: DeFiLlamaCoinsCurrentQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> List[DeFiLlamaCoinsCurrentData]: + """Transform the data into the desired format.""" + transformed_data: List[Dict[str, Any]] = [] + + transformed_data = [ + { + "chain": k.split(":")[0], + "address": k.split(":")[1], + "price": v["price"], + "symbol": v["symbol"], + "timestamp": v["timestamp"], + "confidence": v["confidence"], + } + for k, v in data.items() + ] + + return [DeFiLlamaCoinsCurrentData.model_validate(d) for d in transformed_data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/coins_first.py b/openbb_platform/providers/defillama/openbb_defillama/models/coins_first.py new file mode 100644 index 000000000000..b790e4f1487e --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/coins_first.py @@ -0,0 +1,80 @@ +"""DeFiLlama Coins First Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from defillama import coins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaCoinsFirstQueryParams(QueryParams): + """DeFiLlama Coins First Query. + + Source: https://defillama.com/docs/api + """ + + token: str = Field( + description="Token to fetch data for. Must be in chain:address format." + ) + + +class DeFiLlamaCoinsFirstData(Data): + """DeFiLlama Coins First Data.""" + + chain: str = Field(description="The chain the token is on.") + address: str = Field(description="The address of the token.") + symbol: str = Field(description="The symbol of the token.") + price: float = Field(description="The price of the token.") + timestamp: datetime = Field(description="The timestamp of the first data point.") + + @field_validator("symbol", mode="before") + def validate_symbol(cls, v): + return v.upper() + + @field_validator("timestamp", mode="before") + def validate_timestamp(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaCoinsFirstFetcher( + Fetcher[DeFiLlamaCoinsFirstQueryParams, List[DeFiLlamaCoinsFirstData]] +): + """DeFiLlama Coins First Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaCoinsFirstQueryParams: + """Transform query parameters.""" + return DeFiLlamaCoinsFirstQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaCoinsFirstQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + token = [{query.token.split(":")[0]: query.token.split(":")[1]}] + return coins.get_first_prices(tokens=token) + + @staticmethod + def transform_data( + query: DeFiLlamaCoinsFirstQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> List[DeFiLlamaCoinsFirstData]: + """Transform the data into the desired format.""" + + transformed_data: List[Dict[str, Any]] = [] + + transformed_data = [ + { + "chain": k.split(":")[0], + "address": k.split(":")[1], + "price": v["price"], + "symbol": v["symbol"], + "timestamp": v["timestamp"], + } + for k, v in data.items() + ] + return [DeFiLlamaCoinsFirstData.model_validate(d) for d in transformed_data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/coins_historical.py b/openbb_platform/providers/defillama/openbb_defillama/models/coins_historical.py new file mode 100644 index 000000000000..4daf48597169 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/coins_historical.py @@ -0,0 +1,114 @@ +"""DeFiLlama Coins Historical Model.""" + +import re +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from defillama import coins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaCoinsHistoricalQueryParams(QueryParams): + """DeFiLlama Coins Historical Query. + + Source: https://defillama.com/docs/api + """ + + token: str = Field(description="The token to get historical data for.") + timestamp: Union[int, str, datetime] = Field( + description="The timestamp to get historical data for." + ) + search_width: str = Field( + default="6h", + description="Time range to get the current price for. Acceptable format: W, D, H, or M (case insensitive).", # noqa: E501 + ) + + @field_validator("timestamp", mode="before") + def validate_timestamp(cls, v): + if isinstance(v, (int, float)) or (isinstance(v, str) and v.isdigit()): + return int(v) + elif isinstance(v, str): + try: + return int(datetime.fromisoformat(v).timestamp()) + except ValueError as e: + raise ValueError(f"Invalid timestamp format: {v}") from e + elif isinstance(v, datetime): + return int(v.timestamp()) + else: + raise ValueError(f"Invalid timestamp type: {type(v)}") + + @field_validator("search_width", mode="before") + def validate_search_width(cls, v): + pattern = re.compile(r"^(\d+)[WwDdHhMm]$") + if not pattern.match(v): + raise ValueError( + "search_width must be in the format W, D, H, or M (case insensitive)" + ) + return v.lower() + + +class DeFiLlamaCoinsHistoricalData(Data): + """DeFiLlama Coins Historical Data.""" + + chain: str = Field(description="The chain the token is on.") + address: str = Field(description="The address of the token.") + price: float = Field(description="The price of the token.") + symbol: str = Field(description="The symbol of the token.") + timestamp: datetime = Field(description="The timestamp of the data.") + decimals: Optional[int] = Field( + default=None, + description="Smallest unit of the token that can be traded or transferred", + ) + + @field_validator("symbol", mode="before") + def validate_symbol(cls, v): + return v.upper() + + +class DeFiLlamaCoinsHistoricalFetcher( + Fetcher[DeFiLlamaCoinsHistoricalQueryParams, List[DeFiLlamaCoinsHistoricalData]] +): + """DeFiLlama Coins Historical Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaCoinsHistoricalQueryParams: + """Transform query parameters.""" + return DeFiLlamaCoinsHistoricalQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaCoinsHistoricalQueryParams, + credentials: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + token = [{query.token.split(":")[0]: query.token.split(":")[1]}] + return coins.get_historical_prices( + tokens=token, timestamp=query.timestamp, search_width=query.search_width + ) + + @staticmethod + def transform_data( + query: DeFiLlamaCoinsHistoricalQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> List[DeFiLlamaCoinsHistoricalData]: + """Transform the data into the desired format.""" + transformed_data: List[Dict[str, Any]] = [] + + transformed_data = [ + { + "chain": k.split(":")[0], + "address": k.split(":")[1], + "price": v["price"], + "symbol": v["symbol"], + "timestamp": v["timestamp"], + "decimals": v.get("decimals", None), + } + for k, v in data.items() + ] + + return [ + DeFiLlamaCoinsHistoricalData.model_validate(d) for d in transformed_data + ] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/fees_overview.py b/openbb_platform/providers/defillama/openbb_defillama/models/fees_overview.py new file mode 100644 index 000000000000..80eabe5e3ec5 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/fees_overview.py @@ -0,0 +1,228 @@ +"""DeFiLlama Fees Overview Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from defillama import fees_revenue +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaFeesOverviewQueryParams(QueryParams): + """DeFiLlama Fees Overview Query. + + Source: https://defillama.com/docs/api + """ + + chain: Optional[str] = Field( + default=None, description="The chain to fetch data for." + ) + all: bool = Field( + default=False, description="Whether to fetch details of all protocols." + ) + # NOTE: Disabled since the API does not return any data + # aggregate: Literal["daily", "total"] = Field( + # default="daily", + # description="Whether to fetch daily or total aggregate data.", + # ) + + +class DeFiLlamaChainsBreakdownData(Data): + """DeFiLlama Chains Breakdown Data.""" + + date: datetime = Field(description="The date of the data.") + chains: Dict[str, int] = Field(description="The chains data.") + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaProtocolsData(Data): + """DeFiLlama Protocols Data.""" + + total_24h: Optional[int] = Field( + default=None, description="Total value in the last 24 hours", alias="total24h" + ) + total_48h_to_24h: Optional[int] = Field( + default=None, + description="Total value from 48 to 24 hours ago", + alias="total48hto24h", + ) + total_7d: Optional[int] = Field( + default=None, description="Total value in the last 7 days", alias="total7d" + ) + total_14d_to_7d: Optional[int] = Field( + default=None, + description="Total value from 14 to 7 days ago", + alias="total14dto7d", + ) + total_60d_to_30d: Optional[int] = Field( + default=None, + description="Total value from 60 to 30 days ago", + alias="total60dto30d", + ) + total_30d: Optional[int] = Field( + default=None, description="Total value in the last 30 days", alias="total30d" + ) + total_1y: Optional[int] = Field( + default=None, description="Total value in the last year", alias="total1y" + ) + total_all_time: Optional[float] = Field( + default=None, description="Total value of all time", alias="totalAllTime" + ) + average_1y: Optional[float] = Field( + default=None, description="Average value over the last year", alias="average1y" + ) + change_1d: Optional[float] = Field( + default=None, description="Percentage change in the last day" + ) + change_7d: Optional[float] = Field( + default=None, description="Percentage change in the last 7 days" + ) + change_1m: Optional[float] = Field( + default=None, description="Percentage change in the last month" + ) + change_7d_over_7d: Optional[float] = Field( + default=None, + description="Percentage change over the last two 7-day periods", + alias="change_7dover7d", + ) + change_30d_over_30d: Optional[float] = Field( + default=None, + description="Percentage change over the last two 30-day periods", + alias="change_30dover30d", + ) + breakdown_24h: Optional[Dict[str, Dict[str, int]]] = Field( + default=None, + description="Breakdown of values in the last 24 hours", + alias="breakdown24h", + ) + defillama_id: int = Field(description="DeFiLlama ID", alias="defillamaId") + name: str = Field(description="Name of the chain or protocol") + display_name: str = Field( + description="Display name of the chain or protocol", alias="displayName" + ) + module: str = Field(description="Module name") + category: str = Field(description="Category of the chain or protocol") + logo: str = Field(description="URL to the logo image") + chains: List[str] = Field(description="List of chains") + protocol_type: str = Field(description="Type of protocol", alias="protocolType") + methodology_url: str = Field( + description="URL to the methodology document", alias="methodologyURL" + ) + methodology: Union[str, Dict[str, str]] = Field(description="Methodology details") + latest_fetch_is_ok: bool = Field( + description="Indicates if the latest fetch was successful", + alias="latestFetchIsOk", + ) + slug: str = Field(description="Slug identifier") + id: int = Field(description="Unique identifier") + + +class DeFiLlamaFeesOverviewData(Data): + """DeFiLlama Fees Overview Data.""" + + total_data_chart: List[Dict[datetime, int]] = Field( + description="Aggregated chart data.", alias="totalDataChart" + ) + total_data_chart_breakdown: List[DeFiLlamaChainsBreakdownData] = Field( + description="Aggregated chart data breakdown by chains.", + alias="totalDataChartBreakdown", + ) + breakdown_24h: Optional[Dict[str, Any]] = Field( + default=None, + description="Total fees collected in the last 24 hours broken down by chains", + alias="breakdown24h", + ) + total_24h: int = Field(description="Total 24h fees.", alias="total24h") + total_48h_to_24h: int = Field( + description="Total 48h to 24h fees.", alias="total48hto24h" + ) + total_7d: int = Field(description="Total 7d fees.", alias="total7d") + total_14d_to_7d: int = Field( + description="Total 14d to 7d fees.", alias="total14dto7d" + ) + total_60d_to_30d: int = Field( + description="Total 60d to 30d fees.", alias="total60dto30d" + ) + total_30d: int = Field(description="Total 30d fees.", alias="total30d") + total_1y: int = Field(description="Total 1y fees.", alias="total1y") + change_1d: float = Field(description="1d change in fees.") + change_7d: float = Field(description="7d change in fees.") + change_1m: float = Field(description="1m change in fees.") + change_7d_over_7d: float = Field( + description="7d change in fees over 7d.", alias="change_7dover7d" + ) + change_30d_over_30d: float = Field( + description="30d change in fees over 30d.", alias="change_30dover30d" + ) + protocols: Optional[List[DeFiLlamaProtocolsData]] = Field( + default=None, description="Protocols data.", alias="protocols" + ) + + @field_validator("total_data_chart_breakdown", mode="before") + def validate_total_data_chart_breakdown(cls, v): + return [DeFiLlamaChainsBreakdownData.model_validate(item) for item in v] + + @field_validator("protocols", mode="before") + def validate_protocols(cls, v): + return ( + [DeFiLlamaProtocolsData.model_validate(item) for item in v] if v else None + ) + + +class DeFiLlamaFeesOverviewFetcher( + Fetcher[DeFiLlamaFeesOverviewQueryParams, DeFiLlamaFeesOverviewData] +): + """DeFiLlama Fees Overview Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaFeesOverviewQueryParams: + """Transform query parameters.""" + return DeFiLlamaFeesOverviewQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaFeesOverviewQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + data = fees_revenue.get_overview( + chain=query.chain, + exclude_total_data_chart=False, + exclude_total_data_chart_breakdown=False, + type="fees", + ) + + if not query.all: + del data["protocols"] + return data + + return data + + @staticmethod + def transform_data( + query: DeFiLlamaFeesOverviewQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaFeesOverviewData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = {} + + for k, v in data.items(): + if k in ("chain", "allChains"): + continue + elif k == "totalDataChart": + transformed_data[k] = [ + {datetime.fromtimestamp(item[0]): item[1]} for item in v + ] + elif k == "totalDataChartBreakdown": + transformed_data[k] = [ + {"date": item[0], "chains": item[1]} for item in v + ] + else: + transformed_data[k] = v + + return DeFiLlamaFeesOverviewData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/fees_summary.py b/openbb_platform/providers/defillama/openbb_defillama/models/fees_summary.py new file mode 100644 index 000000000000..d0c41e0d0c6e --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/fees_summary.py @@ -0,0 +1,179 @@ +"""DeFiLlama Fees Summary Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from defillama import fees_revenue +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaFeesSummaryQueryParams(QueryParams): + """DeFiLlama Fees Summary Query.""" + + protocol: str = Field(description="The protocol to fetch data for.") + + +class DeFiLlamaChainsBreakdownData(Data): + """DeFiLlama Chains Breakdown. + + Source: https://defillama.com/docs/api + """ + + date: datetime = Field(description="The date of the data.") + chains: Dict[str, Dict[str, int]] = Field(description="The chains data.") + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaFeesSummaryData(Data): + """DeFiLlama Fees Summary Data.""" + + name: str = Field(description="The name of the protocol.") + defillama_id: Optional[str] = Field( + default=None, + description="The defillama id of the protocol.", + alias="defillamaId", + ) + disabled: Optional[bool] = Field( + default=None, description="Whether the protocol is disabled." + ) + display_name: str = Field( + description="The display name of the protocol.", alias="displayName" + ) + module: Optional[str] = Field( + default=None, description="The module of the protocol." + ) + category: Optional[str] = Field( + default=None, description="The category of the protocol." + ) + logo: str = Field(description="The logo of the protocol.") + chains: List[str] = Field(description="The chains of the protocol.") + methodology_url: Optional[str] = Field( + default=None, + description="The methodology URL of the protocol.", + alias="methodologyURL", + ) + methodology: Optional[Union[str, Dict[str, str]]] = Field( + default=None, description="The methodology of the protocol." + ) + gecko_id: str = Field(description="The gecko id of the protocol.") + forked_from: Optional[str] = Field( + default=None, description="The forked from of the protocol.", alias="forkedFrom" + ) + twitter: Optional[str] = Field( + default=None, description="The twitter of the protocol." + ) + audits: Optional[str] = Field( + default=None, description="The audits of the protocol." + ) + description: Optional[str] = Field( + default=None, description="The description of the protocol." + ) + address: Optional[str] = Field( + default=None, description="The address of the protocol." + ) + url: Optional[str] = Field(default=None, description="The url of the protocol.") + audit_links: Optional[List[str]] = Field( + default=None, description="The audit links of the protocol." + ) + version_key: Optional[str] = Field( + default=None, description="The version key of the protocol.", alias="versionKey" + ) + cmc_id: Optional[str] = Field( + default=None, description="The cmc id of the protocol.", alias="cmcId" + ) + id: str = Field(description="The id of the protocol.") + github: Optional[List[str]] = Field( + default=None, description="The github of the protocol." + ) + governance_id: Optional[List[str]] = Field( + default=None, + description="The governance id of the protocol.", + alias="governanceID", + ) + treasury: Optional[str] = Field( + default=None, description="The treasury of the protocol." + ) + parent_protocol: Optional[str] = Field( + default=None, + description="The parent protocol of the protocol.", + alias="parentProtocol", + ) + latest_fetch_is_ok: bool = Field( + default=None, + description="Whether the latest fetch is ok.", + alias="latestFetchIsOk", + ) + slug: str = Field(description="The slug of the protocol.") + protocol_type: str = Field( + description="The protocol type of the protocol.", alias="protocolType" + ) + total_24h: int = Field( + description="The total 24h of the protocol.", alias="total24h" + ) + total_48h_to_24h: int = Field( + description="The total 48h to 24h of the protocol.", alias="total48hto24h" + ) + total_7d: int = Field(description="The total 7d of the protocol.", alias="total7d") + total_all_time: int = Field( + description="The total all time of the protocol.", alias="totalAllTime" + ) + total_data_chart: List[Dict[datetime, int]] = Field( + description="Aggregated chart data.", alias="totalDataChart" + ) + total_data_chart_breakdown: List[DeFiLlamaChainsBreakdownData] = Field( + description="Aggregated chart data breakdown by chains.", + alias="totalDataChartBreakdown", + ) + change_1d: float = Field(description="The change 1d of the protocol.") + + @field_validator("total_data_chart_breakdown", mode="before") + def validate_total_data_chart_breakdown(cls, v): + return [DeFiLlamaChainsBreakdownData.model_validate(item) for item in v] + + +class DeFiLlamaFeesSummaryFetcher( + Fetcher[DeFiLlamaFeesSummaryQueryParams, DeFiLlamaFeesSummaryData] +): + """DeFiLlama Fees Summary Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaFeesSummaryQueryParams: + """Transform query parameters.""" + return DeFiLlamaFeesSummaryQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaFeesSummaryQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + return fees_revenue.get_summary( + protocol=query.protocol, + data="daily", + type="fees", + ) + + @staticmethod + def transform_data( + query: DeFiLlamaFeesSummaryQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaFeesSummaryData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = dict(data) + + transformed_data["totalDataChart"] = [ + {datetime.fromtimestamp(item[0]): item[1]} + for item in data["totalDataChart"] + ] + transformed_data["totalDataChartBreakdown"] = [ + {"date": item[0], "chains": item[1]} + for item in data["totalDataChartBreakdown"] + ] + + return DeFiLlamaFeesSummaryData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/revenue_overview.py b/openbb_platform/providers/defillama/openbb_defillama/models/revenue_overview.py new file mode 100644 index 000000000000..b2c1e877e751 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/revenue_overview.py @@ -0,0 +1,228 @@ +"""DeFiLlama Revenue Overview Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from defillama import fees_revenue +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaRevenueOverviewQueryParams(QueryParams): + """DeFiLlama Revenue Overview Query. + + Source: https://defillama.com/docs/api + """ + + chain: Optional[str] = Field( + default=None, description="The chain to fetch data for." + ) + all: bool = Field( + default=False, description="Whether to fetch details of all protocols." + ) + # NOTE: Disabled since the API does not return any data + # aggregate: Literal["daily", "total"] = Field( + # default="daily", + # description="Whether to fetch daily or total aggregate data.", + # ) + + +class DeFiLlamaChainsBreakdownData(Data): + """DeFiLlama Chains Breakdown Data.""" + + date: datetime = Field(description="The date of the data.") + chains: Dict[str, int] = Field(description="The chains data.") + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaProtocolsData(Data): + """DeFiLlama Protocols Data.""" + + total_24h: Optional[int] = Field( + default=None, description="Total value in the last 24 hours", alias="total24h" + ) + total_48h_to_24h: Optional[int] = Field( + default=None, + description="Total value from 48 to 24 hours ago", + alias="total48hto24h", + ) + total_7d: Optional[int] = Field( + default=None, description="Total value in the last 7 days", alias="total7d" + ) + total_14d_to_7d: Optional[int] = Field( + default=None, + description="Total value from 14 to 7 days ago", + alias="total14dto7d", + ) + total_60d_to_30d: Optional[int] = Field( + default=None, + description="Total value from 60 to 30 days ago", + alias="total60dto30d", + ) + total_30d: Optional[int] = Field( + default=None, description="Total value in the last 30 days", alias="total30d" + ) + total_1y: Optional[int] = Field( + default=None, description="Total value in the last year", alias="total1y" + ) + total_all_time: Optional[float] = Field( + default=None, description="Total value of all time", alias="totalAllTime" + ) + average_1y: Optional[float] = Field( + default=None, description="Average value over the last year", alias="average1y" + ) + change_1d: Optional[float] = Field( + default=None, description="Percentage change in the last day" + ) + change_7d: Optional[float] = Field( + default=None, description="Percentage change in the last 7 days" + ) + change_1m: Optional[float] = Field( + default=None, description="Percentage change in the last month" + ) + change_7d_over_7d: Optional[float] = Field( + default=None, + description="Percentage change over the last two 7-day periods", + alias="change_7dover7d", + ) + change_30d_over_30d: Optional[float] = Field( + default=None, + description="Percentage change over the last two 30-day periods", + alias="change_30dover30d", + ) + breakdown_24h: Optional[Dict[str, Dict[str, int]]] = Field( + default=None, + description="Breakdown of values in the last 24 hours", + alias="breakdown24h", + ) + defillama_id: int = Field(description="DeFiLlama ID", alias="defillamaId") + name: str = Field(description="Name of the chain or protocol") + display_name: str = Field( + description="Display name of the chain or protocol", alias="displayName" + ) + module: str = Field(description="Module name") + category: str = Field(description="Category of the chain or protocol") + logo: str = Field(description="URL to the logo image") + chains: List[str] = Field(description="List of chains") + protocol_type: str = Field(description="Type of protocol", alias="protocolType") + methodology_url: str = Field( + description="URL to the methodology document", alias="methodologyURL" + ) + methodology: Union[str, Dict[str, str]] = Field(description="Methodology details") + latest_fetch_is_ok: bool = Field( + description="Indicates if the latest fetch was successful", + alias="latestFetchIsOk", + ) + slug: str = Field(description="Slug identifier") + id: int = Field(description="Unique identifier") + + +class DeFiLlamaRevenueOverviewData(Data): + """DeFiLlama Revenue Overview Data.""" + + total_data_chart: List[Dict[datetime, int]] = Field( + description="Aggregated chart data.", alias="totalDataChart" + ) + total_data_chart_breakdown: List[DeFiLlamaChainsBreakdownData] = Field( + description="Aggregated chart data breakdown by chains.", + alias="totalDataChartBreakdown", + ) + breakdown_24h: Optional[Dict[str, Any]] = Field( + default=None, + description="Total revenue collected in the last 24 hours broken down by chains", + alias="breakdown24h", + ) + total_24h: int = Field(description="Total 24h revenue.", alias="total24h") + total_48h_to_24h: int = Field( + description="Total 48h to 24h revenue.", alias="total48hto24h" + ) + total_7d: int = Field(description="Total 7d revenue.", alias="total7d") + total_14d_to_7d: int = Field( + description="Total 14d to 7d revenue.", alias="total14dto7d" + ) + total_60d_to_30d: int = Field( + description="Total 60d to 30d revenue.", alias="total60dto30d" + ) + total_30d: int = Field(description="Total 30d revenue.", alias="total30d") + total_1y: int = Field(description="Total 1y revenue.", alias="total1y") + change_1d: float = Field(description="1d change in revenue.") + change_7d: float = Field(description="7d change in revenue.") + change_1m: float = Field(description="1m change in revenue.") + change_7d_over_7d: float = Field( + description="7d change in revenue over 7d.", alias="change_7dover7d" + ) + change_30d_over_30d: float = Field( + description="30d change in revenue over 30d.", alias="change_30dover30d" + ) + protocols: Optional[List[DeFiLlamaProtocolsData]] = Field( + default=None, description="Protocols data.", alias="protocols" + ) + + @field_validator("total_data_chart_breakdown", mode="before") + def validate_total_data_chart_breakdown(cls, v): + return [DeFiLlamaChainsBreakdownData.model_validate(item) for item in v] + + @field_validator("protocols", mode="before") + def validate_protocols(cls, v): + return ( + [DeFiLlamaProtocolsData.model_validate(item) for item in v] if v else None + ) + + +class DeFiLlamaRevenueOverviewFetcher( + Fetcher[DeFiLlamaRevenueOverviewQueryParams, DeFiLlamaRevenueOverviewData] +): + """DeFiLlama Revenue Overview Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaRevenueOverviewQueryParams: + """Transform query parameters.""" + return DeFiLlamaRevenueOverviewQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaRevenueOverviewQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + data = fees_revenue.get_overview( + chain=query.chain, + exclude_total_data_chart=False, + exclude_total_data_chart_breakdown=False, + type="revenue", + ) + + if not query.all: + del data["protocols"] + return data + + return data + + @staticmethod + def transform_data( + query: DeFiLlamaRevenueOverviewQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaRevenueOverviewData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = {} + + for k, v in data.items(): + if k in ("chain", "allChains"): + continue + elif k == "totalDataChart": + transformed_data[k] = [ + {datetime.fromtimestamp(item[0]): item[1]} for item in v + ] + elif k == "totalDataChartBreakdown": + transformed_data[k] = [ + {"date": item[0], "chains": item[1]} for item in v + ] + else: + transformed_data[k] = v + + return DeFiLlamaRevenueOverviewData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/revenue_summary.py b/openbb_platform/providers/defillama/openbb_defillama/models/revenue_summary.py new file mode 100644 index 000000000000..561e496a996d --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/revenue_summary.py @@ -0,0 +1,187 @@ +"""DeFiLlama Revenue Summary Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from defillama import fees_revenue +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaRevenueSummaryQueryParams(QueryParams): + """DeFiLlama Revenue Summary Query. + + Source: https://defillama.com/docs/api + """ + + protocol: str = Field(description="The protocol to fetch data for.") + + +class DeFiLlamaChainsBreakdownData(Data): + """DeFiLlama Chains Breakdown Data.""" + + date: datetime = Field(description="The date of the data.") + chains: Dict[str, Dict[str, int]] = Field(description="The chains data.") + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaRevenueSummaryData(Data): + """DeFiLlama Revenue Summary Data.""" + + name: str = Field(description="The name of the protocol.") + defillama_id: Optional[str] = Field( + default=None, + description="The defillama id of the protocol.", + alias="defillamaId", + ) + disabled: Optional[bool] = Field( + default=None, description="Whether the protocol is disabled." + ) + display_name: str = Field( + description="The display name of the protocol.", alias="displayName" + ) + module: Optional[str] = Field( + default=None, description="The module of the protocol." + ) + category: Optional[str] = Field( + default=None, description="The category of the protocol." + ) + logo: str = Field(description="The logo of the protocol.") + chains: List[str] = Field(description="The chains of the protocol.") + methodology_url: Optional[str] = Field( + default=None, + description="The methodology URL of the protocol.", + alias="methodologyURL", + ) + methodology: Optional[Union[str, Dict[str, str]]] = Field( + default=None, description="The methodology of the protocol." + ) + gecko_id: str = Field(description="The gecko id of the protocol.") + forked_from: Optional[str] = Field( + default=None, description="The forked from of the protocol.", alias="forkedFrom" + ) + twitter: Optional[str] = Field( + default=None, description="The twitter of the protocol." + ) + audits: Optional[str] = Field( + default=None, description="The audits of the protocol." + ) + description: Optional[str] = Field( + default=None, description="The description of the protocol." + ) + address: Optional[str] = Field( + default=None, description="The address of the protocol." + ) + url: Optional[str] = Field(default=None, description="The url of the protocol.") + audit_links: Optional[List[str]] = Field( + default=None, description="The audit links of the protocol." + ) + version_key: Optional[str] = Field( + default=None, description="The version key of the protocol.", alias="versionKey" + ) + cmc_id: Optional[str] = Field( + default=None, description="The cmc id of the protocol.", alias="cmcId" + ) + id: str = Field(description="The id of the protocol.") + github: Optional[List[str]] = Field( + default=None, description="The github of the protocol." + ) + governance_id: Optional[List[str]] = Field( + default=None, + description="The governance id of the protocol.", + alias="governanceID", + ) + treasury: Optional[str] = Field( + default=None, description="The treasury of the protocol." + ) + parent_protocol: Optional[str] = Field( + default=None, + description="The parent protocol of the protocol.", + alias="parentProtocol", + ) + latest_fetch_is_ok: bool = Field( + default=None, + description="Whether the latest fetch is ok.", + alias="latestFetchIsOk", + ) + slug: str = Field(description="The slug of the protocol.") + protocol_type: str = Field( + description="The protocol type of the protocol.", alias="protocolType" + ) + total_24h: Optional[int] = Field( + default=None, description="The total 24h of the protocol.", alias="total24h" + ) + total_48h_to_24h: Optional[int] = Field( + default=None, + description="The total 48h to 24h of the protocol.", + alias="total48hto24h", + ) + total_7d: Optional[int] = Field( + default=None, description="The total 7d of the protocol.", alias="total7d" + ) + total_all_time: Optional[int] = Field( + default=None, + description="The total all time of the protocol.", + alias="totalAllTime", + ) + total_data_chart: List[Dict[datetime, int]] = Field( + description="Aggregated chart data.", alias="totalDataChart" + ) + total_data_chart_breakdown: List[DeFiLlamaChainsBreakdownData] = Field( + description="Aggregated chart data breakdown by chains.", + alias="totalDataChartBreakdown", + ) + change_1d: Optional[float] = Field( + default=None, description="The change 1d of the protocol." + ) + + @field_validator("total_data_chart_breakdown", mode="before") + def validate_total_data_chart_breakdown(cls, v): + return [DeFiLlamaChainsBreakdownData.model_validate(item) for item in v] + + +class DeFiLlamaRevenueSummaryFetcher( + Fetcher[DeFiLlamaRevenueSummaryQueryParams, DeFiLlamaRevenueSummaryData] +): + """DeFiLlama Revenue Summary Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaRevenueSummaryQueryParams: + """Transform query parameters.""" + return DeFiLlamaRevenueSummaryQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaRevenueSummaryQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + return fees_revenue.get_summary( + protocol=query.protocol, + data="daily", + type="revenue", + ) + + @staticmethod + def transform_data( + query: DeFiLlamaRevenueSummaryQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaRevenueSummaryData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = dict(data) + + transformed_data["totalDataChart"] = [ + {datetime.fromtimestamp(item[0]): item[1]} + for item in data["totalDataChart"] + ] + transformed_data["totalDataChartBreakdown"] = [ + {"date": item[0], "chains": item[1]} + for item in data["totalDataChartBreakdown"] + ] + + return DeFiLlamaRevenueSummaryData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_charts.py b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_charts.py new file mode 100644 index 000000000000..855fe5531633 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_charts.py @@ -0,0 +1,88 @@ +"""DeFiLlama Stablecoins Charts Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from defillama import stablecoins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaStablecoinsChartsQueryParams(QueryParams): + """DeFiLlama Stablecoins Charts Query. + + Source: https://defillama.com/docs/api + """ + + stablecoin: Optional[str] = Field( + default=None, + description="Stablecoin ID to get the mcap of.", + ) + chain: Optional[str] = Field( + default=None, + description="Chain of the stablecoin.", + ) + + +class DeFiLlamaStablecoinsChartsData(Data): + """DeFiLlama Stablecoins Charts Data.""" + + date: datetime = Field(description="Date of the data.") + total_circulating: Dict[str, float] = Field( + description="Total circulating of the stablecoin on each chain.", + alias="totalCirculating", + ) + total_circulating_usd: Dict[str, float] = Field( + description="Total circulating of the stablecoin in USD on each chain.", + alias="totalCirculatingUSD", + ) + total_unreleased: Optional[Dict[str, float]] = Field( + default=None, + description="Total unreleased amount of the stablecoin on each chain.", + alias="totalUnreleased", + ) + total_minted_usd: Optional[Dict[str, float]] = Field( + default=None, + description="Total minted amount of the stablecoin in USD on each chain.", + alias="totalMintedUSD", + ) + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(int(v)) + + +class DeFiLlamaStablecoinsChartsFetcher( + Fetcher[ + DeFiLlamaStablecoinsChartsQueryParams, + List[DeFiLlamaStablecoinsChartsData], + ] +): + """DeFiLlama Stablecoins Charts Fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> DeFiLlamaStablecoinsChartsQueryParams: + """Transform query parameters.""" + return DeFiLlamaStablecoinsChartsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaStablecoinsChartsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any + ) -> List[Dict[str, Any]]: + """Fetch data from DeFiLlama.""" + return stablecoins.get_charts(chain=query.chain, stablecoin=query.stablecoin) + + @staticmethod + def transform_data( + query: DeFiLlamaStablecoinsChartsQueryParams, + data: List[Dict[str, Any]], + **kwargs: Any + ) -> List[DeFiLlamaStablecoinsChartsData]: + """Transform the data into the desired format.""" + return [DeFiLlamaStablecoinsChartsData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_current.py b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_current.py new file mode 100644 index 000000000000..cc27342be4c0 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_current.py @@ -0,0 +1,69 @@ +"""DeFiLlama Stablecoins Current Model.""" + +from typing import Any, Dict, List, Optional + +from defillama import stablecoins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaStablecoinsCurrentQueryParams(QueryParams): + """DeFiLlama Stablecoins Current Query. + + Source: https://defillama.com/docs/api + """ + + pass + + +class DeFiLlamaStablecoinsCurrentData(Data): + """DeFiLlama Stablecoins Current Data.""" + + gecko_id: Optional[str] = Field(description="CoinGecko ID of the stablecoin.") + total_circulating_usd: Dict[str, float] = Field( + description="Total circulating USD of the stablecoin.", + alias="totalCirculatingUSD", + ) + token_symbol: Optional[str] = Field( + description="Symbol of the stablecoin.", alias="tokenSymbol" + ) + name: str = Field(description="Name of the stablecoin.") + + @field_validator("token_symbol", mode="before") + def validate_token_symbol(cls, v): + return v.upper() if v else None + + +class DeFiLlamaStablecoinsCurrentFetcher( + Fetcher[ + DeFiLlamaStablecoinsCurrentQueryParams, List[DeFiLlamaStablecoinsCurrentData] + ] +): + """DeFiLlama Stablecoins Current Fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> DeFiLlamaStablecoinsCurrentQueryParams: + """Transform query parameters.""" + return DeFiLlamaStablecoinsCurrentQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaStablecoinsCurrentQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + return stablecoins.get_chains() + + @staticmethod + def transform_data( + query: DeFiLlamaStablecoinsCurrentQueryParams, + data: Dict[str, Any], + **kwargs: Any + ) -> List[DeFiLlamaStablecoinsCurrentData]: + """Transform the data into the desired format.""" + return [DeFiLlamaStablecoinsCurrentData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_distribution.py b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_distribution.py new file mode 100644 index 000000000000..b4ae4eed3346 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_distribution.py @@ -0,0 +1,157 @@ +"""DeFiLlama Stablecoins Distribution Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from defillama import stablecoins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaStablecoinsDistributionQueryParams(QueryParams): + """DeFiLlama Stablecoins Distribution Query. + + Source: https://defillama.com/docs/api + """ + + stablecoin: str = Field(description="Stablecoin ID to get the distribution of.") + + +class DeFiLlamaStablecoinsChainTokensData(Data): + """DeFiLlama Stablecoins Distribution Chain Tokens.""" + + date: datetime = Field(description="Date of the data.") + circulating: Optional[Dict[str, float]] = Field( + default=None, + description="Circulating of the stablecoin on each chain.", + ) + bridged_to: Optional[Dict[str, Any]] = Field( + default=None, + description="Currency bridges value of the stablecoin on each chain.", + alias="bridgedTo", + ) + minted: Optional[Dict[str, Any]] = Field( + default=None, + description="Minted amount of the stablecoin on each chain.", + ) + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaStablecoinsTokensData(Data): + """DeFiLlama Stablecoins Distribution Tokens Data.""" + + date: datetime = Field(description="Date of the data.") + circulating: Optional[Dict[str, float]] = Field( + default=None, + description="Circulating of the stablecoin on each chain.", + ) + unreleased: Optional[Dict[str, float]] = Field( + default=None, + description="Unreleased of the stablecoin on each chain.", + ) + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaStablecoinsDistributionData(Data): + """DeFiLlama Stablecoins Distribution Data.""" + + id: str = Field(description="ID of the stablecoin.") + name: str = Field(description="Name of the stablecoin.") + address: str = Field(description="Address of the stablecoin.") + symbol: str = Field(description="Symbol of the stablecoin.") + url: str = Field(description="URL of the stablecoin.") + description: str = Field(description="Description of the stablecoin.") + mint_redeem_description: str = Field( + description="Mint/Redeem description of the stablecoin.", + alias="mintRedeemDescription", + ) + on_coin_gecko: bool = Field( + description="Whether the stablecoin is on CoinGecko.", alias="onCoinGecko" + ) + gecko_id: str = Field(description="CoinGecko ID of the stablecoin.") + cmc_id: str = Field(description="CMC ID of the stablecoin.", alias="cmcId") + peg_type: str = Field(description="Peg type of the stablecoin.", alias="pegType") + peg_mechanism: str = Field( + description="Peg mechanism of the stablecoin.", alias="pegMechanism" + ) + price_source: str = Field( + description="Price source of the stablecoin.", alias="priceSource" + ) + price: float = Field(description="Price of the stablecoin.") + audit_links: List[str] = Field(description="Audit links of the stablecoin.") + twitter: str = Field(description="Twitter of the stablecoin.") + wiki: str = Field(description="Wiki of the stablecoin.") + chain_balances: Dict[str, List[DeFiLlamaStablecoinsChainTokensData]] = Field( + description="Chain balances of the stablecoin.", alias="chainBalances" + ) + current_chain_balances: Dict[str, Dict[str, float]] = Field( + description="Current chain balances of the stablecoin.", + alias="currentChainBalances", + ) + tokens: List[DeFiLlamaStablecoinsTokensData] = Field( + description="Tokens of the stablecoin." + ) + + @field_validator("symbol", mode="before") + def validate_symbol(cls, v): + return v.upper() + + @field_validator("chain_balances", mode="before") + def validate_chain_balances(cls, v): + return { + c: [DeFiLlamaStablecoinsChainTokensData.model_validate(i) for i in d] + for c, d in v.items() + } + + @field_validator("tokens", mode="before") + def validate_tokens(cls, v): + return [DeFiLlamaStablecoinsTokensData.model_validate(d) for d in v] + + +class DeFiLlamaStablecoinsDistributionFetcher( + Fetcher[ + DeFiLlamaStablecoinsDistributionQueryParams, + DeFiLlamaStablecoinsDistributionData, + ] +): + """DeFiLlama Stablecoins Distribution Fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> DeFiLlamaStablecoinsDistributionQueryParams: + """Transform query parameters.""" + return DeFiLlamaStablecoinsDistributionQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaStablecoinsDistributionQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + return stablecoins.get_distribution(query.stablecoin) + + @staticmethod + def transform_data( + query: DeFiLlamaStablecoinsDistributionQueryParams, + data: Dict[str, Any], + **kwargs: Any + ) -> DeFiLlamaStablecoinsDistributionData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = {} + + transformed_data = { + **data, + "chainBalances": {k: v["tokens"] for k, v in data["chainBalances"].items()}, + } + + return DeFiLlamaStablecoinsDistributionData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_historical.py b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_historical.py new file mode 100644 index 000000000000..f99fcf5758bb --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_historical.py @@ -0,0 +1,66 @@ +"""DeFiLlama Stablecoins Historical Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from defillama import stablecoins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaStablecoinsHistoricalQueryParams(QueryParams): + """DeFiLlama Stablecoins Historical Query. + + Source: https://defillama.com/docs/api + """ + + pass + + +class DeFiLlamaStablecoinsHistoricalData(Data): + """DeFiLlama Stablecoins Historical Data.""" + + date: datetime = Field(description="Date of the data.") + prices: Dict[str, float] = Field( + description="Prices of the stablecoin on each chain." + ) + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaStablecoinsHistoricalFetcher( + Fetcher[ + DeFiLlamaStablecoinsHistoricalQueryParams, + List[DeFiLlamaStablecoinsHistoricalData], + ] +): + """DeFiLlama Stablecoins Historical Fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> DeFiLlamaStablecoinsHistoricalQueryParams: + """Transform query parameters.""" + return DeFiLlamaStablecoinsHistoricalQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaStablecoinsHistoricalQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any + ) -> Dict[str, Any]: + """Fetch data from DeFiLlama.""" + return stablecoins.get_prices() + + @staticmethod + def transform_data( + query: DeFiLlamaStablecoinsHistoricalQueryParams, + data: Dict[str, Any], + **kwargs: Any + ) -> List[DeFiLlamaStablecoinsHistoricalData]: + """Transform the data into the desired format.""" + return [DeFiLlamaStablecoinsHistoricalData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_list.py b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_list.py new file mode 100644 index 000000000000..833920a615b4 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/stablecoins_list.py @@ -0,0 +1,146 @@ +"""DeFiLlama Stablecoins List Model.""" + +from typing import Any, Dict, List, Optional, Union + +from defillama import stablecoins +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaStablecoinsListQueryParams(QueryParams): + """DeFiLlama Stablecoins List Query. + + Source: https://defillama.com/docs/api + """ + + include_prices: bool = Field( + default=False, + description="Whether to include prices.", + ) + + +class DeFiLlamaStablecoinsListChainData(Data): + """DeFiLlama Stablecoins List Chain Data.""" + + chain: str = Field(description="Chain of the stablecoin.") + current: Dict[str, float] = Field( + description="Current circulating amount of the stablecoin by chain." + ) + circulating_prev_day: Union[int, Dict[str, float]] = Field( + default=0, + description="Circulating amount of the stablecoin the previous day.", + alias="circulatingPrevDay", + ) + circulating_prev_week: Union[int, Dict[str, float]] = Field( + default=0, + description="Circulating amount of the stablecoin the previous week.", + alias="circulatingPrevWeek", + ) + circulating_prev_month: Union[int, Dict[str, float]] = Field( + default=0, + description="Circulating amount of the stablecoin the previous month.", + alias="circulatingPrevMonth", + ) + + +class DeFiLlamaStablecoinsListData(Data): + """DeFiLlama Stablecoins List Data.""" + + id: str = Field(description="ID of the stablecoin.") + name: str = Field(description="Name of the stablecoin.") + symbol: str = Field(description="Symbol of the stablecoin.") + gecko_id: Optional[str] = Field( + default=None, description="CoinGecko ID of the stablecoin." + ) + peg_type: str = Field(description="Peg type of the stablecoin.", alias="pegType") + peg_mechanism: str = Field( + description="Peg mechanism of the stablecoin.", alias="pegMechanism" + ) + price: Optional[float] = Field(default=None, description="Price of the stablecoin.") + price_source: Optional[str] = Field( + default=None, description="Price source of the stablecoin.", alias="priceSource" + ) + circulating: Dict[str, float] = Field( + description="Circulating amount of the stablecoin." + ) + circulating_prev_day: Union[int, Dict[str, float]] = Field( + default=0, + description="Circulating amount of the stablecoin the previous day.", + alias="circulatingPrevDay", + ) + circulating_prev_week: Union[int, Dict[str, float]] = Field( + default=0, + description="Circulating amount of the stablecoin the previous week.", + alias="circulatingPrevWeek", + ) + circulating_prev_month: Union[int, Dict[str, float]] = Field( + default=0, + description="Circulating amount of the stablecoin the previous month.", + alias="circulatingPrevMonth", + ) + chain_circulating: List[DeFiLlamaStablecoinsListChainData] = Field( + description="Circulating amount of the stablecoin by chain.", + alias="chainCirculating", + ) + chains: List[str] = Field(description="Chains of the stablecoin.") + + @field_validator("symbol", mode="before") + def validate_symbol(cls, v): + return v.upper() + + @field_validator("chain_circulating", mode="before") + def validate_chain_circulating(cls, v): + return [DeFiLlamaStablecoinsListChainData.model_validate(chain) for chain in v] + + +class DeFiLlamaStablecoinsListFetcher( + Fetcher[DeFiLlamaStablecoinsListQueryParams, List[DeFiLlamaStablecoinsListData]] +): + """DeFiLlama Stablecoins List Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaStablecoinsListQueryParams: + """Transform query parameters.""" + return DeFiLlamaStablecoinsListQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaStablecoinsListQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any + ) -> List[Dict[str, Any]]: + """Fetch data from DeFiLlama.""" + return stablecoins.get_stablecoins(include_prices=query.include_prices) + + @staticmethod + def transform_data( + query: DeFiLlamaStablecoinsListQueryParams, + data: List[Dict[str, Any]], + **kwargs: Any + ) -> List[DeFiLlamaStablecoinsListData]: + """Transform the data into the desired format.""" + + transformed_data: List[Dict[str, Any]] = [] + + transformed_data = [ + { + **item, + "chainCirculating": [ + { + "chain": k, + "current": v["current"], + "circulatingPrevDay": v["circulatingPrevDay"], + "circulatingPrevWeek": v["circulatingPrevWeek"], + "circulatingPrevMonth": v["circulatingPrevMonth"], + } + for k, v in item["chainCirculating"].items() + ], + } + for item in data + ] + + return [ + DeFiLlamaStablecoinsListData.model_validate(d) for d in transformed_data + ] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/tvl_chains.py b/openbb_platform/providers/defillama/openbb_defillama/models/tvl_chains.py new file mode 100644 index 000000000000..569d5eb524f9 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/tvl_chains.py @@ -0,0 +1,64 @@ +"""DeFiLlama TVL Chains model""" + +from typing import Any, Dict, List, Optional + +from defillama import tvl +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field + + +class DeFiLlamaTvlChainsQueryParams(QueryParams): + """DeFiLlama TVL Chains Query Parameters. + + Source: https://defillama.com/docs/api + """ + + pass + + +class DeFiLlamaTvlChainsData(Data): + """DeFiLlama TVL Chains Data.""" + + gecko_id: Optional[str] = Field( + default=None, description="CoinGecko ID of the chain" + ) + tvl: float = Field(description="Current TVL in the chain") + token_symbol: Optional[str] = Field( + default=None, description="Chain symbol (e.g. ETH)", alias="tokenSymbol" + ) + cmc_id: Optional[str] = Field( + default=None, description="CoinMarketCap ID of the chain", alias="cmcId" + ) + name: str = Field(description="Given name of the chain") + chain_id: Optional[int] = Field( + default=None, description="DeFillama ID of the chain", alias="chainId" + ) + + +class DeFiLlamaTvlChainsFetcher( + Fetcher[DeFiLlamaTvlChainsQueryParams, List[DeFiLlamaTvlChainsData]] +): + """DeFiLlama TVL Chains Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaTvlChainsQueryParams: + """Transform query parameters.""" + return DeFiLlamaTvlChainsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaTvlChainsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict[str, Any]]: + """Extract data from DeFiLlama API.""" + return tvl.get_chains() + + @staticmethod + def transform_data( + query: DeFiLlamaTvlChainsQueryParams, data: List[Dict[str, Any]], **kwargs: Any + ) -> List[DeFiLlamaTvlChainsData]: + """Transform the data into the desired format.""" + return [DeFiLlamaTvlChainsData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/tvl_current.py b/openbb_platform/providers/defillama/openbb_defillama/models/tvl_current.py new file mode 100644 index 000000000000..cd114a612d28 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/tvl_current.py @@ -0,0 +1,58 @@ +"""DeFiLlama TVL Current model""" + +from typing import Any, Dict, Optional + +from defillama import tvl +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS +from pydantic import Field + + +class DeFiLlamaTvlCurrentQueryParams(QueryParams): + """DeFiLlama TVL Current Query Parameters. + + Source: https://defillama.com/docs/api + """ + + symbol: str = Field( + description=QUERY_DESCRIPTIONS.get("symbol", "") + " Should be a protocol." + ) + + +class DeFiLlamaTvlCurrentData(Data): + """DeFiLlama TVL Current Data.""" + + protocol: str = Field(description="Protocol name") + tvl: float = Field(description="Current TVL in USD") + + +class DeFiLlamaTvlCurrentFetcher( + Fetcher[DeFiLlamaTvlCurrentQueryParams, DeFiLlamaTvlCurrentData] +): + """DeFiLlama TVL Current Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaTvlCurrentQueryParams: + """Transform query parameters.""" + return DeFiLlamaTvlCurrentQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaTvlCurrentQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + if current_tvl := tvl.get_protocol_tvl(query.symbol): + return {"protocol": query.symbol, "tvl": current_tvl} + else: + return {} + + @staticmethod + def transform_data( + query: DeFiLlamaTvlCurrentQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaTvlCurrentData: + """Transform the data into the desired format.""" + return DeFiLlamaTvlCurrentData.model_validate(data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/tvl_historical.py b/openbb_platform/providers/defillama/openbb_defillama/models/tvl_historical.py new file mode 100644 index 000000000000..7a8316db62f1 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/tvl_historical.py @@ -0,0 +1,348 @@ +"""DeFiLlama TVL Historical model""" + +from datetime import datetime +from typing import Any, Dict, List, Literal, Optional, Union + +from defillama import tvl +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.descriptions import ( + DATA_DESCRIPTIONS, + QUERY_DESCRIPTIONS, +) +from pydantic import Field, field_validator + + +class DeFiLlamaTvlHistoricalQueryParams(QueryParams): + """DeFiLlama TVL Historical Query. + + Source: https://defillama.com/docs/api + """ + + symbol: Optional[str] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("symbol", "") + + " Should be a protocol or chain.", + ) + symbol_type: Optional[Literal["protocol", "chain"]] = Field( + default="protocol", + description="Type of symbol to get data for.", + ) + + +class DeFiLlamaProtocolTokens(Data): + """DeFiLlama Protocol Tokens Data.""" + + date: datetime = Field(description="Date of the token") + tokens: Dict[str, float] = Field(description="Total Value Locked for each token") + + @field_validator("date", mode="before") + def convert_timestamp_to_datetime(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaTvlData(Data): + """DeFiLlama TVL Data""" + + date: datetime = Field(description="Date of the TVL") + total_liquidity_usd: float = Field( + description="Total Value Locked in USD", alias="totalLiquidityUSD" + ) + + @field_validator("date", mode="before") + def convert_timestamp_to_datetime(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaChainTvlsData(Data): + """DeFiLlama Chain TVLs Data.""" + + tvl: List[DeFiLlamaTvlData] = Field(description="Date of the chain") + tokens: Optional[List[DeFiLlamaProtocolTokens]] = Field( + default=None, description="Total Value Locked for each token" + ) + tokens_in_usd: Optional[List[DeFiLlamaProtocolTokens]] = Field( + default=None, + description="Total Value Locked in USD for each token", + alias="tokensInUsd", + ) + + @field_validator("tokens", "tokens_in_usd", mode="before") + def validate_fields(cls, v): + return [DeFiLlamaProtocolTokens.model_validate(d) for d in v] if v else None + + @field_validator("tvl", mode="before") + def validate_tvl(cls, v): + if isinstance(v, list): + return [DeFiLlamaTvlData.model_validate(d) for d in v] + else: + return v + + +class DeFiLlamaProtocolRaisesData(Data): + """DeFiLlama Protocol Raises Data.""" + + date: datetime = Field(description="Date of the raise") + name: str = Field(description="Name of the protocol") + round: str = Field(description="Round of the raise") + amount: float = Field(description="Amount of the raise in USD") + chains: List[str] = Field(description="List of chains the protocol is deployed on") + sector: str = Field(description="Sector of the protocol") + category: str = Field(description="Category of the protocol") + source: Optional[str] = Field(default=None, description="Source of the raise") + lead_investors: List[str] = Field( + description="List of lead investors", alias="leadInvestors" + ) + other_investors: List[str] = Field( + description="List of other investors", alias="otherInvestors" + ) + valuation: Optional[float] = Field( + default=None, description="Valuation of the protocol" + ) + defillama_id: str = Field( + description="DeFiLlama ID of the protocol", alias="defillamaId" + ) + + @field_validator("date", mode="before") + def convert_timestamp_to_datetime(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaProtocolHallmarksData(Data): + """DeFiLlama Protocol Hallmarks Data""" + + date: datetime = Field(description="Date of the hallmark") + label: str = Field(description="Label of the hallmark") + + @field_validator("date", mode="before") + def convert_timestamp_to_datetime(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaTvlHistoricalData(Data): + """DeFiLlama TVL Historical Data.""" + + # Field(s) returned for both protocols and chains + tvl: Optional[Union[float, List[DeFiLlamaTvlData]]] = Field( + default=None, description="Total Value Locked in USD" + ) + + # Field(s) returned when symbol_type is "chain" + date: Optional[datetime] = Field( + default=None, description=DATA_DESCRIPTIONS.get("date", "") + ) + + # Field(s) returned when symbol_type is "protocol" + id: Optional[str] = Field(default=None, description="ID of the protocol") + name: Optional[str] = Field(default=None, description="Name of the protocol") + address: Optional[str] = Field(default=None, description="Address of the protocol") + symbol: Optional[str] = Field(default=None, description="Symbol of the protocol") + url: Optional[str] = Field(default=None, description="Website URL of the protocol") + description: Optional[str] = Field( + default=None, description="Description of the protocol" + ) + chain: Optional[str] = Field(default=None, description="Name of the blockchain") + logo: Optional[str] = Field(default=None, description="URL of the protocol's logo") + audits: Optional[str] = Field( + default=None, description="Number of audits the protocol has undergone" + ) + audit_note: Optional[str] = Field( + default=None, description="Note about the audit status" + ) + gecko_id: Optional[str] = Field( + default=None, description="CoinGecko ID of the token" + ) + cmc_id: Optional[str] = Field( + default=None, description="CoinMarketCap ID of the token", alias="cmcId" + ) + category: Optional[str] = Field( + default=None, description="Category of the protocol" + ) + chains: Optional[List[str]] = Field( + default=None, description="List of chains the protocol is deployed on" + ) + module: Optional[str] = Field( + default=None, description="Module used for TVL calculation" + ) + twitter: Optional[str] = Field( + default=None, description="Twitter handle of the protocol" + ) + forked_from: Optional[List[str]] = Field( + default=None, + description="List of protocols that the current protocol has forked from", + alias="forkedFrom", + ) + oracles: Optional[List[str]] = Field( + default=None, description="List of oracles used for TVL calculation" + ) + listed_at: Optional[datetime] = Field( + default=None, + description="Timestamp of when the protocol was listed", + alias="listedAt", + ) + methodology: Optional[str] = Field( + default=None, description="Methodology used for TVL calculation" + ) + slug: Optional[str] = Field(default=None, description="Slug of the protocol") + chain_tvls: Optional[Union[Dict[str, float], Dict[str, DeFiLlamaChainTvlsData]]] = ( + Field( + default=None, + description="Total Value Locked in USD for each chain of the protocol", + alias="chainTvls", + ) + ) + change_1h: Optional[float] = Field( + default=None, description="Change in TVL over the last hour" + ) + change_1d: Optional[float] = Field( + default=None, description="Change in TVL over the last day" + ) + change_7d: Optional[float] = Field( + default=None, description="Change in TVL over the last week" + ) + + # Additional fields returned when a protocol is provided + token_breakdowns: Optional[Dict[str, float]] = Field( + default=None, description="Breakdown of TVL by token", alias="tokenBreakdowns" + ) + mcap: Optional[float] = Field( + default=None, description="Market capitalization in USD" + ) + treasury: Optional[str] = Field( + default=None, description="Treasury address of the protocol" + ) + governance_id: Optional[List[str]] = Field( + default=None, description="List of governance IDs", alias="governanceID" + ) + github: Optional[List[str]] = Field( + default=None, description="List of GitHub repositories" + ) + wrong_liquidity: Optional[bool] = Field( + default=None, + description="Whether the protocol has wrong liquidity", + alias="wrongLiquidity", + ) + current_chain_tvls: Optional[Dict[str, float]] = Field( + default=None, + description="Total Value Locked in USD for each chain", + alias="currentChainTvls", + ) + tokens: Optional[List[DeFiLlamaProtocolTokens]] = Field( + default=None, description="Total Value Locked in USD for each token" + ) + tokens_in_usd: Optional[List[DeFiLlamaProtocolTokens]] = Field( + default=None, + description="Total Value Locked in USD for each token", + alias="tokensInUsd", + ) + is_parent_protocol: Optional[bool] = Field( + default=None, + description="Whether the protocol is a parent protocol", + alias="isParentProtocol", + ) + raises: Optional[List[DeFiLlamaProtocolRaisesData]] = Field( + default=None, description="List of raises the protocol has undergone" + ) + metrics: Optional[Dict[str, Any]] = Field( + default=None, description="Metrics of the protocol" + ) + other_protocols: Optional[List[str]] = Field( + default=None, description="List of other protocols", alias="otherProtocols" + ) + hallmarks: Optional[List[DeFiLlamaProtocolHallmarksData]] = Field( + default=None, description="List of hallmarks" + ) + + @field_validator("listed_at", mode="before") + def convert_timestamp_to_datetime(cls, v): + return datetime.fromtimestamp(v) if v else None + + @field_validator("tokens", "tokens_in_usd", mode="before") + def validate_tokens(cls, v): + return [DeFiLlamaProtocolTokens.model_validate(d) for d in v] if v else None + + @field_validator("tvl", mode="before") + def validate_tvl(cls, v): + if isinstance(v, list): + return [DeFiLlamaTvlData.model_validate(d) for d in v] + else: + return v + + @field_validator("chain_tvls", mode="before") + def validate_chain_tvls(cls, v): + return ( + { + k: ( + DeFiLlamaChainTvlsData.model_validate(v) + if isinstance(v, dict) + else v + ) + for k, v in v.items() + } + if v + else None + ) + + @field_validator("raises", mode="before") + def validate_raises(cls, v): + return [DeFiLlamaProtocolRaisesData.model_validate(d) for d in v] if v else None + + @field_validator("hallmarks", mode="before") + def validate_hallmarks(cls, v): + if isinstance(v, list): + return [DeFiLlamaProtocolHallmarksData.model_validate(d) for d in v] + else: + return v + + +class DeFiLlamaTvlHistoricalFetcher( + Fetcher[DeFiLlamaTvlHistoricalQueryParams, List[DeFiLlamaTvlHistoricalData]] +): + """DeFiLlama TVL Historical Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaTvlHistoricalQueryParams: + """Transform query parameters.""" + return DeFiLlamaTvlHistoricalQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaTvlHistoricalQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict[str, Any]]: + """Extract data from DeFiLlama API.""" + if query.symbol_type == "protocol": + if query.symbol is None: + return tvl.get_protocols() + else: + return [tvl.get_protocols(query.symbol)] + elif query.symbol_type == "chain": + if query.symbol is None: + return tvl.get_historical_chains_tvl() + else: + return tvl.get_historical_chains_tvl(query.symbol) + + return [] + + @staticmethod + def transform_data( + query: DeFiLlamaTvlHistoricalQueryParams, + data: List[Dict[str, Any]], + **kwargs: Any, + ) -> List[DeFiLlamaTvlHistoricalData]: + """Transform the data into the desired format.""" + data = [ + { + **d, + "hallmarks": ( + [{"date": item[0], "label": item[1]} for item in d["hallmarks"]] + if "hallmarks" in d + else None + ), + } + for d in data + ] + + return [DeFiLlamaTvlHistoricalData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/volumes_overview.py b/openbb_platform/providers/defillama/openbb_defillama/models/volumes_overview.py new file mode 100644 index 000000000000..50a9319a8e81 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/volumes_overview.py @@ -0,0 +1,242 @@ +"""DeFiLlama Volumes Overview Model.""" + +from datetime import datetime +from typing import Any, Dict, List, Literal, Optional, Union + +from defillama import volumes +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaVolumesOverviewQueryParams(QueryParams): + """DeFiLlama Volumes Overview Query. + + Source: https://defillama.com/docs/api + """ + + chain: Optional[str] = Field( + default=None, description="The chain to fetch data for." + ) + is_options: bool = Field( + default=False, description="Whether to fetch options dex data." + ) + all: bool = Field( + default=False, description="Whether to fetch details of all protocols." + ) + volume_type: Optional[Literal["premium", "notional"]] = Field( + default="premium", description="The type of volume to fetch." + ) + # NOTE: Disabled since the API does not return any data + # aggregate: Literal["daily", "total"] = Field( + # default="daily", + # description="Whether to fetch daily or total aggregate data.", + # ) + + +class DeFiLlamaChainsBreakdownData(Data): + """DeFiLlama Chains Breakdown Data.""" + + date: datetime = Field(description="The date of the data.") + chains: Dict[str, int] = Field(description="The chains data.") + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaProtocolsData(Data): + """DeFiLlama Protocols Data.""" + + total_24h: Optional[int] = Field( + default=None, description="Total value in the last 24 hours", alias="total24h" + ) + total_48h_to_24h: Optional[int] = Field( + default=None, + description="Total value from 48 to 24 hours ago", + alias="total48hto24h", + ) + total_7d: Optional[int] = Field( + default=None, description="Total value in the last 7 days", alias="total7d" + ) + total_14d_to_7d: Optional[int] = Field( + default=None, + description="Total value from 14 to 7 days ago", + alias="total14dto7d", + ) + total_60d_to_30d: Optional[int] = Field( + default=None, + description="Total value from 60 to 30 days ago", + alias="total60dto30d", + ) + total_30d: Optional[int] = Field( + default=None, description="Total value in the last 30 days", alias="total30d" + ) + total_1y: Optional[int] = Field( + default=None, description="Total value in the last year", alias="total1y" + ) + total_all_time: Optional[float] = Field( + default=None, description="Total value of all time", alias="totalAllTime" + ) + average_1y: Optional[float] = Field( + default=None, description="Average value over the last year", alias="average1y" + ) + change_1d: Optional[float] = Field( + default=None, description="Percentage change in the last day" + ) + change_7d: Optional[float] = Field( + default=None, description="Percentage change in the last 7 days" + ) + change_1m: Optional[float] = Field( + default=None, description="Percentage change in the last month" + ) + change_7d_over_7d: Optional[float] = Field( + default=None, + description="Percentage change over the last two 7-day periods", + alias="change_7dover7d", + ) + change_30d_over_30d: Optional[float] = Field( + default=None, + description="Percentage change over the last two 30-day periods", + alias="change_30dover30d", + ) + breakdown_24h: Optional[Dict[str, Dict[str, int]]] = Field( + default=None, + description="Breakdown of values in the last 24 hours", + alias="breakdown24h", + ) + defillama_id: int = Field(description="DeFiLlama ID", alias="defillamaId") + name: str = Field(description="Name of the chain or protocol") + display_name: str = Field( + description="Display name of the chain or protocol", alias="displayName" + ) + module: str = Field(description="Module name") + category: str = Field(description="Category of the chain or protocol") + logo: str = Field(description="URL to the logo image") + chains: List[str] = Field(description="List of chains") + protocol_type: str = Field(description="Type of protocol", alias="protocolType") + methodology_url: str = Field( + description="URL to the methodology document", alias="methodologyURL" + ) + methodology: Union[str, Dict[str, str]] = Field(description="Methodology details") + latest_fetch_is_ok: bool = Field( + description="Indicates if the latest fetch was successful", + alias="latestFetchIsOk", + ) + slug: str = Field(description="Slug identifier") + id: int = Field(description="Unique identifier") + + +class DeFiLlamaVolumesOverviewData(Data): + """DeFiLlama Volumes Overview Data.""" + + total_data_chart: List[Dict[datetime, int]] = Field( + description="Aggregated chart data.", alias="totalDataChart" + ) + total_data_chart_breakdown: List[DeFiLlamaChainsBreakdownData] = Field( + description="Aggregated chart data breakdown by chains.", + alias="totalDataChartBreakdown", + ) + breakdown_24h: Optional[Dict[str, Any]] = Field( + default=None, + description="Total volume collected in the last 24 hours broken down by chains", + alias="breakdown24h", + ) + total_24h: int = Field(description="Total 24h volume.", alias="total24h") + total_48h_to_24h: int = Field( + description="Total 48h to 24h volume.", alias="total48hto24h" + ) + total_7d: int = Field(description="Total 7d volume.", alias="total7d") + total_14d_to_7d: int = Field( + description="Total 14d to 7d volume.", alias="total14dto7d" + ) + total_60d_to_30d: int = Field( + description="Total 60d to 30d volume.", alias="total60dto30d" + ) + total_30d: int = Field(description="Total 30d volume.", alias="total30d") + total_1y: int = Field(description="Total 1y volume.", alias="total1y") + change_1d: float = Field(description="1d change in volume.") + change_7d: float = Field(description="7d change in volume.") + change_1m: float = Field(description="1m change in volume.") + change_7d_over_7d: float = Field( + description="7d change in volume over 7d.", alias="change_7dover7d" + ) + change_30d_over_30d: float = Field( + description="30d change in volume over 30d.", alias="change_30dover30d" + ) + protocols: Optional[List[DeFiLlamaProtocolsData]] = Field( + default=None, description="Protocols data.", alias="protocols" + ) + + @field_validator("total_data_chart_breakdown", mode="before") + def validate_total_data_chart_breakdown(cls, v): + return [DeFiLlamaChainsBreakdownData.model_validate(item) for item in v] + + @field_validator("protocols", mode="before") + def validate_protocols(cls, v): + return ( + [DeFiLlamaProtocolsData.model_validate(item) for item in v] if v else None + ) + + +class DeFiLlamaVolumesOverviewFetcher( + Fetcher[DeFiLlamaVolumesOverviewQueryParams, DeFiLlamaVolumesOverviewData] +): + """DeFiLlama Volumes Overview Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaVolumesOverviewQueryParams: + """Transform query parameters.""" + return DeFiLlamaVolumesOverviewQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaVolumesOverviewQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + + if query.is_options: + data = volumes.get_options_overview( + chain=query.chain, + exclude_total_data_chart=False, + exclude_total_data_chart_breakdown=False, + type=query.volume_type, + ) + else: + data = volumes.get_dex_overview( + chain=query.chain, + exclude_total_data_chart=False, + exclude_total_data_chart_breakdown=False, + ) + + if not query.all: + del data["protocols"] + return data + + return data + + @staticmethod + def transform_data( + query: DeFiLlamaVolumesOverviewQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaVolumesOverviewData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = {} + + for k, v in data.items(): + if k in ("chain", "allChains"): + continue + elif k == "totalDataChart": + transformed_data[k] = [ + {datetime.fromtimestamp(item[0]): item[1]} for item in v + ] + elif k == "totalDataChartBreakdown": + transformed_data[k] = [ + {"date": item[0], "chains": item[1]} for item in v + ] + else: + transformed_data[k] = v + + return DeFiLlamaVolumesOverviewData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/volumes_summary.py b/openbb_platform/providers/defillama/openbb_defillama/models/volumes_summary.py new file mode 100644 index 000000000000..df4db4e43dae --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/volumes_summary.py @@ -0,0 +1,217 @@ +"""DeFiLlama Volumes Summary Model.""" + +from datetime import datetime +from json.decoder import JSONDecodeError +from typing import Any, Dict, List, Literal, Optional, Union + +from defillama import volumes +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + + +class DeFiLlamaVolumesSummaryQueryParams(QueryParams): + """DeFiLlama Volumes Summary Query. + + Source: https://defillama.com/docs/api + """ + + protocol: str = Field(description="The protocol to fetch data for.") + is_options: bool = Field( + default=False, description="Whether to fetch options dex data." + ) + volume_type: Optional[Literal["premium", "notional"]] = Field( + default="premium", description="The type of volume to fetch." + ) + # NOTE: Disabled since the API does not return any data + # aggregate: Literal["daily", "total"] = Field( + # default="daily", + # description="Whether to fetch daily or total aggregate data.", + # ) + + +class DeFiLlamaChainsBreakdownData(Data): + """DeFiLlama Chains Breakdown Data.""" + + date: datetime = Field(description="The date of the data.") + chains: Dict[str, Dict[str, int]] = Field(description="The chains data.") + + @field_validator("date", mode="before") + def validate_date(cls, v): + return datetime.fromtimestamp(v) + + +class DeFiLlamaVolumesSummaryData(Data): + """DeFiLlama Volumes Summary Data.""" + + id: str = Field(description="The id of the protocol.") + name: str = Field(description="The name of the protocol.") + url: Optional[str] = Field(default=None, description="The url of the protocol.") + description: Optional[str] = Field( + default=None, description="The description of the protocol." + ) + logo: str = Field(description="The logo of the protocol.") + gecko_id: Optional[str] = Field( + default=None, description="The gecko id of the protocol." + ) + cmc_id: Optional[str] = Field( + default=None, description="The cmc id of the protocol.", alias="cmcId" + ) + chains: List[str] = Field(description="The chains of the protocol.") + twitter: Optional[str] = Field( + default=None, description="The twitter of the protocol." + ) + treasury: Optional[str] = Field( + default=None, description="The treasury of the protocol." + ) + governance_id: Optional[List[str]] = Field( + default=None, + description="The governance id of the protocol.", + alias="governanceID", + ) + github: Optional[List[str]] = Field( + default=None, description="The github of the protocol." + ) + child_protocols: Optional[List[str]] = Field( + default=None, + description="The child protocols of the protocol.", + alias="childProtocols", + ) + defillama_id: Optional[str] = Field( + default=None, + description="The defillama id of the protocol.", + alias="defillamaId", + ) + disabled: Optional[bool] = Field( + default=None, description="Whether the protocol is disabled." + ) + display_name: str = Field( + description="The display name of the protocol.", alias="displayName" + ) + module: Optional[str] = Field( + default=None, description="The module of the protocol." + ) + category: Optional[str] = Field( + default=None, description="The category of the protocol." + ) + methodology_url: Optional[str] = Field( + default=None, + description="The methodology URL of the protocol.", + alias="methodologyURL", + ) + methodology: Optional[Union[str, Dict[str, str]]] = Field( + default=None, description="The methodology of the protocol." + ) + oracles: Optional[List[str]] = Field( + default=None, description="The oracles of the protocol." + ) + forked_from: Optional[Union[str, List[str]]] = Field( + default=None, description="The forked from of the protocol.", alias="forkedFrom" + ) + audits: Optional[str] = Field( + default=None, description="The audits of the protocol." + ) + audit_note: Optional[str] = Field( + default=None, description="The audit note of the protocol.", alias="auditNote" + ) + address: Optional[str] = Field( + default=None, description="The address of the protocol." + ) + audit_links: Optional[List[str]] = Field( + default=None, description="The audit links of the protocol." + ) + version_key: Optional[str] = Field( + default=None, description="The version key of the protocol.", alias="versionKey" + ) + parent_protocol: Optional[str] = Field( + default=None, + description="The parent protocol of the protocol.", + alias="parentProtocol", + ) + latest_fetch_is_ok: bool = Field( + default=None, + description="Whether the latest fetch is ok.", + alias="latestFetchIsOk", + ) + slug: str = Field(description="The slug of the protocol.") + protocol_type: str = Field( + description="The protocol type of the protocol.", alias="protocolType" + ) + total_24h: int = Field( + description="The total 24h of the protocol.", alias="total24h" + ) + total_48h_to_24h: int = Field( + description="The total 48h to 24h of the protocol.", alias="total48hto24h" + ) + total_7d: int = Field(description="The total 7d of the protocol.", alias="total7d") + total_all_time: int = Field( + description="The total all time of the protocol.", alias="totalAllTime" + ) + total_data_chart: List[Dict[datetime, int]] = Field( + description="Aggregated chart data.", alias="totalDataChart" + ) + total_data_chart_breakdown: List[DeFiLlamaChainsBreakdownData] = Field( + description="Aggregated chart data breakdown by chains.", + alias="totalDataChartBreakdown", + ) + change_1d: Optional[float] = Field( + default=None, description="The change 1d of the protocol." + ) + + @field_validator("total_data_chart_breakdown", mode="before") + def validate_total_data_chart_breakdown(cls, v): + return [DeFiLlamaChainsBreakdownData.model_validate(item) for item in v] + + +class DeFiLlamaVolumesSummaryFetcher( + Fetcher[DeFiLlamaVolumesSummaryQueryParams, DeFiLlamaVolumesSummaryData] +): + """DeFiLlama Volumes Summary Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaVolumesSummaryQueryParams: + """Transform query parameters.""" + return DeFiLlamaVolumesSummaryQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaVolumesSummaryQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict[str, Any]: + """Extract data from DeFiLlama API.""" + + try: + if query.is_options: + return volumes.get_options_summary( + protocol=query.protocol, + type=query.volume_type, + ) + else: + return volumes.get_dex_summary( + protocol=query.protocol, + exclude_total_data_chart=False, + exclude_total_data_chart_breakdown=False, + ) + except JSONDecodeError as e: + raise EmptyDataError() from e + + @staticmethod + def transform_data( + query: DeFiLlamaVolumesSummaryQueryParams, data: Dict[str, Any], **kwargs: Any + ) -> DeFiLlamaVolumesSummaryData: + """Transform the data into the desired format.""" + transformed_data: Dict[str, Any] = dict(data) + + transformed_data["totalDataChart"] = [ + {datetime.fromtimestamp(item[0]): item[1]} + for item in data["totalDataChart"] + ] + transformed_data["totalDataChartBreakdown"] = [ + {"date": item[0], "chains": item[1]} + for item in data["totalDataChartBreakdown"] + ] + + return DeFiLlamaVolumesSummaryData.model_validate(transformed_data) diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/yields_historical.py b/openbb_platform/providers/defillama/openbb_defillama/models/yields_historical.py new file mode 100644 index 000000000000..1462a75bc180 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/yields_historical.py @@ -0,0 +1,71 @@ +"""Crypto Yields Historical Price Model.""" + +# pylint: disable=unused-argument + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from defillama import yields +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field + + +class DeFiLlamaYieldsHistoricalQueryParams(QueryParams): + """DeFiLlama Yields Historical Price Query. + + Source: https://defillama.com/docs/api + """ + + pool_id: str = Field(description="The pool id to get data for.") + + +class DeFiLlamaYieldsHistoricalData(Data): + """DeFiLlama Yields Historical Price Data.""" + + date: datetime = Field(description="The timestamp of the data.", alias="timestamp") + tvl_usd: float = Field(description="The TVL of the pool in USD.", alias="tvlUsd") + apy: float = Field(description="The APY of the pool.") + apy_base: float = Field(description="The base APY of the pool.", alias="apyBase") + apy_reward: Optional[float] = Field( + default=None, description="The reward APY of the pool.", alias="apyReward" + ) + il_7d: Optional[float] = Field( + default=None, description="The 7-day illiquidity of the pool.", alias="il7d" + ) + apy_base7d: Optional[float] = Field( + default=None, description="The 7-day base APY of the pool.", alias="apyBase7d" + ) + + +class DeFiLlamaYieldsHistoricalFetcher( + Fetcher[ + DeFiLlamaYieldsHistoricalQueryParams, + List[DeFiLlamaYieldsHistoricalData], + ] +): + """DeFiLlama Yields Historical Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaYieldsHistoricalQueryParams: + """Transform the query params.""" + + return DeFiLlamaYieldsHistoricalQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaYieldsHistoricalQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict]: + """Extract raw data from the DeFiLlama endpoint.""" + + return yields.get_pool_chart(query.pool_id) + + @staticmethod + def transform_data( + query: DeFiLlamaYieldsHistoricalQueryParams, data: List[Dict], **kwargs: Any + ) -> List[DeFiLlamaYieldsHistoricalData]: + """Transform the data into the desired format.""" + return [DeFiLlamaYieldsHistoricalData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/models/yields_pools.py b/openbb_platform/providers/defillama/openbb_defillama/models/yields_pools.py new file mode 100644 index 000000000000..45a8f6af2dd5 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/models/yields_pools.py @@ -0,0 +1,121 @@ +"""DefiLlama Yields Pools Model.""" + +from typing import Any, Dict, List, Optional + +from defillama import yields +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from pydantic import Field, field_validator + + +class DeFiLlamaYieldsPoolsQueryParams(QueryParams): + """DefiLlama Yields Pools Query. + + Source: https://defillama.com/docs/api + """ + + pass + + +class DeFiLlamaYieldsPoolsData(Data): + """DefiLlama Yields Pools Data.""" + + chain: str = Field(description="Chain of the yield") + project: str = Field(description="Project of the yield") + symbol: str = Field(description="Symbol of the yield") + pool: str = Field(description="Pool of the yield", alias="pool") + tvl_usd: float = Field(description="TVL of the yield in USD", alias="tvlUsd") + apy_base: Optional[float] = Field( + default=None, description="APY Base of the yield", alias="apyBase" + ) + apy_reward: Optional[float] = Field( + default=None, description="APY Reward of the yield", alias="apyReward" + ) + apy: float = Field(description="APY of the yield") + reward_tokens: Optional[List[str]] = Field( + default=None, description="Reward tokens of the yield", alias="rewardTokens" + ) + apy_pct_1d: Optional[float] = Field( + default=None, description="APY % 1D of the yield", alias="apyPct1D" + ) + apy_pct_7d: Optional[float] = Field( + default=None, description="APY % 7D of the yield", alias="apyPct7D" + ) + apy_pct_30d: Optional[float] = Field( + default=None, description="APY % 30D of the yield", alias="apyPct30D" + ) + stablecoin: bool = Field(description="Stablecoin of the yield") + il_risk: str = Field(description="IL Risk of the yield", alias="ilRisk") + exposure: str = Field(description="Exposure of the yield") + predictions: Dict[str, Any] = Field(description="Predictions of the yield") + pool_meta: Optional[str] = Field( + default=None, description="Pool metadata of the yield", alias="poolMeta" + ) + mu: float = Field(description="Mu of the yield") + sigma: float = Field(description="Sigma of the yield") + count: int = Field(description="Count of the yield") + outlier: bool = Field(description="Outlier of the yield") + underlying_tokens: Optional[List[str]] = Field( + default=None, + description="Underlying tokens of the yield", + alias="underlyingTokens", + ) + il_7d: Optional[float] = Field( + default=None, description="IL 7d of the yield", alias="il7d" + ) + apy_base_7d: Optional[float] = Field( + default=None, description="APY Base 7d of the yield", alias="apyBase7d" + ) + apy_mean_30d: Optional[float] = Field( + default=None, description="APY Mean 30d of the yield", alias="apyMean30d" + ) + volume_usd_1d: Optional[float] = Field( + default=None, description="Volume USD 1d of the yield", alias="volumeUsd1d" + ) + volume_usd_7d: Optional[float] = Field( + default=None, description="Volume USD 7d of the yield", alias="volumeUsd7d" + ) + apy_base_inception: Optional[float] = Field( + default=None, + description="APY Base Inception of the yield", + alias="apyBaseInception", + ) + + @field_validator("underlying_tokens", "reward_tokens", mode="before") + def validate_list(cls, v): + if isinstance(v, list) and any(item is None for item in v): + return None + return v + + +class DeFiLlamaYieldsPoolsFetcher( + Fetcher[DeFiLlamaYieldsPoolsQueryParams, List[DeFiLlamaYieldsPoolsData]] +): + """DeFiLlama Yields Pools Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> DeFiLlamaYieldsPoolsQueryParams: + """Transform query parameters.""" + return DeFiLlamaYieldsPoolsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeFiLlamaYieldsPoolsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict[str, Any]]: + """Extract data from DeFiLlama API.""" + if data := yields.get_pools(): + return data + + return {} + + @staticmethod + def transform_data( + query: DeFiLlamaYieldsPoolsQueryParams, + data: List[Dict[str, Any]], + **kwargs: Any, + ) -> List[DeFiLlamaYieldsPoolsData]: + """Transform the data into the desired format.""" + return [DeFiLlamaYieldsPoolsData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/defillama/openbb_defillama/utils/helpers.py b/openbb_platform/providers/defillama/openbb_defillama/utils/helpers.py new file mode 100644 index 000000000000..66c6a4c0cf02 --- /dev/null +++ b/openbb_platform/providers/defillama/openbb_defillama/utils/helpers.py @@ -0,0 +1,23 @@ +"""DeFiLlama Helpers Module.""" + +from typing import Any, Dict + +from openbb_core.app.model.abstract.error import OpenBBError +from openbb_core.provider.utils.errors import EmptyDataError +from openbb_core.provider.utils.helpers import amake_request + + +async def response_callback(response, _): + """Use callback for make_request.""" + data = await response.json() + if isinstance(data, dict) and "message" in data: + raise OpenBBError(f"DeFiLlama Error Message -> {data['message']}") + if isinstance(data, dict) and data.get("coins") == {}: + raise EmptyDataError("No data found") + return data + + +async def get_data(url: str, **kwargs: Any) -> Dict[str, Any]: + """Get data from DeFiLlama coins endpoint.""" + # pylint: disable=import-outside-toplevel + return await amake_request(url, response_callback=response_callback, **kwargs) diff --git a/openbb_platform/providers/defillama/poetry.lock b/openbb_platform/providers/defillama/poetry.lock new file mode 100644 index 000000000000..1e3ea6784381 --- /dev/null +++ b/openbb_platform/providers/defillama/poetry.lock @@ -0,0 +1,1674 @@ +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.3" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, +] + +[[package]] +name = "aiohttp" +version = "3.10.10" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, + {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, + {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, + {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, + {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, + {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, + {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, + {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, + {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, + {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, + {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, + {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, + {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, + {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.12.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.6.2.post1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.9" +files = [ + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "defillama-curl" +version = "2.0.1" +description = "Unofficial Python wrapper for DeFi Llama API client using the PyCurl module." +optional = false +python-versions = ">3.6" +files = [ + {file = "DeFiLlama-Curl-2.0.1.tar.gz", hash = "sha256:355edd61be13f377f8c768a9045749bd957f17a6ce53a57cf1eba834effce04f"}, + {file = "DeFiLlama_Curl-2.0.1-py3-none-any.whl", hash = "sha256:2c2954da398203adc0874132ca91a23a2cd7343682570f0af30c4c01b6564219"}, +] + +[package.dependencies] +pycurl = ">=7.44.1" + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.115.3" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.115.3-py3-none-any.whl", hash = "sha256:8035e8f9a2b0aa89cea03b6c77721178ed5358e1aea4cd8570d9466895c0638c"}, + {file = "fastapi-0.115.3.tar.gz", hash = "sha256:c091c6a35599c036d676fa24bd4a6e19fa30058d93d950216cdc672881f6f7db"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.42.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "frozenlist" +version = "1.5.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["chardet (>=2.2)", "genshi", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +optional = false +python-versions = "*" +files = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] + +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "numpy" +version = "2.0.2" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, + {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, + {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, + {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, + {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, + {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, + {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, + {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, + {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, + {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, +] + +[[package]] +name = "numpy" +version = "2.1.2" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d53720b726ec36a7f88dc873f0eec8447fbc93d93a8f079dfac2629598d6ee"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d3ca0a72dd8846eb6f7dfe8f19088060fcb76931ed592d29128e0219652884"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:fc44e3c68ff00fd991b59092a54350e6e4911152682b4782f68070985aa9e648"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7c1c60328bd964b53f8b835df69ae8198659e2b9302ff9ebb7de4e5a5994db3d"}, + {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdb606a7478f9ad91c6283e238544451e3a95f30fb5467fbf715964341a8a86"}, + {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d666cb72687559689e9906197e3bec7b736764df6a2e58ee265e360663e9baf7"}, + {file = "numpy-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6eef7a2dbd0abfb0d9eaf78b73017dbfd0b54051102ff4e6a7b2980d5ac1a03"}, + {file = "numpy-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:12edb90831ff481f7ef5f6bc6431a9d74dc0e5ff401559a71e5e4611d4f2d466"}, + {file = "numpy-2.1.2-cp310-cp310-win32.whl", hash = "sha256:a65acfdb9c6ebb8368490dbafe83c03c7e277b37e6857f0caeadbbc56e12f4fb"}, + {file = "numpy-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:860ec6e63e2c5c2ee5e9121808145c7bf86c96cca9ad396c0bd3e0f2798ccbe2"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b42a1a511c81cc78cbc4539675713bbcf9d9c3913386243ceff0e9429ca892fe"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:faa88bc527d0f097abdc2c663cddf37c05a1c2f113716601555249805cf573f1"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c82af4b2ddd2ee72d1fc0c6695048d457e00b3582ccde72d8a1c991b808bb20f"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:13602b3174432a35b16c4cfb5de9a12d229727c3dd47a6ce35111f2ebdf66ff4"}, + {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebec5fd716c5a5b3d8dfcc439be82a8407b7b24b230d0ad28a81b61c2f4659a"}, + {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1"}, + {file = "numpy-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cbba4b30bf31ddbe97f1c7205ef976909a93a66bb1583e983adbd155ba72ac2"}, + {file = "numpy-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e00ea6fc82e8a804433d3e9cedaa1051a1422cb6e443011590c14d2dea59146"}, + {file = "numpy-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5006b13a06e0b38d561fab5ccc37581f23c9511879be7693bd33c7cd15ca227c"}, + {file = "numpy-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:f1eb068ead09f4994dec71c24b2844f1e4e4e013b9629f812f292f04bd1510d9"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7bf0a4f9f15b32b5ba53147369e94296f5fffb783db5aacc1be15b4bf72f43b"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d0fcae4f0949f215d4632be684a539859b295e2d0cb14f78ec231915d644db"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f751ed0a2f250541e19dfca9f1eafa31a392c71c832b6bb9e113b10d050cb0f1"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bd33f82e95ba7ad632bc57837ee99dba3d7e006536200c4e9124089e1bf42426"}, + {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8cde4f11f0a975d1fd59373b32e2f5a562ade7cde4f85b7137f3de8fbb29a0"}, + {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df"}, + {file = "numpy-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ab4754d432e3ac42d33a269c8567413bdb541689b02d93788af4131018cbf366"}, + {file = "numpy-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e585c8ae871fd38ac50598f4763d73ec5497b0de9a0ab4ef5b69f01c6a046142"}, + {file = "numpy-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9c6c754df29ce6a89ed23afb25550d1c2d5fdb9901d9c67a16e0b16eaf7e2550"}, + {file = "numpy-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3"}, + {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8"}, + {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a"}, + {file = "numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98"}, + {file = "numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe"}, + {file = "numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a"}, + {file = "numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6"}, + {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8"}, + {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35"}, + {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62"}, + {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bdd407c40483463898b84490770199d5714dcc9dd9b792f6c6caccc523c00952"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:da65fb46d4cbb75cb417cddf6ba5e7582eb7bb0b47db4b99c9fe5787ce5d91f5"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c193d0b0238638e6fc5f10f1b074a6993cb13b0b431f64079a509d63d3aa8b7"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a7d80b2e904faa63068ead63107189164ca443b42dd1930299e0d1cb041cec2e"}, + {file = "numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c"}, +] + +[[package]] +name = "openbb-core" +version = "1.3.5" +description = "OpenBB package with core functionality." +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "openbb_core-1.3.5-py3-none-any.whl", hash = "sha256:bb93b2343eea06faff2644e03fd6ee7a09c37392c5486d96021aa9ae137e1a90"}, + {file = "openbb_core-1.3.5.tar.gz", hash = "sha256:d8abed2a351a0bca1b02fdaba439574b452a7a812d72d5e059f2dbedab55bd19"}, +] + +[package.dependencies] +aiohttp = ">=3.10.4,<4.0.0" +fastapi = ">=0.115,<0.116" +html5lib = ">=1.1,<2.0" +importlib-metadata = ">=6.8.0" +pandas = ">=1.5.3" +posthog = ">=3.3.1,<4.0.0" +pydantic = ">=2.5.1,<3.0.0" +pyjwt = ">=2.8.0,<3.0.0" +python-dotenv = ">=1.0.0,<2.0.0" +python-multipart = ">=0.0.7,<0.0.8" +requests = ">=2.32.1,<3.0.0" +ruff = ">=0.7,<0.8" +uuid7 = ">=0.1.0,<0.2.0" +uvicorn = ">=0.32.0,<0.33.0" +websockets = ">=13.0,<14.0" + +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "posthog" +version = "3.7.0" +description = "Integrate PostHog into any python application." +optional = false +python-versions = "*" +files = [ + {file = "posthog-3.7.0-py2.py3-none-any.whl", hash = "sha256:3555161c3a9557b5666f96d8e1f17f410ea0f07db56e399e336a1656d4e5c722"}, + {file = "posthog-3.7.0.tar.gz", hash = "sha256:b095d4354ba23f8b346ab5daed8ecfc5108772f922006982dfe8b2d29ebc6e0e"}, +] + +[package.dependencies] +backoff = ">=1.10.0" +monotonic = ">=1.5" +python-dateutil = ">2.1" +requests = ">=2.7,<3.0" +six = ">=1.5" + +[package.extras] +dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] +sentry = ["django", "sentry-sdk"] +test = ["coverage", "django", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] + +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + +[[package]] +name = "pycurl" +version = "7.45.3" +description = "PycURL -- A Python Interface To The cURL library" +optional = false +python-versions = ">=3.5" +files = [ + {file = "pycurl-7.45.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86f66d334deaaab20a576fb785587566081407adc703318203fe26e43277ef12"}, + {file = "pycurl-7.45.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:205983e87d6aa0b6e93ec7320060de44efaa905ecc5d13f70cbe38c65684c5c4"}, + {file = "pycurl-7.45.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbd4a6b8654b779089c5a44af1c65c1419c2cd60718780df6d8f354eb35d6d55"}, + {file = "pycurl-7.45.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5ebc6a0ac60c371a9efaf7d55dec5820f76fdafb43a3be1e390011339dc329ae"}, + {file = "pycurl-7.45.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2facab1c35600088cb82b5b093bd700bfbd1e3191deab24f7d1803d9dc5b76fc"}, + {file = "pycurl-7.45.3-cp310-cp310-win32.whl", hash = "sha256:7cfca02d70579853041063e53ca713d31161b8831b98d4f68c3554dc0448beec"}, + {file = "pycurl-7.45.3-cp310-cp310-win_amd64.whl", hash = "sha256:8451e8475051f16eb4776380384699cb8ddd10ea8410bcbfaee5a6fc4c046de6"}, + {file = "pycurl-7.45.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1610cc45b5bc8b39bc18b981d0473e59ef41226ee467eaa8fbfc7276603ef5af"}, + {file = "pycurl-7.45.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c854885398410fa6e88fc29f7a420a3c13b88bae9b4e10a804437b582e24f58b"}, + {file = "pycurl-7.45.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:921c9db0c3128481954f625b3b1bc10c730100aa944d54643528f716676439ee"}, + {file = "pycurl-7.45.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:483f3aa5d1bc8cff5657ad96f68e1d89281f971a7b6aa93408a31e3199981ea9"}, + {file = "pycurl-7.45.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1e0d32d6ed3a7ba13dbbd3a6fb50ca76c40c70e6bc6fe347f90677478d3422c7"}, + {file = "pycurl-7.45.3-cp311-cp311-win32.whl", hash = "sha256:beaaa4450e23d41dd0c2f2f47a4f8a171210271543550c2c556090c7eeea88f5"}, + {file = "pycurl-7.45.3-cp311-cp311-win_amd64.whl", hash = "sha256:dd33fd9de8907a6275c70113124aeb7eea672c1324f5d5423f203738b341697d"}, + {file = "pycurl-7.45.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0c41a172d5e8a5cdd8328cc8134f47b2a57960ac677f7cda8520eaa9fbe7d990"}, + {file = "pycurl-7.45.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13006b62c157bb4483c58e1abdced6df723c9399255a4f5f6bb7f8e425106679"}, + {file = "pycurl-7.45.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27f4c5c20c86a9a823677316724306fb1ce3b25ec568efd52026dc6c563e5b29"}, + {file = "pycurl-7.45.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c2c246bc29e8762ff4c8a833ac5b4da4c797d16ab138286e8aec9b0c0a0da2d4"}, + {file = "pycurl-7.45.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3d07c5daef2d0d85949e32ec254ee44232bb57febb0634194379dd14d1ff4f87"}, + {file = "pycurl-7.45.3-cp312-cp312-win32.whl", hash = "sha256:9f7afe5ef0e4750ac4515baebc251ee94aaefe5de6e2e8a24668473128d69904"}, + {file = "pycurl-7.45.3-cp312-cp312-win_amd64.whl", hash = "sha256:3648ed9a57a6b704673faeab3dc64d1469cc69f2bc1ed8227ffa0f84e147c500"}, + {file = "pycurl-7.45.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c0915ea139f66a289edc4f9de10cb45078af1bb950491c5612969864236a2e7e"}, + {file = "pycurl-7.45.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:43c5e61a58783ddf78ef84949f6bb6e52e092a13ec67678e9a9e21071ecf5b80"}, + {file = "pycurl-7.45.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bf613844a1647fe3d2bba1f5c9c96a62a85280123a57a8a0c8d2f37d518bc10a"}, + {file = "pycurl-7.45.3-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:936afd9c5ff7fe7457065e878a279811787778f472f9a4e8c5df79e7728358e2"}, + {file = "pycurl-7.45.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:dbf816a6d0cb71e7fd06609246bbea4eaf100649d9decf49e4eb329594f70be7"}, + {file = "pycurl-7.45.3-cp38-cp38-win32.whl", hash = "sha256:2c8a2ce568193f9f84763717d8961cec0db4ec1aa08c6bcf4d90da5eb72bec86"}, + {file = "pycurl-7.45.3-cp38-cp38-win_amd64.whl", hash = "sha256:80ac7c17e69ca6b76ccccb4255f7c29a2a36e5b69eb10c2adba82135d43afe8c"}, + {file = "pycurl-7.45.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fa7751b614d9aa82d7a0f49ca90924c29c6cedf85a2f8687fb6a772dbfe48711"}, + {file = "pycurl-7.45.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b129e9ee07f80b4af957607917af46ab517b0c4e746692f6d9e50e973edba8d8"}, + {file = "pycurl-7.45.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a0f920582b8713ca87d5a288a7532607bc4454275d733fc880650d602dbe3c67"}, + {file = "pycurl-7.45.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c7c13e4268550cde14a6f4743cc8bd8c035d4cd36514d58eff70276d68954b6f"}, + {file = "pycurl-7.45.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:0f0e1251a608ffd75fc502f4014442e554c67d3d7a1b0a839c35efb6ad2f8bf8"}, + {file = "pycurl-7.45.3-cp39-cp39-win32.whl", hash = "sha256:51a40a56c58e63dac6145829f9e9bd66e5867a9f0741bcb9ffefab619851d44f"}, + {file = "pycurl-7.45.3-cp39-cp39-win_amd64.whl", hash = "sha256:e08a06802c8c8a9d04cf3319f9230ec09062c55d2550bd48f8ada1df1431adcf"}, + {file = "pycurl-7.45.3.tar.gz", hash = "sha256:8c2471af9079ad798e1645ec0b0d3d4223db687379d17dd36a70637449f81d6b"}, +] + +[[package]] +name = "pydantic" +version = "2.9.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" +typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.23.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyjwt" +version = "2.9.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, + {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.7" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python_multipart-0.0.7-py3-none-any.whl", hash = "sha256:b1fef9a53b74c795e2347daac8c54b252d9e0df9c619712691c1cc8021bd3c49"}, + {file = "python_multipart-0.0.7.tar.gz", hash = "sha256:288a6c39b06596c1b988bb6794c6fbc80e6c369e35e5062637df256bee0c9af9"}, +] + +[package.extras] +dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==2.2.0)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"] + +[[package]] +name = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruff" +version = "0.7.1" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"}, + {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, + {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"}, + {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"}, + {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, + {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, + {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.41.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"}, + {file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uuid7" +version = "0.1.0" +description = "UUID version 7, generating time-sorted UUIDs with 200ns time resolution and 48 bits of randomness" +optional = false +python-versions = ">=3.7" +files = [ + {file = "uuid7-0.1.0-py2.py3-none-any.whl", hash = "sha256:5e259bb63c8cb4aded5927ff41b444a80d0c7124e8a0ced7cf44efa1f5cccf61"}, + {file = "uuid7-0.1.0.tar.gz", hash = "sha256:8c57aa32ee7456d3cc68c95c4530bc571646defac01895cfc73545449894a63c"}, +] + +[[package]] +name = "uvicorn" +version = "0.32.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, + {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "websockets" +version = "13.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, + {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, + {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, + {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, + {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, + {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, + {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, + {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, + {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, + {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, + {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, + {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, + {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, + {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, + {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, + {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, + {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, + {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, + {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, + {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, + {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, + {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, + {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, + {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, +] + +[[package]] +name = "yarl" +version = "1.16.0" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +files = [ + {file = "yarl-1.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32468f41242d72b87ab793a86d92f885355bcf35b3355aa650bfa846a5c60058"}, + {file = "yarl-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:234f3a3032b505b90e65b5bc6652c2329ea7ea8855d8de61e1642b74b4ee65d2"}, + {file = "yarl-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a0296040e5cddf074c7f5af4a60f3fc42c0237440df7bcf5183be5f6c802ed5"}, + {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6c14dd7c7c0badba48157474ea1f03ebee991530ba742d381b28d4f314d6f3"}, + {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b140e532fe0266003c936d017c1ac301e72ee4a3fd51784574c05f53718a55d8"}, + {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:019f5d58093402aa8f6661e60fd82a28746ad6d156f6c5336a70a39bd7b162b9"}, + {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c42998fd1cbeb53cd985bff0e4bc25fbe55fd6eb3a545a724c1012d69d5ec84"}, + {file = "yarl-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7c30fb38c300fe8140df30a046a01769105e4cf4282567a29b5cdb635b66c4"}, + {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e49e0fd86c295e743fd5be69b8b0712f70a686bc79a16e5268386c2defacaade"}, + {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b9ca7b9147eb1365c8bab03c003baa1300599575effad765e0b07dd3501ea9af"}, + {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27e11db3f1e6a51081a981509f75617b09810529de508a181319193d320bc5c7"}, + {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8994c42f4ca25df5380ddf59f315c518c81df6a68fed5bb0c159c6cb6b92f120"}, + {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:542fa8e09a581bcdcbb30607c7224beff3fdfb598c798ccd28a8184ffc18b7eb"}, + {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2bd6a51010c7284d191b79d3b56e51a87d8e1c03b0902362945f15c3d50ed46b"}, + {file = "yarl-1.16.0-cp310-cp310-win32.whl", hash = "sha256:178ccb856e265174a79f59721031060f885aca428983e75c06f78aa24b91d929"}, + {file = "yarl-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe8bba2545427418efc1929c5c42852bdb4143eb8d0a46b09de88d1fe99258e7"}, + {file = "yarl-1.16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d8643975a0080f361639787415a038bfc32d29208a4bf6b783ab3075a20b1ef3"}, + {file = "yarl-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:676d96bafc8c2d0039cea0cd3fd44cee7aa88b8185551a2bb93354668e8315c2"}, + {file = "yarl-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9525f03269e64310416dbe6c68d3b23e5d34aaa8f47193a1c45ac568cecbc49"}, + {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b37d5ec034e668b22cf0ce1074d6c21fd2a08b90d11b1b73139b750a8b0dd97"}, + {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f32c4cb7386b41936894685f6e093c8dfaf0960124d91fe0ec29fe439e201d0"}, + {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b8e265a0545637492a7e12fd7038370d66c9375a61d88c5567d0e044ded9202"}, + {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:789a3423f28a5fff46fbd04e339863c169ece97c827b44de16e1a7a42bc915d2"}, + {file = "yarl-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1d1f45e3e8d37c804dca99ab3cf4ab3ed2e7a62cd82542924b14c0a4f46d243"}, + {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:621280719c4c5dad4c1391160a9b88925bb8b0ff6a7d5af3224643024871675f"}, + {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed097b26f18a1f5ff05f661dc36528c5f6735ba4ce8c9645e83b064665131349"}, + {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2f1fe2b2e3ee418862f5ebc0c0083c97f6f6625781382f828f6d4e9b614eba9b"}, + {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:87dd10bc0618991c66cee0cc65fa74a45f4ecb13bceec3c62d78ad2e42b27a16"}, + {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4199db024b58a8abb2cfcedac7b1292c3ad421684571aeb622a02f242280e8d6"}, + {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:99a9dcd4b71dd5f5f949737ab3f356cfc058c709b4f49833aeffedc2652dac56"}, + {file = "yarl-1.16.0-cp311-cp311-win32.whl", hash = "sha256:a9394c65ae0ed95679717d391c862dece9afacd8fa311683fc8b4362ce8a410c"}, + {file = "yarl-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b9101f528ae0f8f65ac9d64dda2bb0627de8a50344b2f582779f32fda747c1d"}, + {file = "yarl-1.16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ffb7c129707dd76ced0a4a4128ff452cecf0b0e929f2668ea05a371d9e5c104"}, + {file = "yarl-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1a5e9d8ce1185723419c487758d81ac2bde693711947032cce600ca7c9cda7d6"}, + {file = "yarl-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d743e3118b2640cef7768ea955378c3536482d95550222f908f392167fe62059"}, + {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26768342f256e6e3c37533bf9433f5f15f3e59e3c14b2409098291b3efaceacb"}, + {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1b0796168b953bca6600c5f97f5ed407479889a36ad7d17183366260f29a6b9"}, + {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:858728086914f3a407aa7979cab743bbda1fe2bdf39ffcd991469a370dd7414d"}, + {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5570e6d47bcb03215baf4c9ad7bf7c013e56285d9d35013541f9ac2b372593e7"}, + {file = "yarl-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66ea8311422a7ba1fc79b4c42c2baa10566469fe5a78500d4e7754d6e6db8724"}, + {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:649bddcedee692ee8a9b7b6e38582cb4062dc4253de9711568e5620d8707c2a3"}, + {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a91654adb7643cb21b46f04244c5a315a440dcad63213033826549fa2435f71"}, + {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b439cae82034ade094526a8f692b9a2b5ee936452de5e4c5f0f6c48df23f8604"}, + {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:571f781ae8ac463ce30bacebfaef2c6581543776d5970b2372fbe31d7bf31a07"}, + {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:aa7943f04f36d6cafc0cf53ea89824ac2c37acbdb4b316a654176ab8ffd0f968"}, + {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1a5cf32539373ff39d97723e39a9283a7277cbf1224f7aef0c56c9598b6486c3"}, + {file = "yarl-1.16.0-cp312-cp312-win32.whl", hash = "sha256:a5b6c09b9b4253d6a208b0f4a2f9206e511ec68dce9198e0fbec4f160137aa67"}, + {file = "yarl-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1208ca14eed2fda324042adf8d6c0adf4a31522fa95e0929027cd487875f0240"}, + {file = "yarl-1.16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5ace0177520bd4caa99295a9b6fb831d0e9a57d8e0501a22ffaa61b4c024283"}, + {file = "yarl-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7118bdb5e3ed81acaa2095cba7ec02a0fe74b52a16ab9f9ac8e28e53ee299732"}, + {file = "yarl-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38fec8a2a94c58bd47c9a50a45d321ab2285ad133adefbbadf3012c054b7e656"}, + {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8791d66d81ee45866a7bb15a517b01a2bcf583a18ebf5d72a84e6064c417e64b"}, + {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cf936ba67bc6c734f3aa1c01391da74ab7fc046a9f8bbfa230b8393b90cf472"}, + {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1aab176dd55b59f77a63b27cffaca67d29987d91a5b615cbead41331e6b7428"}, + {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d0759004c08abd5d1b81300a91d18c8577c6389300bed1c7c11675105a44d"}, + {file = "yarl-1.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bc22e00edeb068f71967ab99081e9406cd56dbed864fc3a8259442999d71552"}, + {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35b4f7842154176523e0a63c9b871168c69b98065d05a4f637fce342a6a2693a"}, + {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7ace71c4b7a0c41f317ae24be62bb61e9d80838d38acb20e70697c625e71f120"}, + {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8f639e3f5795a6568aa4f7d2ac6057c757dcd187593679f035adbf12b892bb00"}, + {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e8be3aff14f0120ad049121322b107f8a759be76a6a62138322d4c8a337a9e2c"}, + {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:122d8e7986043d0549e9eb23c7fd23be078be4b70c9eb42a20052b3d3149c6f2"}, + {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0fd9c227990f609c165f56b46107d0bc34553fe0387818c42c02f77974402c36"}, + {file = "yarl-1.16.0-cp313-cp313-win32.whl", hash = "sha256:595ca5e943baed31d56b33b34736461a371c6ea0038d3baec399949dd628560b"}, + {file = "yarl-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:921b81b8d78f0e60242fb3db615ea3f368827a76af095d5a69f1c3366db3f596"}, + {file = "yarl-1.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab2b2ac232110a1fdb0d3ffcd087783edd3d4a6ced432a1bf75caf7b7be70916"}, + {file = "yarl-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f8713717a09acbfee7c47bfc5777e685539fefdd34fa72faf504c8be2f3df4e"}, + {file = "yarl-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdcffe1dbcb4477d2b4202f63cd972d5baa155ff5a3d9e35801c46a415b7f71a"}, + {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a91217208306d82357c67daeef5162a41a28c8352dab7e16daa82e3718852a7"}, + {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ab3ed42c78275477ea8e917491365e9a9b69bb615cb46169020bd0aa5e2d6d3"}, + {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:707ae579ccb3262dfaef093e202b4c3fb23c3810e8df544b1111bd2401fd7b09"}, + {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7a852d1cd0b8d8b37fc9d7f8581152add917a98cfe2ea6e241878795f917ae"}, + {file = "yarl-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3f1cc3d3d4dc574bebc9b387f6875e228ace5748a7c24f49d8f01ac1bc6c31b"}, + {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5ff96da263740779b0893d02b718293cc03400c3a208fc8d8cd79d9b0993e532"}, + {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3d375a19ba2bfe320b6d873f3fb165313b002cef8b7cc0a368ad8b8a57453837"}, + {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:62c7da0ad93a07da048b500514ca47b759459ec41924143e2ddb5d7e20fd3db5"}, + {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:147b0fcd0ee33b4b5f6edfea80452d80e419e51b9a3f7a96ce98eaee145c1581"}, + {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:504e1fe1cc4f170195320eb033d2b0ccf5c6114ce5bf2f617535c01699479bca"}, + {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bdcf667a5dec12a48f669e485d70c54189f0639c2157b538a4cffd24a853624f"}, + {file = "yarl-1.16.0-cp39-cp39-win32.whl", hash = "sha256:e9951afe6557c75a71045148890052cb942689ee4c9ec29f5436240e1fcc73b7"}, + {file = "yarl-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d7aaa8ff95d0840e289423e7dc35696c2b058d635f945bf05b5cd633146b027"}, + {file = "yarl-1.16.0-py3-none-any.whl", hash = "sha256:e6980a558d8461230c457218bd6c92dfc1d10205548215c2c21d79dc8d0a96f3"}, + {file = "yarl-1.16.0.tar.gz", hash = "sha256:b6f687ced5510a9a2474bbae96a4352e5ace5fa34dc44a217b0537fec1db00b4"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.0" + +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.13" +content-hash = "c3b667a9eca13d2213539b02e46a77204c744b7a68e7f334f50cc0714b904c37" diff --git a/openbb_platform/providers/defillama/pyproject.toml b/openbb_platform/providers/defillama/pyproject.toml new file mode 100644 index 000000000000..77421ecf00a8 --- /dev/null +++ b/openbb_platform/providers/defillama/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "openbb-defillama" +version = "1.0.0" +description = "DeFiLlama extension for OpenBB" +authors = ["Pratyush Shukla "] +license = "AGPL-3.0-only" +readme = "README.md" +packages = [{ include = "openbb_defillama" }] + +[tool.poetry.dependencies] +python = ">=3.9,<3.13" +openbb-core = "^1.3.2" +defillama-curl = "^2.0.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.plugins."openbb_provider_extension"] +defillama = "openbb_defillama:defillama_provider" diff --git a/openbb_platform/providers/defillama/tests/__init__.py b/openbb_platform/providers/defillama/tests/__init__.py new file mode 100644 index 000000000000..a171e9a21243 --- /dev/null +++ b/openbb_platform/providers/defillama/tests/__init__.py @@ -0,0 +1 @@ +"""DeFiLlama tests.""" diff --git a/openbb_platform/providers/defillama/tests/test_defillama_fetchers.py b/openbb_platform/providers/defillama/tests/test_defillama_fetchers.py new file mode 100644 index 000000000000..585bdec0a04a --- /dev/null +++ b/openbb_platform/providers/defillama/tests/test_defillama_fetchers.py @@ -0,0 +1,276 @@ +"""Unit tests for DeFiLlama provider modules.""" + +import pytest +from openbb_core.app.service.user_service import UserService +from openbb_defillama.models.coins_block_timestamp import ( + DeFiLlamaCoinsBlockTimestampFetcher, +) +from openbb_defillama.models.coins_change import DeFiLlamaCoinsChangeFetcher +from openbb_defillama.models.coins_chart import DeFiLlamaCoinsChartFetcher +from openbb_defillama.models.coins_current import DeFiLlamaCoinsCurrentFetcher +from openbb_defillama.models.coins_first import DeFiLlamaCoinsFirstFetcher +from openbb_defillama.models.coins_historical import DeFiLlamaCoinsHistoricalFetcher +from openbb_defillama.models.fees_overview import DeFiLlamaFeesOverviewFetcher +from openbb_defillama.models.fees_summary import DeFiLlamaFeesSummaryFetcher +from openbb_defillama.models.revenue_overview import DeFiLlamaRevenueOverviewFetcher +from openbb_defillama.models.revenue_summary import DeFiLlamaRevenueSummaryFetcher +from openbb_defillama.models.stablecoins_charts import DeFiLlamaStablecoinsChartsFetcher +from openbb_defillama.models.stablecoins_current import ( + DeFiLlamaStablecoinsCurrentFetcher, +) +from openbb_defillama.models.stablecoins_distribution import ( + DeFiLlamaStablecoinsDistributionFetcher, +) +from openbb_defillama.models.stablecoins_historical import ( + DeFiLlamaStablecoinsHistoricalFetcher, +) +from openbb_defillama.models.stablecoins_list import DeFiLlamaStablecoinsListFetcher +from openbb_defillama.models.tvl_chains import DeFiLlamaTvlChainsFetcher +from openbb_defillama.models.tvl_current import DeFiLlamaTvlCurrentFetcher +from openbb_defillama.models.tvl_historical import DeFiLlamaTvlHistoricalFetcher +from openbb_defillama.models.volumes_overview import DeFiLlamaVolumesOverviewFetcher +from openbb_defillama.models.volumes_summary import DeFiLlamaVolumesSummaryFetcher +from openbb_defillama.models.yields_historical import DeFiLlamaYieldsHistoricalFetcher +from openbb_defillama.models.yields_pools import DeFiLlamaYieldsPoolsFetcher + +test_credentials = UserService().default_user_settings.credentials.model_dump( + mode="json" +) + + +@pytest.fixture(scope="module") +def vcr_config(): + return { + "filter_headers": [("User-Agent", None)], + "filter_query_parameters": [ + ("token", "MOCK_TOKEN"), + ], + } + + +@pytest.mark.record_http +def test_defillama_coins_block_timestamp_fetcher(credentials=test_credentials): + params = { + "chain": "ethereum", + "timestamp": 1704047400, + } + + fetcher = DeFiLlamaCoinsBlockTimestampFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_coins_change_fetcher(credentials=test_credentials): + params = { + "token": "coingecko:ethereum", + "timestamp": 1704047400, + "look_forward": False, + "period": "24h", + } + + fetcher = DeFiLlamaCoinsChangeFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_coins_chart_fetcher(credentials=test_credentials): + params = { + "token": "coingecko:ethereum", + "start_date": 1704047400, + } + + fetcher = DeFiLlamaCoinsChartFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_coins_current_fetcher(credentials=test_credentials): + params = { + "token": "coingecko:ethereum", + } + + fetcher = DeFiLlamaCoinsCurrentFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_coins_first_fetcher(credentials=test_credentials): + params = { + "token": "coingecko:ethereum", + } + + fetcher = DeFiLlamaCoinsFirstFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_coins_historical_fetcher(credentials=test_credentials): + params = { + "token": "coingecko:ethereum", + "timestamp": 1704047400, + } + + fetcher = DeFiLlamaCoinsHistoricalFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_fees_overview_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaFeesOverviewFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_fees_summary_fetcher(credentials=test_credentials): + params = { + "protocol": "litecoin", + } + + fetcher = DeFiLlamaFeesSummaryFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_revenue_overview_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaRevenueOverviewFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_revenue_summary_fetcher(credentials=test_credentials): + params = { + "protocol": "litecoin", + } + + fetcher = DeFiLlamaRevenueSummaryFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_stablecoins_charts_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaStablecoinsChartsFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_stablecoins_current_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaStablecoinsCurrentFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_stablecoins_distribution_fetcher(credentials=test_credentials): + params = { + "stablecoin": "1", + } + + fetcher = DeFiLlamaStablecoinsDistributionFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_stablecoins_historical_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaStablecoinsHistoricalFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_stablecoins_list_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaStablecoinsListFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_tvl_chains_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaTvlChainsFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_tvl_current_fetcher(credentials=test_credentials): + params = { + "symbol": "uniswap", + } + + fetcher = DeFiLlamaTvlCurrentFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_tvl_historical_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaTvlHistoricalFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_volumes_overview_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaVolumesOverviewFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_volumes_summary_fetcher(credentials=test_credentials): + params = { + "protocol": "uniswap", + } + + fetcher = DeFiLlamaVolumesSummaryFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_yields_historical_fetcher(credentials=test_credentials): + params = { + "pool_id": "747c1d2a-c668-4682-b9f9-296708a3dd90", + } + + fetcher = DeFiLlamaYieldsHistoricalFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_defillama_yields_pools_fetcher(credentials=test_credentials): + params = {} + + fetcher = DeFiLlamaYieldsPoolsFetcher() + result = fetcher.test(params, credentials) + assert result is None diff --git a/openbb_platform/providers/yfinance/tests/test_yfinance_helpers.py b/openbb_platform/providers/yfinance/tests/test_yfinance_helpers.py index 2cb649fb6fd4..3a4cc6112221 100644 --- a/openbb_platform/providers/yfinance/tests/test_yfinance_helpers.py +++ b/openbb_platform/providers/yfinance/tests/test_yfinance_helpers.py @@ -33,4 +33,4 @@ def test_df_transform_numbers(): ) transformed = df_transform_numbers(data, ["Value", "% Change"]) assert transformed["Value"].equals(pd.Series([1e6, 2.5e9, 3e12])) - assert transformed["% Change"].equals(pd.Series([1/100, -2/100, 3.5/100])) + assert transformed["% Change"].equals(pd.Series([1 / 100, -2 / 100, 3.5 / 100])) diff --git a/openbb_platform/pyproject.toml b/openbb_platform/pyproject.toml index f2102d1d220f..f04023e62d1e 100644 --- a/openbb_platform/pyproject.toml +++ b/openbb_platform/pyproject.toml @@ -45,6 +45,7 @@ openbb-regulators = "^1.3.4" openbb-alpha-vantage = { version = "^1.3.4", optional = true } openbb-biztoc = { version = "^1.3.4", optional = true } openbb-cboe = { version = "^1.3.4", optional = true } +openbb-defillama = { version = "^1.0.0", optional = true } openbb-ecb = { version = "^1.3.4", optional = true } openbb-finra = { version = "^1.3.4", optional = true } openbb-finviz = { version = "^1.2.4", optional = true } @@ -67,6 +68,7 @@ alpha_vantage = ["openbb-alpha-vantage"] biztoc = ["openbb-biztoc"] cboe = ["openbb-cboe"] charting = ["openbb-charting"] +defillama = ["openbb-defillama"] ecb = ["openbb-ecb"] econometrics = ["openbb-econometrics"] finra = ["openbb-finra"] @@ -88,6 +90,7 @@ all = [ "openbb-biztoc", "openbb-cboe", "openbb-charting", + "openbb-defillama", "openbb-ecb", "openbb-econometrics", "openbb-finra",