mirror of
https://github.com/Retropex/custom-ocean.xyz-dashboard.git
synced 2025-05-12 19:20:45 +02:00
Update setup.py
This commit is contained in:
parent
f34dec00a5
commit
0cbbe8c6a8
409
setup.py
409
setup.py
@ -1,12 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Setup script to prepare project structure and directories.
|
||||
Enhanced setup script for Bitcoin Mining Dashboard.
|
||||
|
||||
This script prepares the project structure, installs dependencies,
|
||||
verifies configuration, and provides system checks for optimal operation.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import logging
|
||||
import argparse
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
# Configure logging with color support
|
||||
try:
|
||||
import colorlog
|
||||
|
||||
handler = colorlog.StreamHandler()
|
||||
handler.setFormatter(colorlog.ColoredFormatter(
|
||||
'%(log_color)s%(asctime)s - %(levelname)s - %(message)s',
|
||||
log_colors={
|
||||
'DEBUG': 'cyan',
|
||||
'INFO': 'green',
|
||||
'WARNING': 'yellow',
|
||||
'ERROR': 'red',
|
||||
'CRITICAL': 'red,bg_white',
|
||||
}
|
||||
))
|
||||
|
||||
logger = colorlog.getLogger()
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
except ImportError:
|
||||
# Fallback to standard logging if colorlog is not available
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger()
|
||||
|
||||
# Directory structure to create
|
||||
DIRECTORIES = [
|
||||
@ -14,7 +45,8 @@ DIRECTORIES = [
|
||||
'static/js',
|
||||
'static/img',
|
||||
'templates',
|
||||
'logs'
|
||||
'logs',
|
||||
'data' # For temporary data storage
|
||||
]
|
||||
|
||||
# Files to move to their correct locations
|
||||
@ -26,11 +58,14 @@ FILE_MAPPINGS = {
|
||||
'boot.css': 'static/css/boot.css',
|
||||
'error.css': 'static/css/error.css',
|
||||
'retro-refresh.css': 'static/css/retro-refresh.css',
|
||||
'blocks.css': 'static/css/blocks.css',
|
||||
|
||||
# JS files
|
||||
'main.js': 'static/js/main.js',
|
||||
'workers.js': 'static/js/workers.js',
|
||||
'retro-refresh.js': 'static/js/retro-refresh.js',
|
||||
'blocks.js': 'static/js/blocks.js',
|
||||
'block-animation.js': 'static/js/block-animation.js',
|
||||
'BitcoinProgressBar.js': 'static/js/BitcoinProgressBar.js',
|
||||
|
||||
# Template files
|
||||
'base.html': 'templates/base.html',
|
||||
@ -38,48 +73,372 @@ FILE_MAPPINGS = {
|
||||
'workers.html': 'templates/workers.html',
|
||||
'boot.html': 'templates/boot.html',
|
||||
'error.html': 'templates/error.html',
|
||||
'blocks.html': 'templates/blocks.html',
|
||||
}
|
||||
|
||||
# Default configuration
|
||||
DEFAULT_CONFIG = {
|
||||
"power_cost": 0.0,
|
||||
"power_usage": 0.0,
|
||||
"wallet": "bc1py5zmrtssheq3shd8cptpl5l5m3txxr5afynyg2gyvam6w78s4dlqqnt4v9"
|
||||
}
|
||||
|
||||
def parse_arguments():
|
||||
"""Parse command line arguments."""
|
||||
parser = argparse.ArgumentParser(description='Setup the Bitcoin Mining Dashboard')
|
||||
parser.add_argument('--debug', action='store_true', help='Enable debug logging')
|
||||
parser.add_argument('--wallet', type=str, help='Set your Ocean.xyz wallet address')
|
||||
parser.add_argument('--power-cost', type=float, help='Set your electricity cost per kWh')
|
||||
parser.add_argument('--power-usage', type=float, help='Set your power consumption in watts')
|
||||
parser.add_argument('--skip-checks', action='store_true', help='Skip dependency checks')
|
||||
parser.add_argument('--force', action='store_true', help='Force file overwrite')
|
||||
parser.add_argument('--config', type=str, help='Path to custom config.json')
|
||||
return parser.parse_args()
|
||||
|
||||
def create_directory_structure():
|
||||
"""Create the necessary directory structure."""
|
||||
for directory in DIRECTORIES:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
logging.info(f"Created directory: {directory}")
|
||||
logger.info("Creating directory structure...")
|
||||
success = True
|
||||
|
||||
for directory in DIRECTORIES:
|
||||
try:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
logger.debug(f"Created directory: {directory}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create directory {directory}: {str(e)}")
|
||||
success = False
|
||||
|
||||
if success:
|
||||
logger.info("✓ Directory structure created successfully")
|
||||
else:
|
||||
logger.warning("⚠ Some directories could not be created")
|
||||
|
||||
return success
|
||||
|
||||
def move_files(force=False):
|
||||
"""
|
||||
Move files to their correct locations.
|
||||
|
||||
Args:
|
||||
force (bool): Force overwriting of existing files
|
||||
"""
|
||||
logger.info("Moving files to their correct locations...")
|
||||
success = True
|
||||
moved_count = 0
|
||||
skipped_count = 0
|
||||
missing_count = 0
|
||||
|
||||
def move_files():
|
||||
"""Move files to their correct locations."""
|
||||
for source, destination in FILE_MAPPINGS.items():
|
||||
if os.path.exists(source):
|
||||
# Create the directory if it doesn't exist
|
||||
os.makedirs(os.path.dirname(destination), exist_ok=True)
|
||||
|
||||
# Check if destination exists and handle according to force flag
|
||||
if os.path.exists(destination) and not force:
|
||||
logger.debug(f"Skipped {source} (destination already exists)")
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
# Copy the file to its destination
|
||||
shutil.copy2(source, destination)
|
||||
logging.info(f"Moved {source} to {destination}")
|
||||
logger.debug(f"Moved {source} to {destination}")
|
||||
moved_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to copy {source} to {destination}: {str(e)}")
|
||||
success = False
|
||||
else:
|
||||
logging.warning(f"Source file not found: {source}")
|
||||
logger.warning(f"Source file not found: {source}")
|
||||
missing_count += 1
|
||||
|
||||
def create_empty_config():
|
||||
"""Create an empty config.json file if it doesn't exist."""
|
||||
if not os.path.exists('config.json'):
|
||||
with open('config.json', 'w') as f:
|
||||
f.write('{\n "power_cost": 0.0,\n "power_usage": 0.0,\n "wallet": "bc1py5zmrtssheq3shd8cptpl5l5m3txxr5afynyg2gyvam6w78s4dlqqnt4v9"\n}')
|
||||
logging.info("Created empty config.json")
|
||||
if success:
|
||||
logger.info(f"✓ File movement completed: {moved_count} moved, {skipped_count} skipped, {missing_count} missing")
|
||||
else:
|
||||
logger.warning("⚠ Some files could not be moved")
|
||||
|
||||
return success
|
||||
|
||||
def validate_wallet_address(wallet):
|
||||
"""
|
||||
Validate Bitcoin wallet address format.
|
||||
|
||||
Args:
|
||||
wallet (str): Bitcoin wallet address
|
||||
|
||||
Returns:
|
||||
bool: True if valid, False otherwise
|
||||
"""
|
||||
# Basic validation patterns for different Bitcoin address formats
|
||||
patterns = [
|
||||
r'^1[a-km-zA-HJ-NP-Z1-9]{25,34}$', # Legacy
|
||||
r'^3[a-km-zA-HJ-NP-Z1-9]{25,34}$', # P2SH
|
||||
r'^bc1[a-zA-Z0-9]{39,59}$', # Bech32
|
||||
r'^bc1p[a-zA-Z0-9]{39,59}$', # Taproot
|
||||
r'^bc1p[a-z0-9]{73,107}$' # Longform Taproot
|
||||
]
|
||||
|
||||
# Check if the wallet matches any of the patterns
|
||||
for pattern in patterns:
|
||||
if re.match(pattern, wallet):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def create_config(args):
|
||||
"""
|
||||
Create or update config.json file.
|
||||
|
||||
Args:
|
||||
args: Command line arguments
|
||||
"""
|
||||
config_file = args.config if args.config else 'config.json'
|
||||
config = DEFAULT_CONFIG.copy()
|
||||
|
||||
# Load existing config if available
|
||||
if os.path.exists(config_file):
|
||||
try:
|
||||
with open(config_file, 'r') as f:
|
||||
existing_config = json.load(f)
|
||||
config.update(existing_config)
|
||||
logger.info(f"Loaded existing configuration from {config_file}")
|
||||
except json.JSONDecodeError:
|
||||
logger.warning(f"Invalid JSON in {config_file}, using default configuration")
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading {config_file}: {str(e)}")
|
||||
|
||||
# Update config from command line arguments
|
||||
if args.wallet:
|
||||
if validate_wallet_address(args.wallet):
|
||||
config["wallet"] = args.wallet
|
||||
else:
|
||||
logger.warning(f"Invalid wallet address format: {args.wallet}")
|
||||
logger.warning("Using default or existing wallet address")
|
||||
|
||||
if args.power_cost is not None:
|
||||
if args.power_cost >= 0:
|
||||
config["power_cost"] = args.power_cost
|
||||
else:
|
||||
logger.warning("Power cost cannot be negative, using default or existing value")
|
||||
|
||||
if args.power_usage is not None:
|
||||
if args.power_usage >= 0:
|
||||
config["power_usage"] = args.power_usage
|
||||
else:
|
||||
logger.warning("Power usage cannot be negative, using default or existing value")
|
||||
|
||||
# Save the configuration
|
||||
try:
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(config, f, indent=2, sort_keys=True)
|
||||
logger.info(f"✓ Configuration saved to {config_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save configuration: {str(e)}")
|
||||
return False
|
||||
|
||||
# Print current configuration
|
||||
logger.info("Current configuration:")
|
||||
logger.info(f" ├── Wallet address: {config['wallet']}")
|
||||
logger.info(f" ├── Power cost: ${config['power_cost']} per kWh")
|
||||
logger.info(f" └── Power usage: {config['power_usage']} watts")
|
||||
|
||||
return True
|
||||
|
||||
def check_dependencies(skip=False):
|
||||
"""
|
||||
Check if required Python dependencies are installed.
|
||||
|
||||
Args:
|
||||
skip (bool): Skip the dependency check
|
||||
"""
|
||||
if skip:
|
||||
logger.info("Skipping dependency check")
|
||||
return True
|
||||
|
||||
logger.info("Checking dependencies...")
|
||||
|
||||
try:
|
||||
# Check if pip is available
|
||||
subprocess.run([sys.executable, "-m", "pip", "--version"],
|
||||
check=True, capture_output=True, text=True)
|
||||
except Exception as e:
|
||||
logger.error(f"Pip is not available: {str(e)}")
|
||||
logger.error("Please install pip before continuing")
|
||||
return False
|
||||
|
||||
# Check if requirements.txt exists
|
||||
if not os.path.exists('requirements.txt'):
|
||||
logger.error("requirements.txt not found")
|
||||
return False
|
||||
|
||||
# Check currently installed packages
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "pip", "freeze"],
|
||||
check=True, capture_output=True, text=True
|
||||
)
|
||||
installed_output = result.stdout
|
||||
installed_packages = {
|
||||
line.split('==')[0].lower(): line.split('==')[1] if '==' in line else ''
|
||||
for line in installed_output.splitlines()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to check installed packages: {str(e)}")
|
||||
installed_packages = {}
|
||||
|
||||
# Read requirements
|
||||
try:
|
||||
with open('requirements.txt', 'r') as f:
|
||||
requirements = f.read().splitlines()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to read requirements.txt: {str(e)}")
|
||||
return False
|
||||
|
||||
# Check each requirement
|
||||
missing_packages = []
|
||||
for req in requirements:
|
||||
if req and not req.startswith('#'):
|
||||
package = req.split('==')[0].lower()
|
||||
if package not in installed_packages:
|
||||
missing_packages.append(req)
|
||||
|
||||
if missing_packages:
|
||||
logger.warning(f"Missing {len(missing_packages)} required packages")
|
||||
logger.info("Installing missing packages...")
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
|
||||
check=True, capture_output=True, text=True
|
||||
)
|
||||
logger.info("✓ Dependencies installed successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to install dependencies: {str(e)}")
|
||||
logger.error("Please run: pip install -r requirements.txt")
|
||||
return False
|
||||
else:
|
||||
logger.info("✓ All required packages are installed")
|
||||
|
||||
return True
|
||||
|
||||
def check_redis():
|
||||
"""Check if Redis is available."""
|
||||
logger.info("Checking Redis availability...")
|
||||
|
||||
redis_url = os.environ.get("REDIS_URL")
|
||||
if not redis_url:
|
||||
logger.info("⚠ Redis URL not configured (REDIS_URL environment variable not set)")
|
||||
logger.info(" └── The dashboard will run without persistent state")
|
||||
logger.info(" └── Set REDIS_URL for better reliability")
|
||||
return True
|
||||
|
||||
try:
|
||||
import redis
|
||||
client = redis.Redis.from_url(redis_url)
|
||||
client.ping()
|
||||
logger.info(f"✓ Successfully connected to Redis at {redis_url}")
|
||||
return True
|
||||
except ImportError:
|
||||
logger.warning("Redis Python package not installed")
|
||||
logger.info(" └── Run: pip install redis")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to connect to Redis: {str(e)}")
|
||||
logger.info(f" └── Check that Redis is running and accessible at {redis_url}")
|
||||
return False
|
||||
|
||||
def perform_system_checks():
|
||||
"""Perform system checks and provide recommendations."""
|
||||
logger.info("Performing system checks...")
|
||||
|
||||
# Check Python version
|
||||
python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
||||
if sys.version_info.major < 3 or (sys.version_info.major == 3 and sys.version_info.minor < 9):
|
||||
logger.warning(f"⚠ Python version {python_version} is below recommended (3.9+)")
|
||||
else:
|
||||
logger.info(f"✓ Python version {python_version} is compatible")
|
||||
|
||||
# Check available memory
|
||||
try:
|
||||
import psutil
|
||||
memory = psutil.virtual_memory()
|
||||
memory_gb = memory.total / (1024**3)
|
||||
if memory_gb < 1:
|
||||
logger.warning(f"⚠ Low system memory: {memory_gb:.2f} GB (recommended: 1+ GB)")
|
||||
else:
|
||||
logger.info(f"✓ System memory: {memory_gb:.2f} GB")
|
||||
except ImportError:
|
||||
logger.debug("psutil not available, skipping memory check")
|
||||
|
||||
# Check write permissions
|
||||
log_dir = 'logs'
|
||||
try:
|
||||
test_file = os.path.join(log_dir, 'test_write.tmp')
|
||||
with open(test_file, 'w') as f:
|
||||
f.write('test')
|
||||
os.remove(test_file)
|
||||
logger.info(f"✓ Write permissions for logs directory")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠ Cannot write to logs directory: {str(e)}")
|
||||
|
||||
# Check port availability
|
||||
port = 5000 # Default port
|
||||
try:
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.bind(('localhost', port))
|
||||
s.close()
|
||||
logger.info(f"✓ Port {port} is available")
|
||||
except Exception:
|
||||
logger.warning(f"⚠ Port {port} is already in use")
|
||||
|
||||
logger.info("System checks completed")
|
||||
|
||||
def main():
|
||||
"""Main setup function."""
|
||||
logging.info("Starting setup process")
|
||||
args = parse_arguments()
|
||||
|
||||
# Set logging level
|
||||
if args.debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.debug("Debug logging enabled")
|
||||
|
||||
logger.info("=== Bitcoin Mining Dashboard Setup ===")
|
||||
|
||||
# Check dependencies
|
||||
if not check_dependencies(args.skip_checks):
|
||||
logger.error("Dependency check failed. Please install required packages and retry.")
|
||||
return 1
|
||||
|
||||
# Create directory structure
|
||||
create_directory_structure()
|
||||
if not create_directory_structure():
|
||||
logger.error("Failed to create directory structure.")
|
||||
return 1
|
||||
|
||||
# Move files to their correct locations
|
||||
move_files()
|
||||
if not move_files(args.force):
|
||||
logger.warning("Some files could not be moved, but continuing...")
|
||||
|
||||
# Create empty config.json
|
||||
create_empty_config()
|
||||
# Create or update configuration
|
||||
if not create_config(args):
|
||||
logger.error("Failed to create configuration file.")
|
||||
return 1
|
||||
|
||||
logging.info("Setup completed successfully")
|
||||
# Check Redis if available
|
||||
check_redis()
|
||||
|
||||
# Perform system checks
|
||||
if not args.skip_checks:
|
||||
perform_system_checks()
|
||||
|
||||
logger.info("=== Setup completed successfully ===")
|
||||
logger.info("")
|
||||
logger.info("Next steps:")
|
||||
logger.info("1. Verify configuration in config.json")
|
||||
logger.info("2. Start the application with: python App.py")
|
||||
logger.info("3. Access the dashboard at: http://localhost:5000")
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
exit_code = main()
|
||||
sys.exit(exit_code)
|
||||
|
Loading…
Reference in New Issue
Block a user