import csv import io from flask import Blueprint, request, render_template, redirect, flash from werkzeug.utils import secure_filename from app.neo4j_utils import get_neo4j_handler from plugins.plant.models import db, Plant, PlantCommon, PlantScientific bp = Blueprint("importer", __name__, template_folder="templates") @bp.route("/import/", methods=["GET", "POST"]) def upload(): if request.method == "POST": file = request.files.get("file") if not file: flash("No file uploaded.", "error") return redirect(request.url) try: # Handle UTF-8 BOM (Byte Order Mark) if present decoded = file.read().decode("utf-8-sig") stream = io.StringIO(decoded) reader = csv.DictReader(stream) neo = get_neo4j_handler() for row in reader: uuid = row.get("uuid") name = row.get("name") sci_name = row.get("scientific_name") plant_type = row.get("plant_type", "plant") mother_uuid = row.get("mother_uuid") if not all([uuid, name, sci_name]): continue # skip incomplete rows # Common Name common = PlantCommon.query.filter_by(name=name).first() if not common: common = PlantCommon(name=name) db.session.add(common) db.session.flush() # Scientific Name scientific = PlantScientific.query.filter_by(name=sci_name).first() if not scientific: scientific = PlantScientific(name=sci_name, common_id=common.id) db.session.add(scientific) db.session.flush() # Plant plant = Plant.query.filter_by(uuid=uuid).first() if not plant: plant = Plant( uuid=uuid, common_id=common.id, scientific_id=scientific.id, plant_type=plant_type ) db.session.add(plant) # Neo4j neo.create_plant_node(uuid, name) if mother_uuid and mother_uuid.strip(): neo.create_plant_node(mother_uuid.strip(), "Parent") neo.create_lineage(uuid, mother_uuid.strip()) db.session.commit() neo.close() flash("CSV imported successfully with lineage.", "success") except Exception as e: flash(f"Import failed: {str(e)}", "error") return redirect(request.url) return render_template("importer/upload.html")