- Create MapRoute database records for all GPX files for map visualization - Populate route statistics (distance, elevation, coordinates) from GPX parsing - Update GPX file statistics to mirror MapRoute data for post detail pages - Enable /community/api/routes endpoint to return proper route data for map iframe - Fix post detail page GPX statistics display This resolves the issue where the community map showed '2 routes discovered' but routes weren't actually rendering on the Leaflet.js map visualization. Changes: - Dockerfile: Updated init script paths and added migrate-db call - app/__init__.py: Added admin user auto-creation with error handling - app/routes/community.py: Added debug logging and API route for map data - docker-compose.yml: Simplified to use .env for environment variables - run.py: Added comprehensive database schema migration command
186 lines
6.7 KiB
Python
186 lines
6.7 KiB
Python
import os
|
|
from app import create_app, db
|
|
from app.models import User, Post, PostImage, GPXFile, Comment, Like, PageView, MapRoute
|
|
|
|
app = create_app()
|
|
|
|
@app.shell_context_processor
|
|
def make_shell_context():
|
|
return {
|
|
'db': db,
|
|
'User': User,
|
|
'Post': Post,
|
|
'PostImage': PostImage,
|
|
'GPXFile': GPXFile,
|
|
'Comment': Comment,
|
|
'Like': Like,
|
|
'PageView': PageView,
|
|
'MapRoute': MapRoute
|
|
}
|
|
|
|
@app.cli.command()
|
|
def init_db():
|
|
"""Initialize the database with complete schema."""
|
|
print('Creating all database tables...')
|
|
db.create_all()
|
|
print('✓ Database schema created successfully')
|
|
|
|
@app.cli.command()
|
|
def migrate_db():
|
|
"""Apply all database schema migrations to ensure compatibility."""
|
|
from sqlalchemy import text, inspect
|
|
|
|
try:
|
|
# Get database inspector
|
|
inspector = inspect(db.engine)
|
|
|
|
print('Starting database schema migration...\n')
|
|
|
|
# 1. Check and migrate posts table
|
|
if 'posts' in inspector.get_table_names():
|
|
columns = [col['name'] for col in inspector.get_columns('posts')]
|
|
|
|
if 'media_folder' not in columns:
|
|
db.session.execute(text('ALTER TABLE posts ADD COLUMN media_folder VARCHAR(100)'))
|
|
db.session.commit()
|
|
print('✓ Added media_folder column to posts table')
|
|
|
|
# Check for other expected columns
|
|
expected = ['title', 'content', 'author_id', 'published', 'created_at']
|
|
for col in expected:
|
|
if col not in columns:
|
|
print(f'✗ WARNING: Expected column {col} not found in posts table')
|
|
|
|
# 2. Check and migrate post_images table
|
|
if 'post_images' in inspector.get_table_names():
|
|
columns = [col['name'] for col in inspector.get_columns('post_images')]
|
|
|
|
if 'is_cover' not in columns:
|
|
db.session.execute(text('ALTER TABLE post_images ADD COLUMN is_cover BOOLEAN DEFAULT 0'))
|
|
db.session.commit()
|
|
print('✓ Added is_cover column to post_images table')
|
|
|
|
# 3. Check and migrate chat_rooms table
|
|
if 'chat_rooms' in inspector.get_table_names():
|
|
columns = [col['name'] for col in inspector.get_columns('chat_rooms')]
|
|
|
|
if 'category' not in columns:
|
|
db.session.execute(text('ALTER TABLE chat_rooms ADD COLUMN category VARCHAR(50) DEFAULT "general"'))
|
|
db.session.commit()
|
|
print('✓ Added category column to chat_rooms table')
|
|
|
|
if 'last_activity' not in columns:
|
|
db.session.execute(text('ALTER TABLE chat_rooms ADD COLUMN last_activity DATETIME DEFAULT CURRENT_TIMESTAMP'))
|
|
db.session.commit()
|
|
print('✓ Added last_activity column to chat_rooms table')
|
|
|
|
# 4. Check and add GPX statistics columns
|
|
if 'gpx_files' in inspector.get_table_names():
|
|
columns = [col['name'] for col in inspector.get_columns('gpx_files')]
|
|
|
|
gpx_columns = {
|
|
'total_distance': 'REAL DEFAULT 0.0',
|
|
'elevation_gain': 'REAL DEFAULT 0.0',
|
|
'max_elevation': 'REAL DEFAULT 0.0',
|
|
'min_elevation': 'REAL DEFAULT 0.0',
|
|
'total_points': 'INTEGER DEFAULT 0'
|
|
}
|
|
|
|
for col_name, col_type in gpx_columns.items():
|
|
if col_name not in columns:
|
|
db.session.execute(text(f'ALTER TABLE gpx_files ADD COLUMN {col_name} {col_type}'))
|
|
db.session.commit()
|
|
print(f'✓ Added {col_name} column to gpx_files table')
|
|
|
|
# 5. Verify all required tables exist
|
|
required_tables = [
|
|
'users', 'posts', 'post_images', 'gpx_files',
|
|
'comments', 'likes', 'page_views', 'chat_rooms',
|
|
'chat_messages', 'map_routes', 'password_reset_requests'
|
|
]
|
|
|
|
existing_tables = inspector.get_table_names()
|
|
missing_tables = [t for t in required_tables if t not in existing_tables]
|
|
|
|
if missing_tables:
|
|
print(f'\n✗ WARNING: Missing tables: {missing_tables}')
|
|
print(' Running init-db to create missing tables...')
|
|
db.create_all()
|
|
print('✓ All missing tables created')
|
|
|
|
print('\n✓ Database migration completed successfully')
|
|
print(f'✓ Total tables: {len(existing_tables)}')
|
|
|
|
except Exception as e:
|
|
print(f'✗ Migration error: {e}')
|
|
db.session.rollback()
|
|
raise
|
|
print(f'Migration error: {e}')
|
|
db.session.rollback()
|
|
|
|
@app.cli.command()
|
|
def process_gpx_files():
|
|
"""Process existing GPX files to extract statistics."""
|
|
from app.utils.gpx_processor import process_gpx_file
|
|
|
|
gpx_files = GPXFile.query.all()
|
|
processed = 0
|
|
|
|
for gpx_file in gpx_files:
|
|
try:
|
|
if process_gpx_file(gpx_file):
|
|
processed += 1
|
|
print(f'Processed: {gpx_file.original_name}')
|
|
else:
|
|
print(f'Failed to process: {gpx_file.original_name}')
|
|
except Exception as e:
|
|
print(f'Error processing {gpx_file.original_name}: {e}')
|
|
|
|
db.session.commit()
|
|
print(f'Processed {processed}/{len(gpx_files)} GPX files')
|
|
|
|
@app.cli.command()
|
|
def set_cover_images():
|
|
"""Set cover images for posts that don't have them."""
|
|
posts = Post.query.all()
|
|
updated = 0
|
|
|
|
for post in posts:
|
|
# Check if post has a cover image
|
|
cover_image = post.images.filter_by(is_cover=True).first()
|
|
if not cover_image:
|
|
# Set the first image as cover if available
|
|
first_image = post.images.first()
|
|
if first_image:
|
|
first_image.is_cover = True
|
|
updated += 1
|
|
print(f'Set cover image for post: {post.title}')
|
|
|
|
db.session.commit()
|
|
print(f'Updated {updated} posts with cover images')
|
|
|
|
@app.cli.command()
|
|
def create_admin():
|
|
"""Create an admin user."""
|
|
admin_email = os.environ.get('ADMIN_EMAIL', 'admin@moto-adv.com')
|
|
admin_password = os.environ.get('ADMIN_PASSWORD', 'admin123')
|
|
|
|
admin = User.query.filter_by(email=admin_email).first()
|
|
if admin:
|
|
print(f'Admin user {admin_email} already exists.')
|
|
return
|
|
|
|
admin = User(
|
|
nickname='admin',
|
|
email=admin_email,
|
|
is_admin=True
|
|
)
|
|
admin.set_password(admin_password)
|
|
|
|
db.session.add(admin)
|
|
db.session.commit()
|
|
print(f'Admin user created: {admin_email}')
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=5000, debug=True)
|