broke currently

This commit is contained in:
2025-06-22 16:11:29 -05:00
parent e7a0f5b1be
commit 2bb7a29141
77 changed files with 1748 additions and 2298 deletions

View File

@ -1,6 +1,5 @@
# plugins/plant/routes.py
from uuid import uuid4
import os
from flask import (
Blueprint,
render_template,
@ -15,75 +14,68 @@ from app import db
from .models import Plant, PlantCommonName, PlantScientificName
from .forms import PlantForm
from plugins.media.models import Media
from plugins.media.utils import (
from plugins.media.routes import (
save_media_file,
delete_media_file,
rotate_media_file,
generate_image_url
generate_image_url,
)
bp = Blueprint(
'plant',
__name__,
url_prefix='/plants',
template_folder='templates'
template_folder='templates',
)
# Make generate_image_url available in all plant templates
@bp.app_context_processor
def inject_image_helper():
return dict(generate_image_url=generate_image_url)
# ─── LIST ─────────────────────────────────────────────────────────────────────
@bp.route('/', methods=['GET'])
@login_required
def index():
# 1. Compute per-type stats
total = Plant.query.filter_by(owner_id=current_user.id).count()
raw_types = (
db.session
.query(Plant.plant_type)
.filter_by(owner_id=current_user.id)
.distinct()
.all()
)
plant_types = [pt[0] for pt in raw_types]
stats = {'All': total}
for pt in plant_types:
stats[pt] = Plant.query.filter_by(
owner_id=current_user.id,
plant_type=pt
).count()
# 2. Load ALL this users plants, ordered by common name
plants = (
Plant.query
.filter_by(owner_id=current_user.id)
.join(PlantCommonName, Plant.common_id == PlantCommonName.id)
.order_by(PlantCommonName.name)
.options(
db.joinedload(Plant.media),
db.joinedload(Plant.common_name)
)
.all()
Plant.query.filter_by(owner_id=current_user.id)
.order_by(Plant.id.desc())
.all()
)
# 3. Render the template (JS will handle filtering & pagination)
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,
plant_types=plant_types
)
# ─── CREATE ───────────────────────────────────────────────────────────────────
@bp.route('/create', methods=['GET', 'POST'])
@login_required
def create():
form = PlantForm()
# ─── dropdown choices ───────────────────────────────────────────────────────
form.plant_type.choices = [
('plant', 'Plant'),
('cutting', 'Cutting'),
@ -93,14 +85,17 @@ def create():
]
form.common_name.choices = [
(c.id, c.name)
for c in PlantCommonName.query.order_by(PlantCommonName.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)
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}")
(
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()
]
@ -116,8 +111,9 @@ def create():
if form.mother_uuid.data != 'N/A'
else None
),
# ← HERE: convert blank slug to NULL
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,
@ -129,21 +125,25 @@ def create():
return render_template('plant/create.html', form=form)
# ─── DETAIL ───────────────────────────────────────────────────────────────────
@bp.route('/<uuid:uuid_val>', methods=['GET'])
@login_required
def detail(uuid_val):
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
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)
# ─── EDIT ─────────────────────────────────────────────────────────────────────
@bp.route('/<uuid:uuid_val>/edit', methods=['GET', 'POST'])
@login_required
def edit(uuid_val):
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
plant = Plant.query.filter_by(
uuid=str(uuid_val),
owner_id=current_user.id,
).first_or_404()
form = PlantForm()
# Populate dropdowns (same as in create)
form.plant_type.choices = [
('plant', 'Plant'),
('cutting', 'Cutting'),
@ -153,14 +153,17 @@ def edit(uuid_val):
]
form.common_name.choices = [
(c.id, c.name)
for c in PlantCommonName.query.order_by(PlantCommonName.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)
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}")
(
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()
]
@ -170,6 +173,8 @@ def edit(uuid_val):
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)
@ -183,47 +188,55 @@ def edit(uuid_val):
if form.mother_uuid.data != 'N/A'
else None
)
# ← HERE as well
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)
# ─── IMAGE ROUTES ────────────────────────────────────────────────────────────
@bp.route('/<uuid:uuid_val>/upload', methods=['POST'])
@login_required
def upload_image(uuid_val):
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
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)
related_uuid=str(plant.uuid),
)
flash('Image uploaded successfully.', 'success')
return redirect(url_for('plant.edit', uuid_val=plant.uuid))
@bp.route('/<uuid:uuid_val>/feature/<int:media_id>', methods=['POST'])
@bp.route("/feature/<int:media_id>", methods=["POST"])
@login_required
def set_featured_image(uuid_val, media_id):
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
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()
flash('Featured image set.', 'success')
return redirect(url_for('plant.edit', uuid_val=plant.uuid))
return jsonify({"status": "success", "media_id": media.id})
@bp.route('/<uuid:uuid_val>/delete/<int:media_id>', methods=['POST'])
@login_required
def delete_image(uuid_val, media_id):
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
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')
@ -232,7 +245,7 @@ def delete_image(uuid_val, media_id):
@bp.route('/<uuid:uuid_val>/rotate/<int:media_id>', methods=['POST'])
@login_required
def rotate_image(uuid_val, media_id):
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
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')