from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, jsonify from flask_login import login_required, current_user from app.models import Post, PostImage, GPXFile, User, Comment, Like, db from app.forms import PostForm, CommentForm from werkzeug.utils import secure_filename from werkzeug.exceptions import RequestEntityTooLarge import os import uuid from datetime import datetime from PIL import Image import gpxpy community = Blueprint('community', __name__) @community.route('/') def index(): """Community posts listing page""" page = request.args.get('page', 1, type=int) posts = Post.query.filter_by(published=True).order_by(Post.created_at.desc()).paginate( page=page, per_page=10, error_out=False ) return render_template('community/index.html', posts=posts) @community.route('/post/') def post_detail(id): """Individual post detail page""" post = Post.query.get_or_404(id) if not post.published and (not current_user.is_authenticated or (current_user.id != post.author_id and not current_user.is_admin)): flash('Post not found.', 'error') return redirect(url_for('community.index')) form = CommentForm() comments = Comment.query.filter_by(post_id=id).order_by(Comment.created_at.asc()).all() return render_template('community/post_detail.html', post=post, form=form, comments=comments) @community.route('/new-post', methods=['GET', 'POST']) @login_required def new_post(): """Create new post page""" form = PostForm() if form.validate_on_submit(): try: # Create post post = Post( title=form.title.data, subtitle=form.subtitle.data, content=form.content.data, difficulty=int(form.difficulty.data), published=form.published.data, author_id=current_user.id ) db.session.add(post) db.session.flush() # Get the post ID # Handle image uploads if form.images.data and form.images.data.filename: images = request.files.getlist('images') for image_file in images: if image_file and image_file.filename: result = save_image(image_file, post.id) if result['success']: post_image = PostImage( filename=result['filename'], original_name=image_file.filename, size=result['size'], mime_type=image_file.content_type, post_id=post.id ) db.session.add(post_image) # Handle GPX file upload if form.gpx_file.data and form.gpx_file.data.filename: result = save_gpx_file(form.gpx_file.data, post.id) if result['success']: gpx_file = GPXFile( filename=result['filename'], original_name=form.gpx_file.data.filename, size=result['size'], post_id=post.id ) db.session.add(gpx_file) db.session.commit() flash('Your adventure has been shared!', 'success') return redirect(url_for('community.post_detail', id=post.id)) except Exception as e: db.session.rollback() flash('An error occurred while creating your post. Please try again.', 'error') current_app.logger.error(f'Error creating post: {str(e)}') return render_template('community/new_post.html', form=form) @community.route('/post//comment', methods=['POST']) @login_required def add_comment(id): """Add comment to post""" post = Post.query.get_or_404(id) form = CommentForm() if form.validate_on_submit(): comment = Comment( content=form.content.data, author_id=current_user.id, post_id=post.id ) db.session.add(comment) db.session.commit() flash('Your comment has been added.', 'success') return redirect(url_for('community.post_detail', id=id)) @community.route('/post//like', methods=['POST']) @login_required def toggle_like(id): """Toggle like on post""" post = Post.query.get_or_404(id) existing_like = Like.query.filter_by(user_id=current_user.id, post_id=post.id).first() if existing_like: db.session.delete(existing_like) liked = False else: like = Like(user_id=current_user.id, post_id=post.id) db.session.add(like) liked = True db.session.commit() return jsonify({'liked': liked, 'count': post.get_like_count()}) def save_image(image_file, post_id): """Save uploaded image file""" try: # Create upload directory upload_dir = os.path.join(current_app.instance_path, 'uploads', 'images') os.makedirs(upload_dir, exist_ok=True) # Generate unique filename filename = secure_filename(f"{uuid.uuid4().hex}_{image_file.filename}") filepath = os.path.join(upload_dir, filename) # Save and resize image image = Image.open(image_file) if image.mode in ('RGBA', 'LA', 'P'): image = image.convert('RGB') # Resize if too large max_size = (1920, 1080) image.thumbnail(max_size, Image.Resampling.LANCZOS) image.save(filepath, 'JPEG', quality=85, optimize=True) file_size = os.path.getsize(filepath) return { 'success': True, 'filename': filename, 'size': file_size } except Exception as e: current_app.logger.error(f'Error saving image: {str(e)}') return {'success': False, 'error': str(e)} def save_gpx_file(gpx_file, post_id): """Save uploaded GPX file""" try: # Create upload directory upload_dir = os.path.join(current_app.instance_path, 'uploads', 'gpx') os.makedirs(upload_dir, exist_ok=True) # Generate unique filename filename = secure_filename(f"{uuid.uuid4().hex}_{gpx_file.filename}") filepath = os.path.join(upload_dir, filename) # Validate GPX file gpx_content = gpx_file.read() gpx = gpxpy.parse(gpx_content.decode('utf-8')) # Save file with open(filepath, 'wb') as f: f.write(gpx_content) file_size = len(gpx_content) return { 'success': True, 'filename': filename, 'size': file_size } except Exception as e: current_app.logger.error(f'Error saving GPX file: {str(e)}') return {'success': False, 'error': str(e)}