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

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:
2026-08-14 13:45:03 +07:00
parent 7e64e0cf18
commit f6570ac10f
37 changed files with 20 additions and 4878 deletions
-57
View File
@@ -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()
-51
View File
@@ -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"[![{name}]({url})]({repo_url})")
print()
print("# Markdown Format")
print()
badge_line = " ".join([f"[![{name}]({url})]({repo_url})" for name, url in badges.items()])
print(badge_line)
if __name__ == "__main__":
main()
-35
View File
@@ -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
-89
View File
@@ -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"
-294
View File
@@ -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()