chore: declutter the repo
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 1m43s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 45s
Documentation / Validate Documentation (push) Failing after 17s
Documentation / Generate API Documentation (push) Successful in 12s
Documentation / Build Sphinx Documentation (push) Successful in 19s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
Documentation / Documentation Summary (push) Successful in 4s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 1m43s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 45s
Documentation / Validate Documentation (push) Failing after 17s
Documentation / Generate API Documentation (push) Successful in 12s
Documentation / Build Sphinx Documentation (push) Successful in 19s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
Documentation / Documentation Summary (push) Successful in 4s
Removes 26 tracked files that no longer describe or serve the running system, verified one by one against the whole repo (source, tests, docs, README, Makefile, Dockerfile, .gitea workflows, pyproject, packaging spec) plus dynamic-reference paths, before deletion. Root (11): one-off launch/setup write-ups from the project's first weeks that document events which never happened the way they describe — a github.com publication (the remote is self-hosted Gitea) and a 15-minute scheduler (the service runs hourly). Also .gitlab-ci.yml (unused, CI is .gitea/), .env.postgres and setup.py.backup (a placeholder env file and a backup in version control), and the PyInstaller packaging trio build_executable.py / build_simple.py / ping-river-monitor.spec, which bundled docs that no longer exist and is not how this deploys. docs (9): stale guides superseded by DATABASE_DEPLOYMENT_GUIDE, GITEA_WORKFLOWS, FLOOD_FORECASTING and DATA_SOURCES, plus two snapshots (PROJECT_STATUS, PROJECT_STRUCTURE) describing a 4-file src/ that is now 39. scripts (5): one-shot bootstrap tools already run — init_git.sh/.bat, generate_badges.py, migrate_geolocation.py, encode_password.py. Every inbound reference was fixed rather than left dangling: README doc index and migration section, three Makefile targets, the docs.yml summary step, and the GITEA_WORKFLOWS resource list. src/ is deliberately untouched. The audit proposed removing several live modules; verification showed those proposals were mis-scoped and would have broken production. .gitignore now covers the agent tooling dirs, model eval output and editor/shell leftovers — the working tree had collected 56 zero-byte files named after fragments of shell commands. 136 tests pass; production modules import clean.
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Password URL encoder for PostgreSQL connection strings
|
||||
"""
|
||||
|
||||
import urllib.parse
|
||||
import sys
|
||||
|
||||
def encode_password(password: str) -> str:
|
||||
"""URL encode a password for use in connection strings"""
|
||||
return urllib.parse.quote(password, safe='')
|
||||
|
||||
def build_connection_string(username: str, password: str, host: str, port: int, database: str) -> str:
|
||||
"""Build a properly encoded PostgreSQL connection string"""
|
||||
encoded_password = encode_password(password)
|
||||
return f"postgresql://{username}:{encoded_password}@{host}:{port}/{database}"
|
||||
|
||||
def main():
|
||||
print("PostgreSQL Password URL Encoder")
|
||||
print("=" * 40)
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
# Password provided as argument
|
||||
password = sys.argv[1]
|
||||
else:
|
||||
# Interactive mode
|
||||
password = input("Enter your password: ")
|
||||
|
||||
encoded = encode_password(password)
|
||||
|
||||
print(f"\nOriginal password: {password}")
|
||||
print(f"URL encoded: {encoded}")
|
||||
|
||||
# Optional: build full connection string
|
||||
try:
|
||||
build_full = input("\nBuild full connection string? (y/N): ").strip().lower() == 'y'
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print("\nDone!")
|
||||
return
|
||||
|
||||
if build_full:
|
||||
username = input("Username: ").strip()
|
||||
host = input("Host: ").strip()
|
||||
port = input("Port [5432]: ").strip() or "5432"
|
||||
database = input("Database [water_monitoring]: ").strip() or "water_monitoring"
|
||||
|
||||
connection_string = build_connection_string(username, password, host, int(port), database)
|
||||
|
||||
print(f"\nComplete connection string:")
|
||||
print(f"POSTGRES_CONNECTION_STRING={connection_string}")
|
||||
|
||||
print(f"\nAdd this to your .env file:")
|
||||
print(f"DB_TYPE=postgresql")
|
||||
print(f"POSTGRES_CONNECTION_STRING={connection_string}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate status badges for README.md
|
||||
"""
|
||||
|
||||
import json
|
||||
import requests
|
||||
from datetime import datetime
|
||||
|
||||
def generate_badge_url(label, message, color="brightgreen"):
|
||||
"""Generate a shields.io badge URL"""
|
||||
return f"https://img.shields.io/badge/{label}-{message}-{color}"
|
||||
|
||||
def generate_workflow_badge(repo_url, workflow_name, branch="main"):
|
||||
"""Generate workflow status badge"""
|
||||
# For Gitea, you might need to adjust this based on your instance
|
||||
badge_url = f"{repo_url}/actions/workflows/{workflow_name}/badge.svg?branch={branch}"
|
||||
return badge_url
|
||||
|
||||
def main():
|
||||
"""Generate badges for the project"""
|
||||
repo_url = "https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor"
|
||||
|
||||
badges = {
|
||||
"CI/CD": generate_workflow_badge(repo_url, "ci.yml"),
|
||||
"Security": generate_workflow_badge(repo_url, "security.yml"),
|
||||
"Documentation": generate_workflow_badge(repo_url, "docs.yml"),
|
||||
"Python": generate_badge_url("Python", "3.9%2B", "blue"),
|
||||
"FastAPI": generate_badge_url("FastAPI", "0.104%2B", "green"),
|
||||
"Docker": generate_badge_url("Docker", "Ready", "blue"),
|
||||
"License": generate_badge_url("License", "MIT", "green"),
|
||||
"Version": generate_badge_url("Version", "v3.1.3", "blue"),
|
||||
}
|
||||
|
||||
print("# Status Badges")
|
||||
print()
|
||||
print("Add these badges to your README.md:")
|
||||
print()
|
||||
|
||||
for name, url in badges.items():
|
||||
print(f"[]({repo_url})")
|
||||
|
||||
print()
|
||||
print("# Markdown Format")
|
||||
print()
|
||||
|
||||
badge_line = " ".join([f"[]({repo_url})" for name, url in badges.items()])
|
||||
print(badge_line)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,35 +0,0 @@
|
||||
@echo off
|
||||
REM Git initialization script for Northern Thailand Ping River Monitor
|
||||
|
||||
echo 🏔️ Initializing Git repository for Northern Thailand Ping River Monitor
|
||||
|
||||
REM Initialize git repository
|
||||
git init
|
||||
|
||||
REM Add remote origin
|
||||
git remote add origin https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor.git
|
||||
|
||||
REM Add all files
|
||||
git add .
|
||||
|
||||
REM Initial commit
|
||||
git commit -m "Initial commit: Northern Thailand Ping River Monitor v3.1.3
|
||||
|
||||
Features:
|
||||
- Real-time water level monitoring for Ping River Basin
|
||||
- 16 monitoring stations from Chiang Dao to Nakhon Sawan
|
||||
- FastAPI web interface with station management
|
||||
- Multi-database support (SQLite, MySQL, PostgreSQL, InfluxDB, VictoriaMetrics)
|
||||
- Comprehensive monitoring and health checks
|
||||
- Docker deployment with Grafana integration
|
||||
- Production-ready architecture with CI/CD pipeline"
|
||||
|
||||
echo ✅ Git repository initialized successfully!
|
||||
echo.
|
||||
echo Next steps:
|
||||
echo 1. Review and edit .env file with your configuration
|
||||
echo 2. Push to remote repository:
|
||||
echo git push -u origin main
|
||||
echo.
|
||||
echo 3. Start the application:
|
||||
echo python run.py --web-api
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Git initialization script for Northern Thailand Ping River Monitor
|
||||
|
||||
echo "🏔️ Initializing Git repository for Northern Thailand Ping River Monitor"
|
||||
|
||||
# Initialize git repository
|
||||
git init
|
||||
|
||||
# Add remote origin
|
||||
git remote add origin https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor.git
|
||||
|
||||
# Create .gitignore if it doesn't exist
|
||||
if [ ! -f .gitignore ]; then
|
||||
echo "Creating .gitignore file..."
|
||||
cat > .gitignore << 'EOF'
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Virtual environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Database files
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Add all files
|
||||
git add .
|
||||
|
||||
# Initial commit
|
||||
git commit -m "Initial commit: Northern Thailand Ping River Monitor v3.1.3
|
||||
|
||||
Features:
|
||||
- Real-time water level monitoring for Ping River Basin
|
||||
- 16 monitoring stations from Chiang Dao to Nakhon Sawan
|
||||
- FastAPI web interface with station management
|
||||
- Multi-database support (SQLite, MySQL, PostgreSQL, InfluxDB, VictoriaMetrics)
|
||||
- Comprehensive monitoring and health checks
|
||||
- Docker deployment with Grafana integration
|
||||
- Production-ready architecture with CI/CD pipeline"
|
||||
|
||||
echo "✅ Git repository initialized successfully!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Review and edit .env file with your configuration"
|
||||
echo "2. Push to remote repository:"
|
||||
echo " git push -u origin main"
|
||||
echo ""
|
||||
echo "3. Start the application:"
|
||||
echo " make run-api"
|
||||
echo " # or: python run.py --web-api"
|
||||
@@ -1,294 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Migration script to add geolocation columns to existing water monitoring database
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sqlite3
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
def migrate_sqlite(db_path: str = 'water_monitoring.db') -> bool:
|
||||
"""Migrate SQLite database to add geolocation columns"""
|
||||
try:
|
||||
logging.info(f"Migrating SQLite database: {db_path}")
|
||||
|
||||
# Connect to database
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if columns already exist
|
||||
cursor.execute("PRAGMA table_info(stations)")
|
||||
columns = [column[1] for column in cursor.fetchall()]
|
||||
|
||||
logging.info(f"Current columns in stations table: {columns}")
|
||||
|
||||
# Add columns if they don't exist
|
||||
columns_added = []
|
||||
|
||||
if 'latitude' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN latitude REAL")
|
||||
columns_added.append('latitude')
|
||||
logging.info("Added latitude column")
|
||||
|
||||
if 'longitude' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN longitude REAL")
|
||||
columns_added.append('longitude')
|
||||
logging.info("Added longitude column")
|
||||
|
||||
if 'geohash' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN geohash TEXT")
|
||||
columns_added.append('geohash')
|
||||
logging.info("Added geohash column")
|
||||
|
||||
if columns_added:
|
||||
# Update P.1 station with sample geolocation data
|
||||
cursor.execute("""
|
||||
UPDATE stations
|
||||
SET latitude = 15.6944, longitude = 100.2028, geohash = 'w5q6uuhvfcfp25'
|
||||
WHERE station_code = 'P.1'
|
||||
""")
|
||||
|
||||
# Commit changes
|
||||
conn.commit()
|
||||
logging.info(f"Successfully added columns: {', '.join(columns_added)}")
|
||||
logging.info("Updated P.1 station with sample geolocation data")
|
||||
else:
|
||||
logging.info("All geolocation columns already exist")
|
||||
|
||||
# Verify the changes
|
||||
cursor.execute("SELECT station_code, latitude, longitude, geohash FROM stations WHERE station_code = 'P.1'")
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
logging.info(f"P.1 station geolocation: {result}")
|
||||
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error migrating SQLite database: {e}")
|
||||
return False
|
||||
|
||||
def migrate_postgresql(connection_string: str) -> bool:
|
||||
"""Migrate PostgreSQL database to add geolocation columns"""
|
||||
try:
|
||||
import psycopg2
|
||||
from urllib.parse import urlparse
|
||||
|
||||
logging.info("Migrating PostgreSQL database")
|
||||
|
||||
# Parse connection string
|
||||
parsed = urlparse(connection_string)
|
||||
|
||||
# Connect to database
|
||||
conn = psycopg2.connect(
|
||||
host=parsed.hostname,
|
||||
port=parsed.port or 5432,
|
||||
database=parsed.path[1:], # Remove leading slash
|
||||
user=parsed.username,
|
||||
password=parsed.password
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if columns exist
|
||||
cursor.execute("""
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'stations'
|
||||
""")
|
||||
columns = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
logging.info(f"Current columns in stations table: {columns}")
|
||||
|
||||
# Add columns if they don't exist
|
||||
columns_added = []
|
||||
|
||||
if 'latitude' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN latitude DECIMAL(10,8)")
|
||||
columns_added.append('latitude')
|
||||
logging.info("Added latitude column")
|
||||
|
||||
if 'longitude' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN longitude DECIMAL(11,8)")
|
||||
columns_added.append('longitude')
|
||||
logging.info("Added longitude column")
|
||||
|
||||
if 'geohash' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN geohash VARCHAR(20)")
|
||||
columns_added.append('geohash')
|
||||
logging.info("Added geohash column")
|
||||
|
||||
if columns_added:
|
||||
# Update P.1 station with sample geolocation data
|
||||
cursor.execute("""
|
||||
UPDATE stations
|
||||
SET latitude = 15.6944, longitude = 100.2028, geohash = 'w5q6uuhvfcfp25'
|
||||
WHERE station_code = 'P.1'
|
||||
""")
|
||||
|
||||
# Commit changes
|
||||
conn.commit()
|
||||
logging.info(f"Successfully added columns: {', '.join(columns_added)}")
|
||||
logging.info("Updated P.1 station with sample geolocation data")
|
||||
else:
|
||||
logging.info("All geolocation columns already exist")
|
||||
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
except ImportError:
|
||||
logging.error("psycopg2 not installed. Run: pip install psycopg2-binary")
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.error(f"Error migrating PostgreSQL database: {e}")
|
||||
return False
|
||||
|
||||
def migrate_mysql(connection_string: str) -> bool:
|
||||
"""Migrate MySQL database to add geolocation columns"""
|
||||
try:
|
||||
import pymysql
|
||||
from urllib.parse import urlparse
|
||||
|
||||
logging.info("Migrating MySQL database")
|
||||
|
||||
# Parse connection string
|
||||
parsed = urlparse(connection_string)
|
||||
|
||||
# Connect to database
|
||||
conn = pymysql.connect(
|
||||
host=parsed.hostname,
|
||||
port=parsed.port or 3306,
|
||||
database=parsed.path[1:], # Remove leading slash
|
||||
user=parsed.username,
|
||||
password=parsed.password
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if columns exist
|
||||
cursor.execute("DESCRIBE stations")
|
||||
columns = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
logging.info(f"Current columns in stations table: {columns}")
|
||||
|
||||
# Add columns if they don't exist
|
||||
columns_added = []
|
||||
|
||||
if 'latitude' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN latitude DECIMAL(10,8)")
|
||||
columns_added.append('latitude')
|
||||
logging.info("Added latitude column")
|
||||
|
||||
if 'longitude' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN longitude DECIMAL(11,8)")
|
||||
columns_added.append('longitude')
|
||||
logging.info("Added longitude column")
|
||||
|
||||
if 'geohash' not in columns:
|
||||
cursor.execute("ALTER TABLE stations ADD COLUMN geohash VARCHAR(20)")
|
||||
columns_added.append('geohash')
|
||||
logging.info("Added geohash column")
|
||||
|
||||
if columns_added:
|
||||
# Update P.1 station with sample geolocation data
|
||||
cursor.execute("""
|
||||
UPDATE stations
|
||||
SET latitude = 15.6944, longitude = 100.2028, geohash = 'w5q6uuhvfcfp25'
|
||||
WHERE station_code = 'P.1'
|
||||
""")
|
||||
|
||||
# Commit changes
|
||||
conn.commit()
|
||||
logging.info(f"Successfully added columns: {', '.join(columns_added)}")
|
||||
logging.info("Updated P.1 station with sample geolocation data")
|
||||
else:
|
||||
logging.info("All geolocation columns already exist")
|
||||
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
except ImportError:
|
||||
logging.error("pymysql not installed. Run: pip install pymysql")
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.error(f"Error migrating MySQL database: {e}")
|
||||
return False
|
||||
|
||||
def load_config_from_env() -> Dict[str, Any]:
|
||||
"""Load database configuration from environment variables"""
|
||||
db_type = os.getenv('DB_TYPE', 'sqlite').lower()
|
||||
|
||||
if db_type == 'postgresql':
|
||||
return {
|
||||
'type': 'postgresql',
|
||||
'connection_string': os.getenv('POSTGRES_CONNECTION_STRING',
|
||||
'postgresql://postgres:password@localhost/water_monitoring')
|
||||
}
|
||||
elif db_type == 'mysql':
|
||||
return {
|
||||
'type': 'mysql',
|
||||
'connection_string': os.getenv('MYSQL_CONNECTION_STRING',
|
||||
'mysql://root:password@localhost/water_monitoring')
|
||||
}
|
||||
elif db_type == 'victoriametrics':
|
||||
logging.info("VictoriaMetrics doesn't require schema migration")
|
||||
return {'type': 'victoriametrics'}
|
||||
elif db_type == 'influxdb':
|
||||
logging.info("InfluxDB doesn't require schema migration")
|
||||
return {'type': 'influxdb'}
|
||||
else:
|
||||
# Default to SQLite
|
||||
return {
|
||||
'type': 'sqlite',
|
||||
'db_path': os.getenv('SQLITE_DB_PATH', 'water_monitoring.db')
|
||||
}
|
||||
|
||||
def main():
|
||||
"""Main migration function"""
|
||||
logging.info("Starting geolocation column migration...")
|
||||
|
||||
# Load configuration
|
||||
config = load_config_from_env()
|
||||
db_type = config['type']
|
||||
|
||||
logging.info(f"Detected database type: {db_type.upper()}")
|
||||
|
||||
success = False
|
||||
|
||||
if db_type == 'sqlite':
|
||||
db_path = config.get('db_path', 'water_monitoring.db')
|
||||
if not os.path.exists(db_path):
|
||||
logging.error(f"Database file not found: {db_path}")
|
||||
sys.exit(1)
|
||||
success = migrate_sqlite(db_path)
|
||||
|
||||
elif db_type == 'postgresql':
|
||||
success = migrate_postgresql(config['connection_string'])
|
||||
|
||||
elif db_type == 'mysql':
|
||||
success = migrate_mysql(config['connection_string'])
|
||||
|
||||
elif db_type in ['victoriametrics', 'influxdb']:
|
||||
logging.info(f"{db_type.upper()} doesn't require schema migration")
|
||||
success = True
|
||||
|
||||
else:
|
||||
logging.error(f"Unsupported database type: {db_type}")
|
||||
sys.exit(1)
|
||||
|
||||
if success:
|
||||
logging.info("✅ Migration completed successfully!")
|
||||
logging.info("You can now restart your water monitoring application")
|
||||
logging.info("The system will automatically use the new geolocation columns")
|
||||
else:
|
||||
logging.error("❌ Migration failed!")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user