Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to fetch recipes #3

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,72 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from datetime import date
import os
import json

app = FastAPI()

origins = ["*"]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

@app.get("/recipes")
def recipes(start_date: date, end_date: date):
# fetches the json
data = load_data()

# filters by start and end date
data = filter_data(data=data, start_date=str(start_date), end_date=str(end_date))

if not data:
return

data_main, data_dessert, data_side = data

return [data_main, data_dessert, data_side]


def load_data():
try:
current_dir = os.path.dirname(os.path.realpath(__file__))
f = open(current_dir + "/recipes.json", "r")
data = json.load(f)
f.close()

if data:
return data
else:
raise Exception(f"There's no recipe data")
except Exception as requestException: # exception - fetching data was unsuccessful
raise Exception("There was a problem getting recipe data")


def filter_data(data: list[dict], start_date: str, end_date: str):
filtered_data_main = []
filtered_data_side = []
filtered_data_dessert = []

for item in data:
if item["date"] >= start_date and item["date"] <= end_date:
if item["category"] == "Main":
filtered_data_main.append(item)
elif item["category"] == "Side":
filtered_data_side.append(item)
else:
filtered_data_dessert.append(item)

message = ""
if len(filtered_data_main + filtered_data_side + filtered_data_dessert) == 0:
message = "No recipes in the given time frame, stopping the process now."

if message:
print(message)
return False
else:
return filtered_data_main, filtered_data_side, filtered_data_dessert
102 changes: 102 additions & 0 deletions backend/app/recipes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
[
{
"category": "Dessert",
"cooking_time": 50,
"date": "2023-08-02",
"difficulty": "Medium",
"preparation_time": 30,
"short_description": "Sweet orange segments, warm melted dark chocolate and a light orange cream, these stunning Pavlovas are the perfect way to finish any dinner.",
"title": "Chocolate & Orange Pavlovas",
"image_url": "https://images.prismic.io/oddbox/03e6cf3f-f3bb-46ed-914a-0928d11acf23_IMG_7841%202.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Main",
"cooking_time": 15,
"date": "2023-08-01",
"difficulty": "Easy",
"preparation_time": 10,
"short_description": "This express aubergine and white beans recipe can be made in 30 minutes. Feel free to swap the veg with whatever you have in your box every week, and use any white beans of your choice.",
"title": "Mediterranean Aubergine & White Beans",
"image_url": "https://images.prismic.io/oddbox/d184f523-9072-40e7-9f70-28a40993a2c2_IMG_7847%203.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Main",
"cooking_time": 0,
"date": "2023-07-26",
"difficulty": "Easy",
"preparation_time": 15,
"short_description": "Salad jars are perfect for a healthy lunch on busy days. Made ahead of time, easy to adapt and so delicious. You can store these for up to 3 days, making them perfect for meal prep and batch cooking. Our Thai red curry paste makes the creamiest and tastiest dressing in just minutes.",
"title": "Thai Red Curry Salad Jars",
"image_url": "https://images.prismic.io/oddbox/da289db2-18d8-4705-bc93-fcc85f310b89_IMG_7410.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Main",
"cooking_time": 0,
"date": "2023-07-25",
"difficulty": "Easy",
"preparation_time": 15,
"short_description": "A bircher is a great make-ahead breakfast and a handy recipe to have on hand.",
"title": "Blackberry Bircher",
"image_url": "https://images.prismic.io/oddbox/c5d6c998-4153-4d28-9c0e-c8b1df4891e7_IMG_7312%202.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Main",
"cooking_time": 20,
"date": "2023-07-25",
"difficulty": "Easy",
"preparation_time": 10,
"short_description": "An easy vegan version of a Thai noodle curry, packed with vegetables, coconut milk, chilli and showcasing our small batch of Thai red curry paste. A delicious warming family meal. ",
"title": "Carrot Thai Noodle Curry ",
"image_url": "https://images.prismic.io/oddbox/57ea6a41-16e7-496d-bf90-98118c993cd8_IMG_7398.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Side",
"cooking_time": 25,
"date": "2023-07-24",
"difficulty": "Medium",
"preparation_time": 120,
"short_description": "Crackingly crispy on the outside and scrumptiously soft on the inside, check out this balti focaccia bread recipe from @lagomchef! Made with our small batch Oddbox Balti paste, it makes for picnic perfection. Take the taste up a notch by serving with spicy Balti yoghurt, crispy fried onions and diced onion and coriander \u2013 this bread is a smash either solo or shared.",
"title": "Balti Focaccia",
"image_url": "https://images.prismic.io/oddbox/3916bc1b-ff57-4e78-8c45-45fdfbfbbdcc_IMG_9026.jpg?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Side",
"cooking_time": 20,
"date": "2023-07-20",
"difficulty": "Easy",
"preparation_time": 10,
"short_description": "Roasting radishes brings out their sweetness and this must-try recipe will turn anyone into a radish lover. We love them served over a creamy sauce made from yoghurt/vegan yoghurt and harissa.\u00a0",
"title": "Quick Garlic Roasted Radishes",
"image_url": "https://images.prismic.io/oddbox/4cff9ce3-9b0a-4b20-bdf6-d06f558e9fa9_IMG_7019%202.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Side",
"cooking_time": 15,
"date": "2023-07-19",
"difficulty": "Easy",
"preparation_time": 15,
"short_description": "Use any leftover vegetables for this recipe, such as carrots, onion, cabbage, potato, spring onions, broccoli, cauliflower and even cauliflower leaves.\u00a0",
"title": "Leftover Vegetable Bhajis with Yoghurt Balti Curry Dip",
"image_url": "https://images.prismic.io/oddbox/8c71a7e4-ec11-4fb5-b56a-332f0dcb28b9_IMG_7024%202.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Main",
"cooking_time": 20,
"date": "2023-07-19",
"difficulty": "Easy",
"preparation_time": 10,
"short_description": "Street food inspired roasted cauliflower pittas with our small batch of balti curry paste. Roasted crispy cauliflower stuffed pittas with all the extras. This loaded pitta is so good and makes for a fun, delicious, and different weeknight dinner.\u00a0",
"title": "Roasted Cauliflower Pittas with Balti Curry Paste",
"image_url": "https://images.prismic.io/oddbox/eaa2c34e-e004-4026-be57-dd26a5e95685_IMG_7106.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
},
{
"category": "Main",
"cooking_time": 0,
"date": "2022-06-30",
"difficulty": "Easy",
"preparation_time": 10,
"short_description": "This classic cherry and chocolate flavour pairing is perfect for meal prep and batch cooking. Super quick, all the ingredients are added to up-cycled jam jars and kept in the fridge for breakfast.",
"title": "Cherry & Chocolate Overnight Oats",
"image_url": "https://images.prismic.io/oddbox/4e4dd1b9-ed14-4e8d-8824-c36619552136_IMG_2353.JPG?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max"
}
]
29 changes: 29 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@types/node": "^16.18.76",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"axios": "^1.6.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/components/RecipeCard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.Recipe-card {
width: 25%;
}

