from uuid import uuid4 import os from sqlalchemy.orm import joinedload from flask import ( Blueprint, render_template, redirect, url_for, request, flash, current_app, ) from flask_login import login_required, current_user from app import db from .models import Plant, PlantCommonName, PlantScientificName from .forms import PlantForm from plugins.media.models import Media from plugins.media.routes import ( save_media_file, delete_media_file, rotate_media_file, generate_image_url, ) bp = Blueprint( 'plant', __name__, url_prefix='/plants', template_folder='templates', ) @bp.app_context_processor def inject_image_helper(): return dict(generate_image_url=generate_image_url) @bp.route('/', methods=['GET']) @login_required def index(): plants = ( Plant.query .options(joinedload(Plant.media_items)) .filter_by(owner_id=current_user.id) .order_by(Plant.id.desc()) .all() ) user_plants_count = Plant.query.filter_by(owner_id=current_user.id).count() user_images_count = Media.query.filter_by(uploader_id=current_user.id).count() total_plants_count = Plant.query.count() total_images_count = Media.query.count() plant_types = [ pt[0] for pt in ( db.session.query(Plant.plant_type) .filter_by(owner_id=current_user.id) .distinct() .all() ) ] stats = { 'user_plants': user_plants_count, 'user_images': user_images_count, 'total_plants': total_plants_count, 'total_images': total_images_count, } return render_template( 'plant/index.html', plants=plants, plant_types=plant_types, stats=stats, ) @bp.route('/create', methods=['GET', 'POST']) @login_required def create(): form = PlantForm() form.plant_type.choices = [ ('plant', 'Plant'), ('cutting', 'Cutting'), ('seed', 'Seed'), ('tissue_culture', 'Tissue Culture'), ('division', 'Division'), ] form.common_name.choices = [ (c.id, c.name) for c in PlantCommonName.query.order_by(PlantCommonName.name).all() ] form.scientific_name.choices = [ (s.id, s.name) for s in PlantScientificName.query.order_by(PlantScientificName.name).all() ] form.mother_uuid.choices = [('N/A', 'None')] + [ ( p.uuid, f"{p.common_name.name if p.common_name else 'Unnamed'} – {p.uuid}" ) for p in Plant.query.order_by(Plant.created_at.desc()).all() ] if form.validate_on_submit(): new_plant = Plant( uuid=str(uuid4()), owner_id=current_user.id, plant_type=form.plant_type.data, common_id=form.common_name.data, scientific_id=form.scientific_name.data, mother_uuid=( form.mother_uuid.data if form.mother_uuid.data != 'N/A' else None ), custom_slug=(form.custom_slug.data.strip() or None), vendor_name=(form.vendor_name.data.strip() or None), price=(form.price.data or None), notes=form.notes.data, data_verified=form.data_verified.data, is_active=form.is_active.data, ) db.session.add(new_plant) db.session.commit() flash('New plant created successfully.', 'success') return redirect(url_for('plant.edit', uuid_val=new_plant.uuid)) return render_template('plant/create.html', form=form) @bp.route('/', methods=['GET']) @login_required def detail(uuid_val): plant = Plant.query.filter_by( uuid=str(uuid_val), owner_id=current_user.id, ).first_or_404() return render_template('plant/detail.html', plant=plant) @bp.route('//edit', methods=['GET', 'POST']) @login_required def edit(uuid_val): plant = Plant.query.filter_by( uuid=str(uuid_val), owner_id=current_user.id, ).first_or_404() form = PlantForm() form.plant_type.choices = [ ('plant', 'Plant'), ('cutting', 'Cutting'), ('seed', 'Seed'), ('tissue_culture', 'Tissue Culture'), ('division', 'Division'), ] form.common_name.choices = [ (c.id, c.name) for c in PlantCommonName.query.order_by(PlantCommonName.name).all() ] form.scientific_name.choices = [ (s.id, s.name) for s in PlantScientificName.query.order_by(PlantScientificName.name).all() ] form.mother_uuid.choices = [('N/A', 'None')] + [ ( p.uuid, f"{p.common_name.name if p.common_name else 'Unnamed'} – {p.uuid}" ) for p in Plant.query.filter(Plant.uuid != plant.uuid).all() ] if request.method == 'GET': form.plant_type.data = plant.plant_type form.common_name.data = plant.common_id form.scientific_name.data = plant.scientific_id form.mother_uuid.data = plant.mother_uuid or 'N/A' form.custom_slug.data = plant.custom_slug or '' form.vendor_name.data = plant.vendor_name or '' form.price.data = plant.price or None form.notes.data = plant.notes form.data_verified.data = plant.data_verified form.is_active.data = getattr(plant, 'is_active', True) if form.validate_on_submit(): plant.plant_type = form.plant_type.data plant.common_id = form.common_name.data plant.scientific_id = form.scientific_name.data plant.mother_uuid = ( form.mother_uuid.data if form.mother_uuid.data != 'N/A' else None ) plant.custom_slug = (form.custom_slug.data.strip() or None) plant.vendor_name = (form.vendor_name.data.strip() or None) plant.price = (form.price.data or None) plant.notes = form.notes.data plant.data_verified = form.data_verified.data plant.is_active = form.is_active.data # — patch to save whichever radio was checked — featured_id = request.form.get("featured_media_id") if featured_id and featured_id.isdigit(): plant.featured_media_id = int(featured_id) db.session.commit() flash('Plant updated successfully.', 'success') return redirect(url_for('plant.detail', uuid_val=plant.uuid)) return render_template('plant/edit.html', form=form, plant=plant) @bp.route('//upload', methods=['POST']) @login_required def upload_image(uuid_val): plant = Plant.query.filter_by(uuid=str(uuid_val)).first_or_404() file = request.files.get('file') if file and file.filename: save_media_file( file, current_user.id, related_model='plant', related_uuid=str(plant.uuid), ) flash('Image uploaded successfully.', 'success') return redirect(url_for('plant.edit', uuid_val=plant.uuid)) @bp.route("/feature/", methods=["POST"]) @login_required def set_featured_image(media_id): media = Media.query.get_or_404(media_id) if current_user.id != media.uploader_id and current_user.role != "admin": return jsonify({"error": "Not authorized"}), 403 plant = media.plant plant.featured_media_id = media.id db.session.commit() return jsonify({"status": "success", "media_id": media.id}) @bp.route('//delete/', methods=['POST']) @login_required def delete_image(uuid_val, media_id): plant = Plant.query.filter_by(uuid=str(uuid_val)).first_or_404() media = Media.query.get_or_404(media_id) delete_media_file(media) flash('Image deleted.', 'success') return redirect(url_for('plant.edit', uuid_val=plant.uuid)) @bp.route('//rotate/', methods=['POST']) @login_required def rotate_image(uuid_val, media_id): plant = Plant.query.filter_by(uuid=str(uuid_val)).first_or_404() media = Media.query.get_or_404(media_id) rotate_media_file(media) flash('Image rotated.', 'success') return redirect(url_for('plant.edit', uuid_val=plant.uuid))