Files
TimeTracker/migrations
Dries Peeters 18d9808d5e feat: add user favorite projects functionality with CSV export enhancements
Features:
Add favorite projects feature allowing users to star/bookmark frequently used projects
New UserFavoriteProject association model with user-project relationships
Star icons in project list for one-click favorite toggling via AJAX
Filter to display only favorite projects
Per-user favorites with proper isolation and cascade delete behavior
Activity logging for favorite/unfavorite actions
Database:
Add user_favorite_projects table with migration (023_add_user_favorite_projects.py)
Foreign keys to users and projects with CASCADE delete
Unique constraint preventing duplicate favorites
Indexes on user_id and project_id for query optimization
Models:
User model: Add favorite_projects relationship with helper methods
add_favorite_project() - add project to favorites
remove_favorite_project() - remove from favorites
is_project_favorite() - check favorite status
get_favorite_projects() - retrieve favorites with status filter
Project model: Add is_favorited_by() method and include favorite status in to_dict()
Export UserFavoriteProject model in app/models/__init__.py
Routes:
Add /projects/<id>/favorite POST endpoint to favorite a project
Add /projects/<id>/unfavorite POST endpoint to unfavorite a project
Update /projects GET route to support favorites=true query parameter
Fix status filtering to work correctly with favorites JOIN query
Add /reports/export/form GET endpoint for enhanced CSV export form
Templates:
Update projects/list.html:
Add favorites filter dropdown to filter form (5-column grid)
Add star icon column with Font Awesome icons (filled/unfilled)
Add JavaScript toggleFavorite() function for AJAX favorite toggling
Improve hover states and transitions for better UX
Pass favorite_project_ids and favorites_only to template
Update reports/index.html:
Update CSV export link to point to new export form
Add icon and improve hover styling
Reports:
Enhance CSV export functionality with dedicated form page
Add filter options for users, projects, clients, and date ranges
Set default date range to last 30 days
Import Client model and or_ operator for advanced filtering
Testing:
Comprehensive test suite in tests/test_favorite_projects.py (550+ lines)
Model tests for UserFavoriteProject creation and validation
User/Project method tests for favorite operations
Route tests for favorite/unfavorite endpoints
Filtering tests for favorites-only view
Relationship tests for cascade delete behavior
Smoke tests for complete workflows
Coverage for edge cases and error handling
Documentation:
Add comprehensive feature documentation in docs/FAVORITE_PROJECTS_FEATURE.md
User guide with step-by-step instructions
Technical implementation details
API documentation for new endpoints
Migration guide and troubleshooting
Performance and security considerations
Template Cleanup:
Remove duplicate templates from root templates/ directory
Admin templates (dashboard, users, settings, OIDC debug, etc.)
Client CRUD templates
Error page templates
Invoice templates
Project templates
Report templates
Timer templates
All templates now properly located in app/templates/
Breaking Changes:
None - fully backward compatible
Migration Required:
Run alembic upgrade head to create user_favorite_projects table
2025-10-23 21:15:16 +02:00
..

Database Migrations with Flask-Migrate

This directory contains the database migration system for TimeTracker, now standardized on Flask-Migrate with proper versioning.

Overview

The migration system has been updated from custom Python scripts to use Flask-Migrate, which provides:

  • Standardized migrations using Alembic
  • Version tracking for all database changes
  • Rollback capabilities to previous versions
  • Automatic schema detection from SQLAlchemy models
  • Cross-database compatibility (PostgreSQL, SQLite)

Quick Start

1. Initialize Migrations (First Time Only)

flask db init

2. Create Your First Migration

flask db migrate -m "Initial database schema"

3. Apply Migrations

flask db upgrade

Migration Commands

Basic Commands

  • flask db init - Initialize migrations directory
  • flask db migrate -m "Description" - Create a new migration
  • flask db upgrade - Apply pending migrations
  • flask db downgrade - Rollback last migration
  • flask db current - Show current migration version
  • flask db history - Show migration history

Advanced Commands

  • flask db show <revision> - Show specific migration details
  • flask db stamp <revision> - Mark database as being at specific revision
  • flask db heads - Show current heads (for branched migrations)

Migration Workflow

1. Make Model Changes

Edit your SQLAlchemy models in app/models/:

