Skip to content

Commit

Permalink
[Updates📢] CRUD updates with validators in place
Browse files Browse the repository at this point in the history
  • Loading branch information
[esekyi] committed Aug 27, 2024
1 parent 948fbb0 commit 6a6960e
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 2 deletions.
76 changes: 74 additions & 2 deletions app/routes/recipe_routes.py
Original file line number Diff line number Diff line change
@@ -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/<uuid:recipe_id>', methods=['GET'], strict_slashes=False)
def view_recipe(recipe_id):
pass


@bp.route('/recipes/<uuid:recipe_id>/edit', methods=['PUT'], strict_slashes=False)
@login_required
def edit_recipe(recipe_id):
pass


@bp.route('/recipes/<uuid:recipe_id>/delete', methods=['DELETE'], strict_slashes=False)
@login_required
def remove_recipe(recipe_id):
pass
80 changes: 80 additions & 0 deletions app/services/recipe_service.py
Original file line number Diff line number Diff line change
@@ -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)
17 changes: 17 additions & 0 deletions app/services/validation_service.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 6a6960e

Please sign in to comment.