#Easy {
padding: 5px 15px;
display: inline-block;
background-color: rgb(190, 237, 190);
border-radius: 1000px
}

#Medium {
padding: 5px 15px;
display: inline-block;
background-color: rgb(218, 192, 107);
border-radius: 1000px
}

#Hard {
padding: 5px 15px;
display: inline-block;
background-color: rgb(212, 139, 139);
border-radius: 1000px
}
27 changes: 27 additions & 0 deletions frontend/src/components/RecipeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import "./RecipeCard.css";

interface RecipeCardProps {
title: string;
description: string;
image: string;
created_at: string; // Format will be DD-MM-YYYY
difficulty: "Easy" | "Medium" | "Difficult";
}

function RecipeCard(props: RecipeCardProps) {
return (
<section className="Recipe-card">
<h3>{props.title}</h3>
<img width="200" height="200" src={props.image} alt="img" />
<br />
<span>Created at: {props.created_at}</span>
<p>{props.description}</p>

{props.difficulty == "Easy" && <div id="Easy">Easy</div>}
{props.difficulty == "Medium" && <div id="Medium">Medium</div>}
{props.difficulty == "Difficult" && <div id="Difficult">Difficult</div>}
</section>
);
}

export default RecipeCard;
36 changes: 36 additions & 0 deletions frontend/src/components/RecipeFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import "./RecipeFilters.css";
import axios from 'axios'
import { useState } from 'react';

interface RecipeFiltersProps {
returnDataCallback: any
}

function RecipeFilters(props: RecipeFiltersProps) {

const [startDate, setStartDate] = useState<string>("")
const [endDate, setEndDate] = useState<string>("")


const fetch_data = () => {
console.log('clicked')

// Fetch recipe data from backend
axios.get("http://localhost:8000/recipes?start_date=" + startDate + "&end_date=" + endDate).then((response) => {
console.log('response is', response.data);

// Pass recipe data from API back to the parent page
props.returnDataCallback(response.data)
}).catch((error) => console.log(error))
}

return (
<section className="Recipe-filters">
From: <input type="text" placeholder="YYYY-MM-DD" style={{'marginRight': "20px"}} onChange={(event) => setStartDate(event.target.value)}></input>
To date: <input type="text" placeholder="YYYY-MM-DD" style={{'marginRight': "20px"}} onChange={(event) => setEndDate(event.target.value)}></input>
<input type="button" value="Search" onClick={() => fetch_data()} />
</section>
);
}

export default RecipeFilters;
8 changes: 8 additions & 0 deletions frontend/src/pages/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@
align-items: center;
background-color: white;
}

.Recipe-card-container {
display: flex;
width: 100%;
flex-wrap: wrap;
justify-content: space-evenly;
gap: 20px;
}
42 changes: 40 additions & 2 deletions frontend/src/pages/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,50 @@
import './App.css';
import Header from '../components/Header';
import "./App.css";
import Header from "../components/Header";
import RecipeCard from "../components/RecipeCard";
import RecipeFilters from "../components/RecipeFilters";
import { useState } from "react";

interface RecipeData {
title: string;
category: string;
cooking_time: number;
date: string;
difficulty: any;
image_url: string;
short_description: string;
}

function App() {
const [recipeArrays, setRecipes] = useState<RecipeData[][]>([]);

return (
<div className="App">
<Header />
<section className="App-body">
<h1>Welcome to the Oddbox Recipe Finder!</h1>
<p>Enter your start date and end date below, and this page will filter your recipes by recipe category.</p>
<RecipeFilters returnDataCallback={(data: any) => setRecipes(data)} />
{recipeArrays.map((recipeArray) => {
return (
<>
<h2>Category: {recipeArray[0].category}</h2>
<section className="Recipe-card-container">
{recipeArray.map((recipe) => {
return (
<RecipeCard
key="key"
title={recipe.title}
description={recipe.short_description}
image={recipe.image_url}
difficulty={recipe.difficulty}
created_at={recipe.date}
/>
);
})}
</section>
</>
);
})}
</section>
</div>
);
Expand Down