#!/usr/bin/env python3 """ Build script to create a standalone executable for Northern Thailand Ping River Monitor """ import os import sys import shutil from pathlib import Path def create_spec_file(): """Create PyInstaller spec file""" spec_content = """ # -*- mode: python ; coding: utf-8 -*- block_cipher = None # Data files to include data_files = [ ('.env', '.'), ('sql/*.sql', 'sql'), ('README.md', '.'), ('POSTGRESQL_SETUP.md', '.'), ('SQLITE_MIGRATION.md', '.'), ] # Hidden imports that PyInstaller might miss hidden_imports = [ 'psycopg2', 'psycopg2-binary', 'sqlalchemy.dialects.postgresql', 'sqlalchemy.dialects.sqlite', 'sqlalchemy.dialects.mysql', 'influxdb', 'pymysql', 'dotenv', 'pydantic', 'fastapi', 'uvicorn', 'schedule', 'pandas', 'requests', 'psutil', ] a = Analysis( ['run.py'], pathex=['.'], binaries=[], datas=data_files, hiddenimports=hidden_imports, hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[ 'tkinter', 'matplotlib', 'PIL', 'jupyter', 'notebook', 'IPython', ], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='ping-river-monitor', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon='icon.ico' if os.path.exists('icon.ico') else None, ) """ with open('ping-river-monitor.spec', 'w') as f: f.write(spec_content.strip()) print("[OK] Created ping-river-monitor.spec") def install_pyinstaller(): """Install PyInstaller if not present""" try: import PyInstaller print("[OK] PyInstaller already installed") except ImportError: print("Installing PyInstaller...") os.system("uv add --dev pyinstaller") print("[OK] PyInstaller installed") def build_executable(): """Build the executable""" print("šŸ”Ø Building executable...") # Clean previous builds if os.path.exists('dist'): shutil.rmtree('dist') if os.path.exists('build'): shutil.rmtree('build') # Build with PyInstaller using uv result = os.system("uv run pyinstaller ping-river-monitor.spec --clean --noconfirm") if result == 0: print("āœ… Executable built successfully!") # Copy additional files to dist directory dist_dir = Path('dist') if dist_dir.exists(): # Copy .env file if it exists if os.path.exists('.env'): shutil.copy2('.env', dist_dir / '.env') print("āœ… Copied .env file") # Copy documentation for doc in ['README.md', 'POSTGRESQL_SETUP.md', 'SQLITE_MIGRATION.md']: if os.path.exists(doc): shutil.copy2(doc, dist_dir / doc) print(f"āœ… Copied {doc}") # Copy SQL files if os.path.exists('sql'): shutil.copytree('sql', dist_dir / 'sql', dirs_exist_ok=True) print("āœ… Copied SQL files") print(f"\nšŸŽ‰ Executable created: {dist_dir / 'ping-river-monitor.exe'}") print(f"šŸ“ All files in: {dist_dir.absolute()}") else: print("āŒ Build failed!") return False return True def create_batch_files(): """Create convenient batch files""" batch_files = { 'start.bat': '''@echo off echo Starting Ping River Monitor... ping-river-monitor.exe pause ''', 'start-api.bat': '''@echo off echo Starting Ping River Monitor Web API... ping-river-monitor.exe --web-api pause ''', 'test.bat': '''@echo off echo Running Ping River Monitor test... ping-river-monitor.exe --test pause ''', 'status.bat': '''@echo off echo Checking Ping River Monitor status... ping-river-monitor.exe --status pause ''' } dist_dir = Path('dist') for filename, content in batch_files.items(): batch_file = dist_dir / filename with open(batch_file, 'w') as f: f.write(content) print(f"āœ… Created {filename}") def create_readme(): """Create deployment README""" readme_content = """# Ping River Monitor - Standalone Executable This is a standalone executable version of the Northern Thailand Ping River Monitor. ## Quick Start 1. **Configure Database**: Edit `.env` file with your PostgreSQL settings 2. **Test Connection**: Double-click `test.bat` 3. **Start Monitoring**: Double-click `start.bat` 4. **Web Interface**: Double-click `start-api.bat` ## Files Included - `ping-river-monitor.exe` - Main executable - `.env` - Configuration file (EDIT THIS!) - `start.bat` - Start continuous monitoring - `start-api.bat` - Start web API server - `test.bat` - Run a test cycle - `status.bat` - Check system status - `README.md`, `POSTGRESQL_SETUP.md` - Documentation - `sql/` - Database initialization scripts ## Configuration Edit `.env` file: ``` DB_TYPE=postgresql POSTGRES_HOST=your-server-ip POSTGRES_PORT=5432 POSTGRES_DB=water_monitoring POSTGRES_USER=your-username POSTGRES_PASSWORD=your-password ``` ## Usage ### Command Line ```cmd # Continuous monitoring ping-river-monitor.exe # Single test run ping-river-monitor.exe --test # Web API server ping-river-monitor.exe --web-api # Check status ping-river-monitor.exe --status ``` ### Batch Files - Just double-click the `.bat` files for easy operation ## Troubleshooting 1. **Database Connection Issues** - Check `.env` file settings - Verify PostgreSQL server is accessible - Test with `test.bat` 2. **Permission Issues** - Run as administrator if needed - Check firewall settings for API mode 3. **Log Files** - Check `water_monitor.log` for detailed logs - Logs are created in the same directory as the executable ## Support For issues or questions, check the documentation files included. """ with open('dist/DEPLOYMENT_README.txt', 'w') as f: f.write(readme_content) print("āœ… Created DEPLOYMENT_README.txt") def main(): """Main build process""" print("Building Ping River Monitor Executable") print("=" * 50) # Check if we're in the right directory if not os.path.exists('run.py'): print("āŒ Error: run.py not found. Please run this from the project root directory.") return False # Install PyInstaller install_pyinstaller() # Create spec file create_spec_file() # Build executable if not build_executable(): return False # Create convenience files create_batch_files() create_readme() print("\n" + "=" * 50) print("šŸŽ‰ BUILD COMPLETE!") print("šŸ“ Check the 'dist' folder for your executable") print("šŸ’” Edit the .env file before distributing") print("šŸš€ Ready for deployment!") return True if __name__ == "__main__": success = main() sys.exit(0 if success else 1)