Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ This client provides methods to interact with the Furthermore API endpoints for
# .venv\Scripts\activate # On Windows
```

3. **Install dependencies:**
With `requests` now listed in your `pyproject.toml`, you can install all project dependencies (including `requests` and the project itself in editable mode if desired) using:
3. **Install dependencies and the project for development:**
To install dependencies and make your project importable (recommended for development, especially for running examples):
```bash
uv pip install .
uv pip install -e .
```
Alternatively, to synchronize your environment with the `pyproject.toml` (recommended after pulling changes or modifying dependencies):
This installs the project in "editable" mode. If you only want to install dependencies as defined (e.g., for a non-development setup or CI), you can use:
```bash
uv sync
# or uv pip install .
```

4. **Set the API Key:**
Expand All @@ -56,7 +57,7 @@ Here's a basic example of how to use the `FurthermoreClient`:
```python
import os
import logging
from src.client import FurthermoreClient # Adjust import based on your project structure
from furthermore_py import FurthermoreClient # Adjust import based on your project structure

if __name__ == '__main__':
# Ensure API key is set
Expand Down Expand Up @@ -111,11 +112,14 @@ if __name__ == '__main__':

### How to Run the Example Script

To run the example script (e.g., if it's `src/examples/basic.py`), ensure your virtual environment is activated and you are in the project root directory:
To run the example script (e.g., `src/examples/basic.py`):

```bash
python src/examples/basic.py
```
1. Ensure your virtual environment is activated (`source .venv/bin/activate`).
2. Make sure the package is installed in editable mode from the project root directory: `uv pip install -e .`
3. Run the script from the project root directory:
```bash
python src/examples/basic.py
```

## API Documentation

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "furthermore-py"
version = "0.1.2"
version = "0.1.3"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
6 changes: 3 additions & 3 deletions src/examples/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

import requests

from src.client import FurthermoreClient
from furthermore_py import FurthermoreClient

# Basic Example - Run this script directly to test the collector
if __name__ == "__main__":
print("Attempting to use FurthermoreCo lector (Example Usage)...")
print("Attempting to use FurthermoreCollector (Example Usage)...")

api_key_present = os.getenv(FurthermoreClient.FURTHERMORE_API_KEY_ENV_VAR)
if not api_key_present:
Expand All @@ -21,7 +21,7 @@
print(
f"Environment variable '{FurthermoreClient.FURTHERMORE_API_KEY_ENV_VAR}' is set."
)
example_logger = logging.getLogger("FurthermoreClientExample")
example_logger = logging.getLogger("ClientExample")
example_logger.setLevel(logging.INFO)
if not example_logger.hasHandlers():
ch = logging.StreamHandler()
Expand Down
File renamed without changes.
66 changes: 39 additions & 27 deletions src/client/client.py → src/furthermore_py/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,42 @@ def _make_request(
)
raise

def get_articles(
def _extract_and_add_name(
self, data: dict | None, path: tuple[str, ...], target_set: set[str]
):
"""
Safely extracts a name from a nested dictionary path and adds it to the target set
if the name is a non-empty string.

Args:
data: The dictionary to extract from (e.g., metadata).
path: A tuple of keys representing the path to the name.
target_set: The set to add the validated name to.
"""
if not isinstance(data, dict):
return

current_level = data
for key in path[:-1]:
current_level = current_level.get(key)
if not isinstance(current_level, dict):
return

name_value = current_level.get(path[-1])
if isinstance(name_value, str):
stripped_name = name_value.strip()
if stripped_name:
target_set.add(stripped_name)

def get_vaults(
self,
offset: int = 0,
limit: int = 10,
sort_by: str | None = None,
sort_direction: str | None = None,
) -> dict[str, Any]:
"""
Fetches a list of articles (vaults) from the API's `/vaults` endpoint.
Fetches a list of vaults from the API's `/vaults` endpoint.

Args:
offset: Number of records to skip (for pagination).
Expand Down Expand Up @@ -172,7 +199,7 @@ def get_bgt_prices(self) -> dict[str, Any]:
def get_sources(self, vault_limit_for_scan: int = 100) -> dict[str, set[str]]:
"""
Extracts unique source names (protocols and incentivizers) by analyzing vault data.
This method calls `get_articles` to fetch a sample of vaults and extracts
This method calls `get_vaults` to fetch a sample of vaults and extracts
protocol and incentivizer names from their metadata.

Args:
Expand All @@ -191,30 +218,15 @@ def get_sources(self, vault_limit_for_scan: int = 100) -> dict[str, set[str]]:
protocols: set[str] = set()
incentivizers: set[str] = set()
try:
vault_data = self.get_articles(limit=vault_limit_for_scan)

if "vaults" in vault_data and isinstance(vault_data["vaults"], list):
for vault in vault_data["vaults"]:
metadata = vault.get("metadata")
if isinstance(metadata, dict):
protocol_name = metadata.get("protocolName")
if protocol_name:
protocols.add(protocol_name)

# Check metadata.protocol.name as per API response structure
protocol_info = metadata.get("protocol")
if isinstance(protocol_info, dict) and protocol_info.get(
"name"
):
protocols.add(protocol_info["name"])

incentivizer_info = metadata.get("incentivizer")
if (
isinstance(incentivizer_info, dict)
and incentivizer_info.get("name")
and incentivizer_info["name"].strip()
):
incentivizers.add(incentivizer_info["name"].strip())
vault_data = self.get_vaults(limit=vault_limit_for_scan)

for vault in vault_data.get("vaults", []):
metadata = vault.get("metadata")
self._extract_and_add_name(metadata, ("protocolName",), protocols)
self._extract_and_add_name(metadata, ("protocol", "name"), protocols)
self._extract_and_add_name(
metadata, ("incentivizer", "name"), incentivizers
)

self.logger.info(
f"Found {len(protocols)} unique protocols and {len(incentivizers)} unique incentivizers."
Expand Down
Loading