Add database cleanup feature to empty all sensor tables
Added a "Danger Zone" section on the database page that allows users to empty all sensor data tables while preserving configuration and timestamp tables. The feature includes: - New Python script (sqlite/empty_sensor_tables.py) to safely empty sensor tables - Backend endpoint in launcher.php (empty_sensor_tables) - Frontend UI with red warning card and confirmation dialog - Detailed feedback showing deleted record counts per table - i18n support for French and English Tables emptied: data_NPM, data_NPM_5channels, data_BME280, data_envea, data_WIND, data_MPPT, data_NOISE, modem_status Tables preserved: timestamp_table, config_table, envea_sondes_table, config_scripts_table 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
204
sqlite/empty_sensor_tables.py
Normal file
204
sqlite/empty_sensor_tables.py
Normal file
@@ -0,0 +1,204 @@
|
||||
'''
|
||||
____ ___ _ _ _
|
||||
/ ___| / _ \| | (_) |_ ___
|
||||
\___ \| | | | | | | __/ _ \
|
||||
___) | |_| | |___| | || __/
|
||||
|____/ \__\_\_____|_|\__\___|
|
||||
|
||||
Script to empty (delete all data from) sensor tables in the SQLite database
|
||||
This script empties sensor data tables but preserves:
|
||||
- timestamp_table
|
||||
- config_table
|
||||
- envea_sondes_table
|
||||
- config_scripts_table
|
||||
|
||||
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/empty_sensor_tables.py
|
||||
'''
|
||||
|
||||
import sqlite3
|
||||
import sys
|
||||
import json
|
||||
|
||||
def table_exists(cursor, table_name):
|
||||
"""Check if a table exists in the database"""
|
||||
try:
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
|
||||
return cursor.fetchone() is not None
|
||||
except sqlite3.Error as e:
|
||||
print(f"[ERROR] Failed to check if table '{table_name}' exists: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_table_count(cursor, table_name):
|
||||
"""Get the number of records in a table"""
|
||||
try:
|
||||
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
|
||||
return cursor.fetchone()[0]
|
||||
except sqlite3.Error as e:
|
||||
print(f"[WARNING] Could not get count for table '{table_name}': {e}")
|
||||
return 0
|
||||
|
||||
|
||||
def empty_table(cursor, table_name):
|
||||
"""Delete all records from a specific table"""
|
||||
try:
|
||||
# Get record count before deletion
|
||||
initial_count = get_table_count(cursor, table_name)
|
||||
|
||||
if initial_count == 0:
|
||||
print(f"[INFO] Table '{table_name}' is already empty")
|
||||
return True, 0
|
||||
|
||||
# Delete all records
|
||||
cursor.execute(f"DELETE FROM {table_name}")
|
||||
deleted_count = cursor.rowcount
|
||||
|
||||
print(f"[SUCCESS] Deleted {deleted_count} records from '{table_name}'")
|
||||
return True, deleted_count
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"[ERROR] Failed to empty table '{table_name}': {e}")
|
||||
return False, 0
|
||||
|
||||
|
||||
def main():
|
||||
result = {
|
||||
'success': False,
|
||||
'message': '',
|
||||
'tables_processed': [],
|
||||
'total_deleted': 0
|
||||
}
|
||||
|
||||
try:
|
||||
# Connect to the SQLite database
|
||||
print("[INFO] Connecting to database...")
|
||||
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check database connection
|
||||
cursor.execute("SELECT sqlite_version()")
|
||||
version = cursor.fetchone()[0]
|
||||
print(f"[INFO] Connected to SQLite version: {version}")
|
||||
|
||||
# List of sensor tables to empty (EXCLUDING timestamp_table and config tables)
|
||||
sensor_tables = [
|
||||
"data_NPM",
|
||||
"data_NPM_5channels",
|
||||
"data_BME280",
|
||||
"data_envea",
|
||||
"data_WIND",
|
||||
"data_MPPT",
|
||||
"data_NOISE",
|
||||
"modem_status"
|
||||
]
|
||||
|
||||
# Tables to PRESERVE (not empty)
|
||||
preserved_tables = [
|
||||
"timestamp_table",
|
||||
"config_table",
|
||||
"envea_sondes_table",
|
||||
"config_scripts_table"
|
||||
]
|
||||
|
||||
print(f"[INFO] Will empty the following sensor tables: {', '.join(sensor_tables)}")
|
||||
print(f"[INFO] Will preserve the following tables: {', '.join(preserved_tables)}")
|
||||
|
||||
# Check which tables actually exist
|
||||
existing_tables = []
|
||||
missing_tables = []
|
||||
|
||||
for table in sensor_tables:
|
||||
if table_exists(cursor, table):
|
||||
existing_tables.append(table)
|
||||
record_count = get_table_count(cursor, table)
|
||||
print(f"[INFO] Table '{table}' exists with {record_count} records")
|
||||
else:
|
||||
missing_tables.append(table)
|
||||
print(f"[WARNING] Table '{table}' does not exist - skipping")
|
||||
|
||||
if missing_tables:
|
||||
print(f"[INFO] Missing tables: {', '.join(missing_tables)}")
|
||||
|
||||
if not existing_tables:
|
||||
result['success'] = True
|
||||
result['message'] = "No sensor tables found to empty"
|
||||
print("[WARNING] No sensor tables found to empty!")
|
||||
print(json.dumps(result))
|
||||
return True
|
||||
|
||||
# Loop through existing tables and empty them
|
||||
successful_operations = 0
|
||||
failed_operations = 0
|
||||
total_deleted = 0
|
||||
|
||||
for table in existing_tables:
|
||||
success, deleted = empty_table(cursor, table)
|
||||
if success:
|
||||
successful_operations += 1
|
||||
total_deleted += deleted
|
||||
result['tables_processed'].append({
|
||||
'name': table,
|
||||
'deleted': deleted
|
||||
})
|
||||
else:
|
||||
failed_operations += 1
|
||||
|
||||
# Commit changes
|
||||
print("[INFO] Committing changes...")
|
||||
conn.commit()
|
||||
print("[SUCCESS] Changes committed successfully!")
|
||||
|
||||
# Run VACUUM to optimize database space
|
||||
if total_deleted > 0:
|
||||
print("[INFO] Running VACUUM to optimize database space...")
|
||||
try:
|
||||
cursor.execute("VACUUM")
|
||||
print("[SUCCESS] Database optimized successfully!")
|
||||
except sqlite3.Error as e:
|
||||
print(f"[WARNING] VACUUM failed: {e}")
|
||||
|
||||
# Summary
|
||||
print(f"\n[SUMMARY]")
|
||||
print(f"Tables emptied successfully: {successful_operations}")
|
||||
print(f"Tables with errors: {failed_operations}")
|
||||
print(f"Tables skipped (missing): {len(missing_tables)}")
|
||||
print(f"Total records deleted: {total_deleted}")
|
||||
|
||||
result['success'] = True
|
||||
result['message'] = f"Successfully emptied {successful_operations} sensor tables. Total records deleted: {total_deleted}"
|
||||
result['total_deleted'] = total_deleted
|
||||
|
||||
if failed_operations == 0:
|
||||
print("[SUCCESS] All sensor tables emptied successfully!")
|
||||
else:
|
||||
result['message'] = f"Partial success: {successful_operations} tables emptied, {failed_operations} failed"
|
||||
print("[WARNING] Some operations failed - check logs above")
|
||||
|
||||
# Output JSON result for web interface
|
||||
print("\n[JSON_RESULT]")
|
||||
print(json.dumps(result))
|
||||
|
||||
return failed_operations == 0
|
||||
|
||||
except sqlite3.Error as e:
|
||||
result['message'] = f"Database error: {e}"
|
||||
print(f"[ERROR] Database error: {e}")
|
||||
print("\n[JSON_RESULT]")
|
||||
print(json.dumps(result))
|
||||
return False
|
||||
except Exception as e:
|
||||
result['message'] = f"Unexpected error: {e}"
|
||||
print(f"[ERROR] Unexpected error: {e}")
|
||||
print("\n[JSON_RESULT]")
|
||||
print(json.dumps(result))
|
||||
return False
|
||||
finally:
|
||||
# Always close the database connection
|
||||
if 'conn' in locals():
|
||||
conn.close()
|
||||
print("[INFO] Database connection closed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user