from uuid import UUID as _UUID from werkzeug.exceptions import NotFound from flask import ( Blueprint, render_template, abort, redirect, url_for, request, flash ) from flask_login import login_required, current_user from app import db from .models import GrowLog from .forms import GrowLogForm from plugins.plant.models import Plant, PlantCommonName bp = Blueprint( 'growlog', __name__, url_prefix='/growlogs', template_folder='templates', ) def _get_plant_by_uuid(uuid_val): """ uuid_val may already be a uuid.UUID (from a route converter) or a string (from form POST). Normalize & validate it, then lookup. """ # 1) If Flask route gave us a UUID instance, just stringify it if isinstance(uuid_val, _UUID): val = str(uuid_val) else: # 2) Otherwise try to parse it as a hex string try: val = str(_UUID(uuid_val)) except (ValueError, TypeError): # invalid format → 404 abort(404) # 3) Only return plants owned by current_user return ( Plant.query .filter_by(uuid=val, owner_id=current_user.id) .first_or_404() ) def _user_plant_choices(): # join to the common‐name table and sort by its name plants = ( Plant.query .filter_by(owner_id=current_user.id) .join(PlantCommonName, Plant.common_id == PlantCommonName.id) .order_by(PlantCommonName.name) .all() ) return [ (p.uuid, f"{p.common_name.name} – {p.uuid}") for p in plants ] @bp.route('/add', methods=['GET','POST']) @bp.route('//add', methods=['GET','POST']) @login_required def add_log(plant_uuid=None): form = GrowLogForm() # 1) always populate the dropdown behind the scenes form.plant_uuid.choices = _user_plant_choices() plant = None hide_select = False # 2) if URL had a plant_uuid, load & pre-select it, hide dropdown if plant_uuid: plant = _get_plant_by_uuid(plant_uuid) form.plant_uuid.data = str(plant_uuid) hide_select = True if form.validate_on_submit(): # 3) on POST, resolve via form.plant_uuid plant = _get_plant_by_uuid(form.plant_uuid.data) log = GrowLog( plant_id = plant.id, event_type = form.event_type.data, title = form.title.data, notes = form.notes.data, is_public = form.is_public.data, ) db.session.add(log) db.session.commit() flash('Grow log added.', 'success') return redirect( url_for('growlog.list_logs', plant_uuid=plant.uuid) ) return render_template( 'growlog/log_form.html', form = form, plant = plant, hide_plant_select = hide_select ) @bp.route('/', defaults={'plant_uuid': None}) @bp.route('/') @login_required def list_logs(plant_uuid): # how many to show? limit = request.args.get('limit', default=10, type=int) if plant_uuid: # logs for a single plant plant = _get_plant_by_uuid(plant_uuid) query = GrowLog.query.filter_by(plant_id=plant.id) else: # logs for all your plants plant = None query = ( GrowLog.query .join(Plant, GrowLog.plant_id == Plant.id) .filter(Plant.owner_id == current_user.id) ) logs = ( query .order_by(GrowLog.created_at.desc()) .limit(limit) .all() ) return render_template( 'growlog/log_list.html', plant=plant, logs=logs, limit=limit ) @bp.route('//edit/', methods=['GET', 'POST']) @login_required def edit_log(plant_uuid, log_id): plant = _get_plant_by_uuid(plant_uuid) log = GrowLog.query.filter_by(id=log_id, plant_id=plant.id).first_or_404() form = GrowLogForm(obj=log) # Lock the dropdown to this one plant form.plant_uuid.choices = [(plant.uuid, plant.common_name.name)] form.plant_uuid.data = plant.uuid if form.validate_on_submit(): log.event_type = form.event_type.data log.title = form.title.data log.notes = form.notes.data log.is_public = form.is_public.data db.session.commit() flash('Grow log updated.', 'success') return redirect(url_for('growlog.list_logs', plant_uuid=plant_uuid)) return render_template( 'growlog/log_form.html', form=form, plant_uuid=plant_uuid, plant=plant, log=log ) @bp.route('//delete/', methods=['POST']) @login_required def delete_log(plant_uuid, log_id): plant = _get_plant_by_uuid(plant_uuid) log = GrowLog.query.filter_by(id=log_id, plant_id=plant.id).first_or_404() db.session.delete(log) db.session.commit() flash('Grow log deleted.', 'warning') return redirect(url_for('growlog.list_logs', plant_uuid=plant_uuid))