From f71cb7d24a9c4b0c81066feeaa111c766aa1ca60 Mon Sep 17 00:00:00 2001 From: Kobe Date: Sun, 8 Jun 2025 17:47:49 +0200 Subject: [PATCH] csv upload --- app.py | 156 ++++++++++++++++++++++++++++++++++- templates/manage_plants.html | 13 ++- 2 files changed, 167 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index b4bfd14..3e6e751 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify +from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, Response from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from datetime import datetime @@ -7,6 +7,8 @@ from werkzeug.utils import secure_filename import os from config import Config import random +import csv +import io app = Flask(__name__) app.config.from_object(Config) @@ -1453,6 +1455,158 @@ def recognize_plant(): random_plant = random.choice(plants) return jsonify({'redirect': url_for('plant', plant_id=random_plant.id)}) +@app.route('/admin/plants/upload-csv', methods=['POST']) +def upload_plants_csv(): + if not is_logged_in(): + return redirect(url_for('login')) + + if 'csv_file' not in request.files: + flash('No file uploaded', 'error') + return redirect(url_for('manage_plants')) + + file = request.files['csv_file'] + if file.filename == '': + flash('No file selected', 'error') + return redirect(url_for('manage_plants')) + + if not file.filename.endswith('.csv'): + flash('Please upload a CSV file', 'error') + return redirect(url_for('manage_plants')) + + try: + # Read the CSV file + stream = io.StringIO(file.stream.read().decode("UTF8"), newline=None) + csv_reader = csv.DictReader(stream) + + # Get all the lookup dictionaries + climates = {c.name.lower(): c.id for c in Climate.query.all()} + environments = {e.name.lower(): e.id for e in Environment.query.all()} + lights = {l.name.lower(): l.id for l in Light.query.all()} + toxicities = {t.name.lower(): t.id for t in Toxicity.query.all()} + sizes = {s.name.lower(): s.id for s in Size.query.all()} + care_difficulties = {d.name.lower(): d.id for d in CareDifficulty.query.all()} + growth_rates = {r.name.lower(): r.id for r in GrowthRate.query.all()} + products = {p.name.lower(): p.id for p in Product.query.all()} + + success_count = 0 + error_count = 0 + duplicate_count = 0 + + for row in csv_reader: + try: + # Check if plant with this name already exists + existing_plant = Plant.query.filter_by(name=row['name']).first() + if existing_plant: + duplicate_count += 1 + continue + + # Create new plant + plant = Plant( + name=row['name'], + description=row.get('description', ''), + care_guide=row.get('care_guide', ''), + date_added=datetime.utcnow() + ) + + # Set relationships using the lookup dictionaries + if row.get('climate'): + plant.climate_id = climates.get(row['climate'].lower()) + if row.get('environment'): + plant.environment_id = environments.get(row['environment'].lower()) + if row.get('light'): + plant.light_id = lights.get(row['light'].lower()) + if row.get('toxicity'): + plant.toxicity_id = toxicities.get(row['toxicity'].lower()) + if row.get('size'): + plant.size_id = sizes.get(row['size'].lower()) + if row.get('care_difficulty'): + plant.care_difficulty_id = care_difficulties.get(row['care_difficulty'].lower()) + if row.get('growth_rate'): + plant.growth_rate_id = growth_rates.get(row['growth_rate'].lower()) + + # Handle products (comma-separated list) + if row.get('products'): + product_ids = [] + for product_name in row['products'].split(','): + product_name = product_name.strip().lower() + if product_name in products: + product_ids.append(str(products[product_name])) + plant.products = ','.join(product_ids) + + db.session.add(plant) + success_count += 1 + + except Exception as e: + error_count += 1 + print(f"Error processing row: {row}, Error: {str(e)}") + continue + + db.session.commit() + message = f'Successfully imported {success_count} plants.' + if duplicate_count > 0: + message += f' {duplicate_count} duplicate plants were skipped.' + if error_count > 0: + message += f' {error_count} errors occurred.' + flash(message, 'success') + + except Exception as e: + flash(f'Error processing CSV file: {str(e)}', 'error') + db.session.rollback() + + return redirect(url_for('manage_plants')) + +@app.route('/admin/plants/download-template') +def download_plants_template(): + if not is_logged_in(): + return redirect(url_for('login')) + + # Create a CSV template with headers and example data + output = io.StringIO() + writer = csv.writer(output) + + # Write headers + headers = [ + 'name', + 'description', + 'care_guide', + 'climate', + 'environment', + 'light', + 'toxicity', + 'size', + 'care_difficulty', + 'growth_rate', + 'products' + ] + writer.writerow(headers) + + # Write example row with available options + example_row = [ + 'Monstera Deliciosa', # name + 'Beautiful tropical plant with distinctive leaf holes', # description + 'Water weekly, mist leaves occasionally', # care_guide + ', '.join([c.name for c in Climate.query.all()]), # climate options + ', '.join([e.name for e in Environment.query.all()]), # environment options + ', '.join([l.name for l in Light.query.all()]), # light options + ', '.join([t.name for t in Toxicity.query.all()]), # toxicity options + ', '.join([s.name for s in Size.query.all()]), # size options + ', '.join([d.name for d in CareDifficulty.query.all()]), # care_difficulty options + ', '.join([r.name for r in GrowthRate.query.all()]), # growth_rate options + ', '.join([p.name for p in Product.query.all()]) # product options + ] + writer.writerow(example_row) + + # Create the response + output.seek(0) + return Response( + output, + mimetype='text/csv', + headers={ + 'Content-Disposition': 'attachment; filename=plants_template.csv', + 'Content-Type': 'text/csv', + } + ) + if __name__ == "__main__": with app.app_context(): db.create_all() # Create all tables diff --git a/templates/manage_plants.html b/templates/manage_plants.html index d785dbf..f0cb54b 100644 --- a/templates/manage_plants.html +++ b/templates/manage_plants.html @@ -6,7 +6,18 @@

All Plants

- +
+ + Download Template + +
+ + +
+ +