Files
TimeTracker/docker/migrate-field-names.py
Dries Peeters 1b3a703c04 feat: comprehensive project cleanup and timezone enhancement
- Remove redundant documentation files (DATABASE_INIT_FIX_*.md, TIMEZONE_FIX_README.md)
- Delete unused Docker files (Dockerfile.test, Dockerfile.combined, docker-compose.yml)
- Remove obsolete deployment scripts (deploy.sh) and unused files (index.html, _config.yml)
- Clean up logs directory (remove 2MB timetracker.log, keep .gitkeep)
- Remove .pytest_cache directory

- Consolidate Docker setup to two main container types:
  * Simple container (recommended for production)
  * Public container (for development/testing)

- Enhance timezone support in admin settings:
  * Add 100+ timezone options organized by region
  * Implement real-time timezone preview with current time display
  * Add timezone offset calculation and display
  * Remove search functionality for cleaner interface
  * Update timezone utility functions for database-driven configuration

- Update documentation:
  * Revise README.md to reflect current project state
  * Add comprehensive timezone features documentation
  * Update Docker deployment instructions
  * Create PROJECT_STRUCTURE.md for project overview
  * Remove references to deleted files

- Improve project structure:
  * Streamlined file organization
  * Better maintainability and focus
  * Preserved all essential functionality
  * Cleaner deployment options
2025-08-28 14:52:09 +02:00

166 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""
Migration script to rename database fields from start_utc/end_utc to start_time/end_time
This script should be run after updating the application code but before starting the new version.
"""
import os
import sys
from sqlalchemy import create_engine, text, inspect
def wait_for_database(url, max_attempts=30, delay=2):
"""Wait for database to be ready"""
print(f"Waiting for database to be ready...")
for attempt in range(max_attempts):
try:
engine = create_engine(url, pool_pre_ping=True)
with engine.connect() as conn:
conn.execute(text("SELECT 1"))
print("Database connection established successfully")
return engine
except Exception as e:
print(f"Waiting for database... (attempt {attempt+1}/{max_attempts}): {e}")
if attempt < max_attempts - 1:
time.sleep(delay)
else:
print("Database not ready after waiting, exiting...")
sys.exit(1)
return None
def check_migration_needed(engine):
"""Check if migration is needed"""
print("Checking if migration is needed...")
try:
inspector = inspect(engine)
columns = inspector.get_columns('time_entries')
column_names = [col['name'] for col in columns]
has_old_fields = 'start_utc' in column_names or 'end_utc' in column_names
has_new_fields = 'start_time' in column_names or 'end_time' in column_names
if has_old_fields and not has_new_fields:
print("✓ Migration needed: old field names detected")
return True
elif has_new_fields and not has_old_fields:
print("✓ Migration not needed: new field names already exist")
return False
elif has_old_fields and has_new_fields:
print("⚠ Migration partially done: both old and new field names exist")
return True
else:
print("⚠ Unknown state: neither old nor new field names found")
return True
except Exception as e:
print(f"✗ Error checking migration status: {e}")
return True
def migrate_database(engine):
"""Perform the migration"""
print("Starting database migration...")
try:
with engine.connect() as conn:
# Start transaction
trans = conn.begin()
try:
# Check if old columns exist
inspector = inspect(engine)
columns = inspector.get_columns('time_entries')
column_names = [col['name'] for col in columns]
if 'start_utc' in column_names:
print("Renaming start_utc to start_time...")
conn.execute(text("ALTER TABLE time_entries RENAME COLUMN start_utc TO start_time"))
if 'end_utc' in column_names:
print("Renaming end_utc to end_time...")
conn.execute(text("ALTER TABLE time_entries RENAME COLUMN end_utc TO end_time"))
# Update indexes
print("Updating indexes...")
# Drop old index if it exists
try:
conn.execute(text("DROP INDEX IF EXISTS idx_time_entries_start_utc"))
except:
pass
# Create new index
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_time_entries_start_time ON time_entries(start_time)"))
# Commit transaction
trans.commit()
print("✓ Migration completed successfully")
return True
except Exception as e:
trans.rollback()
print(f"✗ Migration failed: {e}")
return False
except Exception as e:
print(f"✗ Error during migration: {e}")
return False
def verify_migration(engine):
"""Verify the migration was successful"""
print("Verifying migration...")
try:
inspector = inspect(engine)
columns = inspector.get_columns('time_entries')
column_names = [col['name'] for col in columns]
has_new_fields = 'start_time' in column_names and 'end_time' in column_names
has_old_fields = 'start_utc' in column_names or 'end_utc' in column_names
if has_new_fields and not has_old_fields:
print("✓ Migration verified: new field names are present, old ones are gone")
return True
else:
print(f"✗ Migration verification failed: new fields: {has_new_fields}, old fields: {has_old_fields}")
return False
except Exception as e:
print(f"✗ Error verifying migration: {e}")
return False
def main():
"""Main function"""
url = os.getenv("DATABASE_URL", "")
if not url.startswith("postgresql"):
print("No PostgreSQL database configured, skipping migration")
return
print(f"Database URL: {url}")
# Wait for database to be ready
engine = wait_for_database(url)
# Check if migration is needed
if not check_migration_needed(engine):
print("No migration needed, exiting...")
return
# Perform migration
if not migrate_database(engine):
print("Migration failed, exiting...")
sys.exit(1)
# Verify migration
if not verify_migration(engine):
print("Migration verification failed, exiting...")
sys.exit(1)
print("✓ Database migration completed successfully!")
if __name__ == "__main__":
import time
main()