# Example: Add a new column
class User(db.Model):
    # ... existing fields ...
    phone_number = db.Column(db.String(20), nullable=True)

2. Generate Migration

flask db migrate -m "Add phone number to users"

3. Review Generated Migration

Check the generated migration file in migrations/versions/:

def upgrade():
    op.add_column('users', sa.Column('phone_number', sa.String(length=20), nullable=True))

def downgrade():
    op.drop_column('users', 'phone_number')

4. Apply Migration

flask db upgrade

5. Verify Changes

Check the migration status:

flask db current

Migration Files Structure

migrations/
├── versions/           # Migration files
│   ├── 001_initial_schema.py
│   ├── 002_add_phone_number.py
│   └── ...
├── env.py             # Migration environment
├── script.py.mako     # Migration template
├── alembic.ini        # Alembic configuration
└── README.md          # This file

Transition from Old System

If you're migrating from the old custom migration system:

1. Backup Your Database

# PostgreSQL
pg_dump --format=custom --dbname="$DATABASE_URL" --file=backup_$(date +%Y%m%d_%H%M%S).dump

# SQLite
cp instance/timetracker.db backup_timetracker_$(date +%Y%m%d_%H%M%S).db

2. Use Migration Management Script

python migrations/manage_migrations.py

3. Or Manual Migration

# Initialize Flask-Migrate
flask db init

# Create initial migration (captures current schema)
flask db migrate -m "Initial schema from existing database"

# Apply migration
flask db upgrade

Best Practices

1. Migration Naming

Use descriptive names for migrations:

flask db migrate -m "Add user profile fields"
flask db migrate -m "Create project categories table"
flask db migrate -m "Add invoice payment tracking"

2. Testing Migrations

Always test migrations on a copy of your production data:

# Test upgrade
flask db upgrade

# Test downgrade
flask db downgrade

# Verify data integrity

3. Backup Before Migrations

# Always backup before major migrations
flask db backup  # Custom command
# or
pg_dump --format=custom --dbname="$DATABASE_URL" --file=pre_migration_backup.dump

4. Review Generated Code

Always review auto-generated migrations before applying:

  • Check the upgrade() function
  • Verify the downgrade() function
  • Ensure data types and constraints are correct

Troubleshooting

Common Issues

1. Migration Already Applied

# Check current status
flask db current

# If migration is already applied, stamp the database
flask db stamp <revision>

2. Migration Conflicts

# Show migration heads
flask db heads

# Merge branches if needed
flask db merge -m "Merge migration branches" <revision1> <revision2>

3. Database Out of Sync

# Check migration history
flask db history

# Reset to specific revision
flask db stamp <revision>

4. Model Import Errors

Ensure all models are imported in your application:

# In app/__init__.py or similar
from app.models import User, Project, TimeEntry, Task, Settings, Invoice, Client

Getting Help

  1. Check the migration status: flask db current
  2. Review migration history: flask db history
  3. Check Alembic logs for detailed error messages
  4. Verify database connection and permissions

Advanced Features

Custom Migration Operations

You can add custom operations in your migrations:

def upgrade():
    # Custom data migration
    op.execute("UPDATE users SET role = 'user' WHERE role IS NULL")
    
    # Custom table operations
    op.create_index('custom_idx', 'table_name', ['column_name'])

Data Migrations

For complex data migrations, use the op.execute() method:

def upgrade():
    # Migrate existing data
    op.execute("""
        INSERT INTO new_table (id, name)
        SELECT id, name FROM old_table
    """)

Conditional Migrations

Handle different database types:

def upgrade():
    # PostgreSQL-specific operations
    if op.get_bind().dialect.name == 'postgresql':
        op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')

Environment Variables

Ensure these environment variables are set:

export FLASK_APP=app.py
export DATABASE_URL="postgresql://user:pass@localhost/dbname"
# or
export DATABASE_URL="sqlite:///instance/timetracker.db"

CI/CD Integration

For automated deployments, include migration steps:

# Example GitHub Actions step
- name: Run database migrations
  run: |
    flask db upgrade
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}

Support

For migration-related issues:

  1. Check this README
  2. Review Flask-Migrate documentation: https://flask-migrate.readthedocs.io/
  3. Check Alembic documentation: https://alembic.sqlalchemy.org/
  4. Review generated migration files for errors