# plugins/utility/search/routes.py from flask import Blueprint, render_template, request, jsonify from flask_login import login_required, current_user from app import db from sqlalchemy import or_ from plugins.plant.models import Plant, PlantCommonName, PlantScientificName, Tag from flask_wtf import FlaskForm from wtforms import StringField, SelectMultipleField, SubmitField from wtforms.validators import Optional, Length, Regexp bp = Blueprint( 'search', __name__, url_prefix='/search', template_folder='templates/search' ) class SearchForm(FlaskForm): query = StringField( 'Search', validators=[ Optional(), Length(min=2, max=100, message="Search term must be between 2 and 100 characters."), Regexp(r'^[\w\s\-]+$', message="Search can only include letters, numbers, spaces, and dashes.") ] ) tags = SelectMultipleField('Tags', coerce=int) submit = SubmitField('Search') @bp.route('', methods=['GET', 'POST']) @login_required def search(): form = SearchForm() # populate tag choices form.tags.choices = [(t.id, t.name) for t in Tag.query.order_by(Tag.name).all()] results = [] if form.validate_on_submit(): q = form.query.data or '' db_query = db.session.query(Plant).join(PlantScientific).join(PlantCommon) if q: like_term = f"%{q}%" db_query = db_query.filter( or_( Plant.common_name.ilike(like_term), Plant.scientific_name.ilike(like_term), Plant.current_status.ilike(like_term) ) ) if form.tags.data: db_query = db_query.filter(Plant.tags.any(Tag.id.in_(form.tags.data))) db_query = db_query.filter(Plant.owner_id == current_user.id) results = db_query.all() return render_template('search/search.html', form=form, results=results) @bp.route('/tags') @login_required def search_tags(): term = request.args.get('term', '') matches = Tag.query.filter(Tag.name.ilike(f"%{term}%")).limit(10).all() return jsonify([t.name for t in matches])