mirror of
https://github.com/Retropex/custom-ocean.xyz-dashboard.git
synced 2025-05-12 03:00:45 +02:00

- Added health check for Redis in `docker-compose.yml`. - Introduced new environment variables for the dashboard service. - Updated Redis dependency condition for the dashboard service. - Modified Dockerfile to use Python 3.9.18 and streamlined directory creation. - Enhanced `minify.py` with logging and improved error handling. - Added methods in `OceanData` and `WorkerData` for better data handling. - Improved error handling and logging in `NotificationService`. - Refactored `BitcoinProgressBar.js` for better organization and theme support. - Updated `blocks.js` with new helper functions for block data management. - Enhanced `dashboard.html` for improved display of network stats.
184 lines
6.8 KiB
Python
184 lines
6.8 KiB
Python
"""
|
|
Data models for the Bitcoin Mining Dashboard.
|
|
"""
|
|
from dataclasses import dataclass
|
|
from typing import Optional, Dict, List, Union, Any
|
|
import logging
|
|
|
|
@dataclass
|
|
class OceanData:
|
|
"""Data structure for Ocean.xyz pool mining data."""
|
|
# Keep original definitions with None default to maintain backward compatibility
|
|
pool_total_hashrate: float = None
|
|
pool_total_hashrate_unit: str = None
|
|
hashrate_24hr: float = None
|
|
hashrate_24hr_unit: str = None
|
|
hashrate_3hr: float = None
|
|
hashrate_3hr_unit: str = None
|
|
hashrate_10min: float = None
|
|
hashrate_10min_unit: str = None
|
|
hashrate_5min: float = None
|
|
hashrate_5min_unit: str = None
|
|
hashrate_60sec: float = None
|
|
hashrate_60sec_unit: str = None
|
|
estimated_earnings_per_day: float = None
|
|
estimated_earnings_next_block: float = None
|
|
estimated_rewards_in_window: float = None
|
|
workers_hashing: int = None
|
|
unpaid_earnings: float = None
|
|
est_time_to_payout: str = None
|
|
last_block: str = None
|
|
last_block_height: str = None
|
|
last_block_time: str = None
|
|
blocks_found: str = None
|
|
total_last_share: str = "N/A"
|
|
last_block_earnings: str = None
|
|
pool_fees_percentage: float = None # Added missing attribute
|
|
|
|
def get_normalized_hashrate(self, timeframe: str = "3hr") -> float:
|
|
"""
|
|
Get a normalized hashrate value in TH/s regardless of original units.
|
|
|
|
Args:
|
|
timeframe: The timeframe to get ("24hr", "3hr", "10min", "5min", "60sec")
|
|
|
|
Returns:
|
|
float: Normalized hashrate in TH/s
|
|
"""
|
|
if timeframe == "24hr" and self.hashrate_24hr is not None:
|
|
return convert_to_ths(self.hashrate_24hr, self.hashrate_24hr_unit)
|
|
elif timeframe == "3hr" and self.hashrate_3hr is not None:
|
|
return convert_to_ths(self.hashrate_3hr, self.hashrate_3hr_unit)
|
|
elif timeframe == "10min" and self.hashrate_10min is not None:
|
|
return convert_to_ths(self.hashrate_10min, self.hashrate_10min_unit)
|
|
elif timeframe == "5min" and self.hashrate_5min is not None:
|
|
return convert_to_ths(self.hashrate_5min, self.hashrate_5min_unit)
|
|
elif timeframe == "60sec" and self.hashrate_60sec is not None:
|
|
return convert_to_ths(self.hashrate_60sec, self.hashrate_60sec_unit)
|
|
return 0.0
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert the OceanData object to a dictionary."""
|
|
return {k: v for k, v in self.__dict__.items()}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> 'OceanData':
|
|
"""Create an OceanData instance from a dictionary."""
|
|
filtered_data = {}
|
|
for k, v in data.items():
|
|
if k in cls.__annotations__:
|
|
filtered_data[k] = v
|
|
return cls(**filtered_data)
|
|
|
|
@dataclass
|
|
class WorkerData:
|
|
"""Data structure for individual worker information."""
|
|
name: str = None
|
|
status: str = "offline"
|
|
type: str = "ASIC" # ASIC or Bitaxe
|
|
model: str = "Unknown"
|
|
hashrate_60sec: float = 0
|
|
hashrate_60sec_unit: str = "TH/s"
|
|
hashrate_3hr: float = 0
|
|
hashrate_3hr_unit: str = "TH/s"
|
|
efficiency: float = 0
|
|
last_share: str = "N/A"
|
|
earnings: float = 0
|
|
acceptance_rate: float = 0
|
|
power_consumption: float = 0
|
|
temperature: float = 0
|
|
|
|
def __post_init__(self):
|
|
"""
|
|
Validate worker data after initialization.
|
|
Ensures values are within acceptable ranges and formats.
|
|
"""
|
|
# Ensure hashrates are non-negative
|
|
if self.hashrate_60sec is not None and self.hashrate_60sec < 0:
|
|
self.hashrate_60sec = 0
|
|
|
|
if self.hashrate_3hr is not None and self.hashrate_3hr < 0:
|
|
self.hashrate_3hr = 0
|
|
|
|
# Ensure status is valid, but don't raise exceptions for backward compatibility
|
|
if self.status not in ["online", "offline"]:
|
|
logging.warning(f"Worker {self.name}: Invalid status '{self.status}', using 'offline'")
|
|
self.status = "offline"
|
|
|
|
# Ensure type is valid, but don't raise exceptions for backward compatibility
|
|
if self.type not in ["ASIC", "Bitaxe"]:
|
|
logging.warning(f"Worker {self.name}: Invalid type '{self.type}', using 'ASIC'")
|
|
self.type = "ASIC"
|
|
|
|
def get_normalized_hashrate(self, timeframe: str = "3hr") -> float:
|
|
"""
|
|
Get normalized hashrate in TH/s.
|
|
|
|
Args:
|
|
timeframe: The timeframe to get ("3hr" or "60sec")
|
|
|
|
Returns:
|
|
float: Normalized hashrate in TH/s
|
|
"""
|
|
if timeframe == "3hr":
|
|
return convert_to_ths(self.hashrate_3hr, self.hashrate_3hr_unit)
|
|
elif timeframe == "60sec":
|
|
return convert_to_ths(self.hashrate_60sec, self.hashrate_60sec_unit)
|
|
return 0.0
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert the WorkerData object to a dictionary."""
|
|
return {k: v for k, v in self.__dict__.items()}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> 'WorkerData':
|
|
"""Create a WorkerData instance from a dictionary."""
|
|
filtered_data = {}
|
|
for k, v in data.items():
|
|
if k in cls.__annotations__:
|
|
filtered_data[k] = v
|
|
return cls(**filtered_data)
|
|
|
|
class HashRateConversionError(Exception):
|
|
"""Exception raised for errors in hashrate unit conversion."""
|
|
pass
|
|
|
|
def convert_to_ths(value, unit):
|
|
"""
|
|
Convert any hashrate unit to TH/s equivalent.
|
|
|
|
Args:
|
|
value (float): The numerical value of the hashrate
|
|
unit (str): The unit of measurement (e.g., 'PH/s', 'EH/s', etc.)
|
|
|
|
Returns:
|
|
float: The hashrate value in TH/s
|
|
"""
|
|
if value is None or value == 0:
|
|
return 0
|
|
|
|
try:
|
|
unit = unit.lower() if unit else 'th/s'
|
|
|
|
if 'ph/s' in unit:
|
|
return value * 1000 # 1 PH/s = 1000 TH/s
|
|
elif 'eh/s' in unit:
|
|
return value * 1000000 # 1 EH/s = 1,000,000 TH/s
|
|
elif 'gh/s' in unit:
|
|
return value / 1000 # 1 TH/s = 1000 GH/s
|
|
elif 'mh/s' in unit:
|
|
return value / 1000000 # 1 TH/s = 1,000,000 MH/s
|
|
elif 'kh/s' in unit:
|
|
return value / 1000000000 # 1 TH/s = 1,000,000,000 KH/s
|
|
elif 'h/s' in unit and not any(prefix in unit for prefix in ['th/s', 'ph/s', 'eh/s', 'gh/s', 'mh/s', 'kh/s']):
|
|
return value / 1000000000000 # 1 TH/s = 1,000,000,000,000 H/s
|
|
elif 'th/s' in unit:
|
|
return value
|
|
else:
|
|
# Log unexpected unit
|
|
logging.warning(f"Unexpected hashrate unit: {unit}, defaulting to treating as TH/s")
|
|
return value
|
|
except Exception as e:
|
|
logging.error(f"Error in convert_to_ths: {e}")
|
|
return value # Return original value as fallback
|