Improve API connectivity and error handling

Updated `MiningDashboardService` in `data_service.py` to enhance API connectivity testing and error handling. Introduced `_api_request_with_retry` for retry logic on API requests, and modified `_test_api_connectivity` to log detailed connectivity test information. Refactored multiple API calls to utilize the new retry method, improving reliability when fetching user hashrate data, pool stats, and other metrics.
This commit is contained in:
DJObleezy 2025-04-16 17:58:06 -07:00
parent c9a2f927ff
commit 0a32b492b8

View File

@ -46,53 +46,71 @@ class MiningDashboardService:
def _test_api_connectivity(self): def _test_api_connectivity(self):
"""Test if the new Ocean.xyz Beta API is available.""" """Test if the new Ocean.xyz Beta API is available."""
try: try:
# Add helpful headers # Add helpful headers to increase chances of successful connection
headers = { headers = {
'User-Agent': 'Mozilla/5.0 Mining Dashboard', 'User-Agent': 'Mozilla/5.0 Mining Dashboard',
'Accept': 'application/json', 'Accept': 'application/json, text/plain, */*',
'Cache-Control': 'no-cache' 'Cache-Control': 'no-cache'
} }
# Try wallet-specific ping endpoint first since you confirmed it works # Try the wallet-specific ping endpoint first (this is what works)
wallet_ping_url = f"{self.ocean_api_base}/ping/{self.wallet}" wallet_ping_url = f"{self.ocean_api_base}/ping/{self.wallet}"
logging.info(f"Testing Ocean API connectivity with wallet-specific ping: {wallet_ping_url}") logging.info(f"Testing Ocean API connectivity: {wallet_ping_url}")
response = self.session.get(wallet_ping_url, headers=headers, timeout=5) response = self.session.get(wallet_ping_url, headers=headers, timeout=5)
if response.ok: if response.ok:
logging.info("Ocean.xyz Beta API is available with wallet-specific ping endpoint") logging.info(f"Ocean.xyz Beta API is available through wallet-specific ping: {response.text[:30]}")
return True return True
# Log the failed attempt details
logging.warning(f"Wallet-specific ping failed with status: {response.status_code}, response: {response.text[:100]}")
# If wallet-specific ping fails, try standard ping # Try a different endpoint as backup
standard_ping_url = f"{self.ocean_api_base}/ping"
logging.info(f"Testing Ocean API with standard ping: {standard_ping_url}")
response = self.session.get(standard_ping_url, headers=headers, timeout=5)
if response.ok and response.text.strip() == "PONG":
logging.info("Ocean.xyz Beta API is available with standard ping endpoint")
return True
# If both pings fail, try a wallet-specific endpoint that should return data
statsnap_url = f"{self.ocean_api_base}/statsnap/{self.wallet}" statsnap_url = f"{self.ocean_api_base}/statsnap/{self.wallet}"
logging.info(f"Testing Ocean API with statsnap endpoint: {statsnap_url}") logging.info(f"Trying alternate endpoint: {statsnap_url}")
response = self.session.get(statsnap_url, headers=headers, timeout=5) response = self.session.get(statsnap_url, headers=headers, timeout=5)
if response.ok: if response.ok:
logging.info("Ocean.xyz Beta API is available with statsnap endpoint") logging.info("Ocean.xyz Beta API is available through statsnap endpoint")
return True return True
# All attempts failed # Log all failed attempts and return False
logging.error("All Ocean.xyz Beta API connectivity tests failed") logging.error("All Ocean.xyz API connectivity tests failed")
logging.error(f"Last response status: {response.status_code}, text: {response.text[:200]}")
# Log the exact URL that you confirmed works for debugging
debug_url = f"https://api.ocean.xyz/v1/ping/{self.wallet}"
logging.info(f"Note: The URL {debug_url} should work according to user confirmation")
return False return False
except Exception as e: except Exception as e:
logging.error(f"Error testing Ocean.xyz Beta API connectivity: {e}") logging.error(f"Error testing Ocean.xyz Beta API connectivity: {e}")
return False return False
def _api_request_with_retry(self, endpoint, timeout=10, retries=3):
"""Make an API request with retry logic."""
url = f"{self.ocean_api_base}/{endpoint}"
logging.info(f"API request: {url}")
headers = {
'User-Agent': 'Mozilla/5.0 Mining Dashboard',
'Accept': 'application/json, text/plain, */*',
'Cache-Control': 'no-cache'
}
for attempt in range(retries):
try:
response = self.session.get(url, headers=headers, timeout=timeout)
if response.ok:
return response
logging.warning(f"API request failed (attempt {attempt+1}/{retries}): {url}, status: {response.status_code}")
if attempt < retries - 1:
time.sleep(1) # Wait before retry
except Exception as e:
logging.error(f"API request exception (attempt {attempt+1}/{retries}): {e}")
if attempt < retries - 1:
time.sleep(1) # Wait before retry
return None
def fetch_metrics(self): def fetch_metrics(self):
""" """
Fetch metrics from Ocean.xyz and other sources. Fetch metrics from Ocean.xyz and other sources.
@ -227,13 +245,19 @@ class MiningDashboardService:
data = OceanData() data = OceanData()
try: try:
# Fetch user hashrate data # First test if API connectivity is still valid
hashrate_resp = self.session.get(f"{self.ocean_api_base}/user_hashrate/{self.wallet}", timeout=10) if not self._test_api_connectivity():
if not hashrate_resp.ok: logging.warning("API connectivity test failed during data fetch, falling back to scraping")
logging.error(f"Error fetching hashrate data: status code {hashrate_resp.status_code}") return self.get_ocean_data()
return None
# Fetch user hashrate data with retry logic
hashrate_resp = self._api_request_with_retry(f"user_hashrate/{self.wallet}")
if not hashrate_resp:
logging.error("Error fetching hashrate data from API, falling back to scraping")
return self.get_ocean_data()
hashrate_data = hashrate_resp.json() hashrate_data = hashrate_resp.json()
logging.debug(f"Hashrate API response: {str(hashrate_data)[:200]}...")
# Convert and populate hashrates # Convert and populate hashrates
if "hashrate_60s" in hashrate_data: if "hashrate_60s" in hashrate_data:
@ -262,8 +286,8 @@ class MiningDashboardService:
data.hashrate_24hr_unit = self._format_hashrate_value(hashrate_24hr)[1] data.hashrate_24hr_unit = self._format_hashrate_value(hashrate_24hr)[1]
# Fetch pool stats for pool hashrate # Fetch pool stats for pool hashrate
pool_resp = self.session.get(f"{self.ocean_api_base}/pool_hashrate", timeout=10) pool_resp = self._api_request_with_retry("pool_hashrate")
if pool_resp.ok: if pool_resp:
pool_data = pool_resp.json() pool_data = pool_resp.json()
if "pool_300s" in pool_data: if "pool_300s" in pool_data:
pool_hashrate = pool_data["pool_300s"] pool_hashrate = pool_data["pool_300s"]
@ -271,9 +295,10 @@ class MiningDashboardService:
data.pool_total_hashrate_unit = self._format_hashrate_value(pool_hashrate)[1] data.pool_total_hashrate_unit = self._format_hashrate_value(pool_hashrate)[1]
# Fetch user's stats for earnings info # Fetch user's stats for earnings info
stats_resp = self.session.get(f"{self.ocean_api_base}/statsnap/{self.wallet}", timeout=10) stats_resp = self._api_request_with_retry(f"statsnap/{self.wallet}")
if stats_resp.ok: if stats_resp:
stats_data = stats_resp.json() stats_data = stats_resp.json()
logging.debug(f"Statsnap API response: {str(stats_data)[:200]}...")
# Get unpaid earnings # Get unpaid earnings
if "unpaid" in stats_data: if "unpaid" in stats_data:
@ -297,17 +322,19 @@ class MiningDashboardService:
data.total_last_share = la_dt.strftime("%Y-%m-%d %I:%M %p") data.total_last_share = la_dt.strftime("%Y-%m-%d %I:%M %p")
# Fetch user_hashrate_full to count active workers # Fetch user_hashrate_full to count active workers
workers_resp = self.session.get(f"{self.ocean_api_base}/user_hashrate_full/{self.wallet}", timeout=10) workers_resp = self._api_request_with_retry(f"user_hashrate_full/{self.wallet}")
if workers_resp.ok: if workers_resp:
workers_data = workers_resp.json() workers_data = workers_resp.json()
if "workers" in workers_data: if "workers" in workers_data:
logging.info(f"Found {len(workers_data['workers'])} workers in API response")
# Count non-zero hashrate workers as active # Count non-zero hashrate workers as active
data.workers_hashing = sum(1 for worker in workers_data["workers"] data.workers_hashing = sum(1 for worker in workers_data["workers"]
if worker.get("hashrate_300s", 0) > 0) if worker.get("hashrate_300s", 0) > 0)
logging.info(f"Workers currently hashing: {data.workers_hashing}")
# Fetch latest block info # Fetch latest block info
latest_block_resp = self.session.get(f"{self.ocean_api_base}/latest_block", timeout=10) latest_block_resp = self._api_request_with_retry("latest_block")
if latest_block_resp.ok: if latest_block_resp:
latest_block_data = latest_block_resp.json() latest_block_data = latest_block_resp.json()
if latest_block_data: if latest_block_data:
# Get the first block in the response # Get the first block in the response
@ -327,8 +354,8 @@ class MiningDashboardService:
logging.error(f"Error converting block timestamp: {e}") logging.error(f"Error converting block timestamp: {e}")
# Fetch blocks for blocks found count # Fetch blocks for blocks found count
blocks_resp = self.session.get(f"{self.ocean_api_base}/blocks", timeout=10) blocks_resp = self._api_request_with_retry("blocks")
if blocks_resp.ok: if blocks_resp:
blocks_data = blocks_resp.json() blocks_data = blocks_resp.json()
if isinstance(blocks_data, list): if isinstance(blocks_data, list):
# Count blocks mined by this user # Count blocks mined by this user
@ -338,8 +365,8 @@ class MiningDashboardService:
data.blocks_found = str(len(user_blocks)) data.blocks_found = str(len(user_blocks))
# Fetch earnpay for last block earnings # Fetch earnpay for last block earnings
earnpay_resp = self.session.get(f"{self.ocean_api_base}/earnpay/{self.wallet}", timeout=10) earnpay_resp = self._api_request_with_retry(f"earnpay/{self.wallet}")
if earnpay_resp.ok: if earnpay_resp:
earnpay_data = earnpay_resp.json() earnpay_data = earnpay_resp.json()
if "earnings" in earnpay_data and earnpay_data["earnings"]: if "earnings" in earnpay_data and earnpay_data["earnings"]:
# Get the latest earning entry # Get the latest earning entry
@ -371,11 +398,15 @@ class MiningDashboardService:
# Rough estimate based on 144 blocks per day average # Rough estimate based on 144 blocks per day average
data.estimated_earnings_per_day = data.estimated_earnings_next_block * 144 data.estimated_earnings_per_day = data.estimated_earnings_next_block * 144
# Log successful API data retrieval
logging.info("Successfully retrieved Ocean data from API")
return data return data
except Exception as e: except Exception as e:
logging.error(f"Error fetching Ocean data from API: {e}") logging.error(f"Error fetching Ocean data from API: {e}")
return None # Fall back to scraping method
logging.info("Falling back to web scraping method")
return self.get_ocean_data()
def _format_hashrate_value(self, hashrate_h_per_sec): def _format_hashrate_value(self, hashrate_h_per_sec):
""" """
@ -797,10 +828,10 @@ class MiningDashboardService:
dict: Worker data dictionary with stats and list of workers dict: Worker data dictionary with stats and list of workers
""" """
try: try:
# Fetch full worker hashrate information # Fetch full worker hashrate information with retry
response = self.session.get(f"{self.ocean_api_base}/user_hashrate_full/{self.wallet}", timeout=15) response = self._api_request_with_retry(f"user_hashrate_full/{self.wallet}", timeout=15)
if not response.ok: if not response:
logging.error(f"Error fetching worker data from API: status code {response.status_code}") logging.error("Error fetching worker data from API")
return None return None
data = response.json() data = response.json()
@ -808,6 +839,7 @@ class MiningDashboardService:
logging.error("No worker data found in API response") logging.error("No worker data found in API response")
return None return None
logging.debug(f"Worker API response: {str(data)[:200]}...")
workers = [] workers = []
total_hashrate = 0 total_hashrate = 0
workers_online = 0 workers_online = 0
@ -881,9 +913,9 @@ class MiningDashboardService:
workers.append(worker) workers.append(worker)
# Try to get earnings info from statsnap endpoint # Try to get earnings info from statsnap endpoint
earnings_resp = self.session.get(f"{self.ocean_api_base}/statsnap/{self.wallet}", timeout=10) earnings_resp = self._api_request_with_retry(f"statsnap/{self.wallet}", timeout=10)
daily_sats = 0 daily_sats = 0
if earnings_resp.ok: if earnings_resp:
stats_data = earnings_resp.json() stats_data = earnings_resp.json()
if "estimated_earn_next_block" in stats_data: if "estimated_earn_next_block" in stats_data:
# Approximately 144 blocks per day # Approximately 144 blocks per day