lots of things

This commit is contained in:
2025-06-06 22:02:44 -05:00
parent 9daee50a3a
commit 96c634897b
30 changed files with 1120 additions and 182 deletions

View File

@ -1,78 +1,144 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, send_from_directory, current_app
from flask_login import current_user, login_required
from werkzeug.utils import secure_filename
import os
# plugins/submission/routes.py
from flask import (
Blueprint,
render_template,
request,
redirect,
url_for,
flash,
jsonify
)
from flask_login import login_required, current_user
from app import db
from .models import SubmissionImage, ImageHeart, FeaturedImage
from plugins.plant.models import Plant
from .models import Submission, SubmissionImage
from .forms import SubmissionForm
from datetime import datetime
import os
from werkzeug.utils import secure_filename
from plugins.media.utils import generate_random_filename, strip_metadata_and_save
bp = Blueprint("submission", __name__, template_folder="templates")
bp = Blueprint("submission", __name__, template_folder="templates", url_prefix="/submission")
UPLOAD_FOLDER = "static/uploads"
# We store only "YYYY/MM/DD/<uuid>.ext" in SubmissionImage.file_url.
# All files live under "/app/static/uploads/YYYY/MM/DD/<uuid>.ext" in the container.
BASE_UPLOAD_FOLDER = "static/uploads"
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif"}
def allowed_file(filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
return (
"." in filename
and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
)
@bp.route("/submissions/upload", methods=["GET", "POST"])
@bp.route("/", methods=["GET"])
@login_required
def upload_submissions():
if request.method == "POST":
file = request.files.get("image")
caption = request.form.get("caption")
plant_id = request.form.get("plant_id")
def submission_index():
return redirect(url_for("submission.new_submission"))
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
save_path = os.path.join(current_app.root_path, UPLOAD_FOLDER)
os.makedirs(save_path, exist_ok=True)
file.save(os.path.join(save_path, filename))
submissions = SubmissionImage(file_url=f"{UPLOAD_FOLDER}/{filename}", caption=caption, plant_id=plant_id)
db.session.add(submissions)
db.session.commit()
flash("Image uploaded successfully.", "success")
return redirect(url_for("submissions.upload_submissions"))
else:
flash("Invalid file or no file uploaded.", "danger")
return render_template("submissions/upload.html")
@bp.route("/submissions/files/<path:filename>")
def submissions_file(filename):
return send_from_directory(os.path.join(current_app.root_path, "static/uploads"), filename)
@bp.route("/submissions/heart/<int:image_id>", methods=["POST"])
@bp.route("/new", methods=["GET", "POST"])
@bp.route("/new/", methods=["GET", "POST"])
@login_required
def toggle_heart(image_id):
existing = ImageHeart.query.filter_by(user_id=current_user.id, submission_image_id=image_id).first()
if existing:
db.session.delete(existing)
def new_submission():
form = SubmissionForm()
if form.validate_on_submit():
plant_types = {"market_price", "name_correction", "new_plant", "mutation"}
t = form.submission_type.data
# Only require plant_name if the type is plantrelated
if t in plant_types and not form.plant_name.data.strip():
flash("Common Name is required for this submission type.", "danger")
return render_template("submission/new.html", form=form)
submission = Submission(
user_id=current_user.id,
submitted_at=datetime.utcnow(),
submission_type=t,
plant_name=form.plant_name.data,
scientific_name=form.scientific_name.data,
notes=form.notes.data,
price=form.price.data if form.price.data else None,
source=form.source.data,
vendor_name=form.vendor_name.data,
rating=form.rating.data,
old_vendor=form.old_vendor.data,
new_vendor=form.new_vendor.data,
alias_reason=form.alias_reason.data,
approved=None
)
db.session.add(submission)
db.session.flush()
# date subfolder: "YYYY/MM/DD"
today = datetime.utcnow().strftime("%Y/%m/%d")
# Write into "/app/static/uploads/YYYY/MM/DD", not "/app/app/static/uploads..."
save_dir = os.path.join(os.getcwd(), BASE_UPLOAD_FOLDER, today)
os.makedirs(save_dir, exist_ok=True)
files = request.files.getlist("images")
for f in files:
if f and allowed_file(f.filename):
orig_name = secure_filename(f.filename)
rand_name = generate_random_filename(orig_name)
# Temporarily save under "/app/temp_<uuid>.ext"
temp_path = os.path.join(os.getcwd(), "temp_" + rand_name)
f.save(temp_path)
final_path = os.path.join(save_dir, rand_name)
strip_metadata_and_save(temp_path, final_path)
os.remove(temp_path)
# Store only "YYYY/MM/DD/<uuid>.ext"
rel_url = f"{today}/{rand_name}"
img = SubmissionImage(
submission_id=submission.id,
file_url=rel_url,
uploaded_at=datetime.utcnow()
)
db.session.add(img)
db.session.commit()
return jsonify({"status": "unhearted"})
else:
heart = ImageHeart(user_id=current_user.id, submission_image_id=image_id)
db.session.add(heart)
db.session.commit()
return jsonify({"status": "hearted"})
flash("Submission received. Thank you!", "success")
return redirect(url_for("submission.new_submission"))
@bp.route("/submissions/feature/<int:image_id>", methods=["POST"])
return render_template("submission/new.html", form=form)
@bp.route("/list", methods=["GET"])
@bp.route("/list/", methods=["GET"])
@login_required
def set_featured_image(image_id):
image = SubmissionImage.query.get_or_404(image_id)
plant = image.plant
if not plant:
flash("This image is not linked to a plant.", "danger")
return redirect(request.referrer or url_for("core_ui.home"))
def list_submissions():
selected_type = request.args.get("type", None)
query = Submission.query.filter_by(user_id=current_user.id)
if selected_type:
query = query.filter_by(submission_type=selected_type)
subs = query.order_by(Submission.submitted_at.desc()).all()
if current_user.id != plant.owner_id and current_user.role != "admin":
flash("Not authorized to set featured image.", "danger")
return redirect(request.referrer or url_for("core_ui.home"))
all_types = [
("", "All"),
("market_price", "Market Price"),
("name_correction", "Name Correction"),
("new_plant", "New Plant Suggestion"),
("mutation", "Mutation Discovery"),
("vendor_rating", "Vendor Rating/Review"),
("vendor_alias", "Vendor Alias Submission"),
]
return render_template(
"submission/list.html",
submissions=subs,
selected_type=selected_type,
all_types=all_types
)
FeaturedImage.query.filter_by(submission_image_id=image_id).delete()
featured = FeaturedImage(submission_image_id=image_id, is_featured=True)
db.session.add(featured)
db.session.commit()
flash("Image set as featured.", "success")
return redirect(request.referrer or url_for("core_ui.home"))
@bp.route("/view/<int:submission_id>", methods=["GET"])
@bp.route("/view/<int:submission_id>/", methods=["GET"])
@login_required
def view_submission(submission_id):
sub = Submission.query.get_or_404(submission_id)
if sub.user_id != current_user.id and current_user.role != "admin":
flash("Not authorized to view this submission.", "danger")
return redirect(url_for("submission.list_submissions"))
images = SubmissionImage.query.filter_by(submission_id=sub.id).all()
return render_template("submission/view.html", submission=sub, images=images)