diff --git a/app/routes/recipe_routes.py b/app/routes/recipe_routes.py index 0b6c1ab..807c871 100644 --- a/app/routes/recipe_routes.py +++ b/app/routes/recipe_routes.py @@ -1,4 +1,76 @@ -from flask import Blueprint, jsonify, request +from flask import Blueprint, request, url_for, render_template, jsonify, flash, redirect from app import db +from app.services.recipe_service import get_all_recipes, get_recipe_by_id, create_recipe +from app.services.validation_service import validate_recipe_data +from app.services.category_sevice import get_all_categories +from app.services.image_service import upload_image_to_s3 +from flask_login import login_required, current_user -bp = Blueprint('recipe_route', __name__) +bp = Blueprint('recipe_routes', __name__) + + +@bp.route('/recipes/create', methods=['GET', 'POST'], strict_slashes=False) +@login_required +def add_recipe(): + if request.method == 'POST': + data = request.form.to_dict() # ensuring form data comes as to dictionary. + ingredients = request.form.getlist('ingredients[]') + comments = request.form.getlist('comments[]') + instructions = request.form.getlist('instructions[]') + image = request.files.get('image') + + errors = validate_recipe_data(data) + if errors: + for error in errors: + flash(error, 'error') + return redirect(url_for('recipe_routes.add_recipe')) + + + image_url = None + if image: + try: + image_url = upload_image_to_s3(image) + except ValueError as e: + flash(str(e), 'danger') + return redirect(url_for('recipe_routes.add_recipe')) + + + try: + send_url = image_url if image_url is not None else '' + create_recipe(data, comments, ingredients, instructions, send_url, user_id=current_user.id) + flash("Recipe created successfully!", 'success') + return redirect(url_for('recipe_routes.list_recipes')) + + except Exception as e: + # Rollback db session in case of an error + db.session.rollback() + flash( + f"An error occurred while creating the recipe: {str(e)}", "error") + return redirect(url_for('recipe_routes.add_recipe')) + + + categories = get_all_categories() + return render_template('recipes/create.html', categories=categories) + + +@bp.route('/recipes', methods=['GET'], strict_slashes=False) +def list_recipes(): + recipes = get_all_recipes() + return render_template('recipes/list.html', recipes=recipes) + + +@bp.route('/recipes/', methods=['GET'], strict_slashes=False) +def view_recipe(recipe_id): + pass + + +@bp.route('/recipes//edit', methods=['PUT'], strict_slashes=False) +@login_required +def edit_recipe(recipe_id): + pass + + +@bp.route('/recipes//delete', methods=['DELETE'], strict_slashes=False) +@login_required +def remove_recipe(recipe_id): + pass diff --git a/app/services/recipe_service.py b/app/services/recipe_service.py new file mode 100644 index 0000000..61ef130 --- /dev/null +++ b/app/services/recipe_service.py @@ -0,0 +1,80 @@ +from app.models.recipe import Recipe +from app.models.ingredient import Ingredient +from app.models.comment import Comment +from app.models.instruction import Instruction +from app import db + + +def create_recipe(data, user_id, ingredients, comments, instructions, send_url): + """ + Create a new recipe and save it to the database. + + :param data: A dictionary containing recipe data. + :param ingredients: A list of ingredients to associate with the recipe. + :param comments: A list of comments to associate with the recipe. + :param user_id: The ID of the user creating the recipe. + :return: The newly created Recipe object. + """ + try: + new_recipe = Recipe( + title=data['title'], + description=data['description'], + instructions=data['instructions'], + prep_time=int(data.get('prep_time', 0)), # Default to 0 if not provided + cook_time=int(data.get('cook_time', 0)), # Default to 0 if not provided + servings=int(data.get('servings', 0)), # Default to 0 if not provided + category_id=data['category_id'], + user_id=user_id, + image_url=send_url + ) + db.session.add(new_recipe) + db.session.flush() # Flush pending transaction to get the recipe ID before committing + + # Handle ingredients + for ingredient_name in ingredients: + if ingredient_name: # not tolerating any empty ingredient + ingredient = Ingredient( + name=ingredient_name, + recipe_id=new_recipe.id + ) + db.session.add(ingredient) + + # Handle comments + for comment_text in comments: + if comment_text: # not tolerating empty comments + comment = Comment( + text=comment_text, + user_id=user_id, + recipe_id=new_recipe.id + ) + db.session.add(comment) + + # Handle each instruction + for i, instruction in enumerate(instructions): + if instruction: + new_instruction = Instruction( + step_number=i + 1, + name=instruction, + recipe_id=new_recipe.id + ) + db.session.add(new_instruction) + + + db.session.commit() # comit all changes to different tables + return new_recipe + + except Exception as e: + db.session.rollback() + raise Exception(f"Failed to create recipe: {str(e)}") + + +def get_all_recipes(): + return Recipe.query.all() + + +def get_recipe_by_id(recipe_id): + return Recipe.query.get(recipe_id) + + +def delete_recipe(recipe): + db.session.delete(recipe) diff --git a/app/services/validation_service.py b/app/services/validation_service.py new file mode 100644 index 0000000..af7eb83 --- /dev/null +++ b/app/services/validation_service.py @@ -0,0 +1,17 @@ +def validate_recipe_data(data): + """validate_recipe_data(data) + validate recipe data as they come in + and display errors if not available + """ + errors = [] + if not data.get('title'): + errors.append('Title is required.') + if not data.get('description'): + errors.append('Description is required.') + if not data.get('instructions'): + errors.append('Instructions are required.') + if not data.get('category_id'): + errors.append('Category is required.') + + return errors +