diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fc691a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/venv +/frontend/package-lock.json \ No newline at end of file diff --git a/backend/crud.py b/backend/crud.py new file mode 100644 index 0000000..2fc770f --- /dev/null +++ b/backend/crud.py @@ -0,0 +1,15 @@ +from unicodedata import name +from unittest import skip +from sqlalchemy.orm import Session + +import models, schemas + +def get_grocery_list(db: Session, skip: int = 0, limit: int = 50): + return db.query(models.Food).offset(skip).limit(limit).all() + +def add_to_grocery_list(db: Session, food: schemas.FoodCreate ): + db_food = models.Food(name = food.name) + db.add(db_food) + db.commit() + db.refresh(db_food) + return db_food \ No newline at end of file diff --git a/backend/database.py b/backend/database.py new file mode 100644 index 0000000..5fe3f80 --- /dev/null +++ b/backend/database.py @@ -0,0 +1,20 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +# ------ Create a database URL for SQLAlchemy +# SQLALCHEMY_DATABASE_URL = "postgresql://localhost:5432" +# SQLALCHEMY_DATABASE_URL = "postgresql://postgres:password@localhost:5432/food" +SQLALCHEMY_DATABASE_URL = "postgresql://postgres:password@localhost:5432/postgres" +# SQLALCHEMY_DATABASE_URL = "postgresql://postgres:postgrespassword@host.docker.internal:5432/postgres" + + +# ------ Create the SQLAlchemy engine +engine = create_engine( + SQLALCHEMY_DATABASE_URL +) +# ------ Create a SessionLocal (the class for a database session) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# ------ Create a base Class (used to create the database models/classes/ORM models) +Base = declarative_base() diff --git a/backend/main.py b/backend/main.py index 7e89bbb..073cde6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,9 +1,18 @@ -from fastapi import FastAPI +from fastapi import FastAPI, Depends, HTTPException from pydantic import BaseModel +from sqlalchemy.orm import Session + from fastapi.middleware.cors import CORSMiddleware import requests +# from . import crud, models, schemas +# from .database import SessionLocal, engine +import crud, models, schemas +from database import SessionLocal, engine +# Initialize tables; generally, Alembic is a tool meant for initializing DBs and migrations. +# But, for now we can use this +models.Base.metadata.create_all(bind=engine) app = FastAPI() @@ -19,30 +28,51 @@ allow_headers=["*"], ) -class Food(BaseModel): +# Dependancy +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +class Food2(BaseModel): + id:str name:str -grocery_list = [ - {"name":"oranges"}, - {"name":"bread"} -] + +# grocery_list = [ +# {"name":"oranges"}, +# {"name":"bread"} +# ] ## Endpoints @app.get("/") async def welcome(): return "Who's Hungry!" -@app.get("/groceries") -async def get_groceries_list(): + +@app.get("/groceries", response_model = list[schemas.Food]) +def get_groceries_list(skip: int = 0, limit: int = 50, db: Session = Depends(get_db)): + grocery_list = crud.get_grocery_list(db, skip, limit=limit) return grocery_list -@app.post('/add') -async def add_item(item:Food): - grocery_list.append(item) - return "Added " + item.name +@app.post('/add', response_model = schemas.Food) +def add_to_grocery_list(food:schemas.FoodCreate, db: Session = Depends(get_db)): + return crud.add_to_grocery_list(db=db, food=food) + + +# @app.get("/groceries") +# async def get_groceries_list(): +# return grocery_list + +# @app.post('/add') +# async def add_item(item:Food): +# grocery_list.append(item) +# return "Added " + item.name @app.get('/recipes') -async def find_recipes(item:Food): +async def find_recipes(item:Food2): #schemas.Food result = [] response = find_by_ingredients(item.name) diff --git a/backend/models.py b/backend/models.py new file mode 100644 index 0000000..c93356b --- /dev/null +++ b/backend/models.py @@ -0,0 +1,10 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.orm import relationship + +from database import Base + +class Food(Base): + __tablename__ = "food" + + id = Column(Integer, index=True, primary_key=True) + name = Column(String, index=True) diff --git a/backend/schemas.py b/backend/schemas.py new file mode 100644 index 0000000..c57f5b3 --- /dev/null +++ b/backend/schemas.py @@ -0,0 +1,15 @@ +# Create the Pydantic models(schemas) +from typing import List, Union +from pydantic import BaseModel + +class FoodBase(BaseModel): + name: str + class Config: # Use Pydantic's orm_mode + orm_mode = True + +class FoodCreate(FoodBase): + pass + +class Food(FoodBase): + id: int + pass \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 03843ca..c9075db 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,10 +18,13 @@ "awesome-typescript-loader": "^5.2.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-scripts": "5.0.1", + "react-scripts": "^5.0.1", "source-map-loader": "^4.0.0", "typescript": "^4.7.4", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/uuid": "^8.3.4" } }, "node_modules/@ampproject/remapping": { @@ -4009,6 +4012,12 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -21132,6 +21141,12 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index d3cc4c0..e066af8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "awesome-typescript-loader": "^5.2.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-scripts": "5.0.1", + "react-scripts": "^5.0.1", "source-map-loader": "^4.0.0", "typescript": "^4.7.4", "web-vitals": "^2.1.4" @@ -42,5 +42,8 @@ "last 1 safari version" ] }, - "proxy":"http://localhost:8000" + "proxy": "http://localhost:8000", + "devDependencies": { + "@types/uuid": "^8.3.4" + } } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0d38da1..a88ed36 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5,6 +5,7 @@ import React from 'react'; import List from './components/List'; import Recipe from './components/Recipe'; import { isTemplateExpression } from 'typescript'; +import {v4 as uuid} from 'uuid'; export default function App() { @@ -22,6 +23,11 @@ export default function App() { function onClose(){ setAddModalIsOpen(false); console.log('canceled'); + + // const myuuid = uuid(); + // console.log('Your UUID is:' + myuuid); + // console.log(typeof myuuid); + } function onAdd(foodName: string){ fetch('http://localhost:3000/add',{ @@ -29,7 +35,11 @@ export default function App() { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({"name":foodName}), + body: JSON.stringify({ + "id":uuid(), + "name":foodName + }), + // body: JSON.stringify({"name":foodName}), }) .then(data => { console.log('Success', data) @@ -41,12 +51,14 @@ export default function App() { } async function fetchFoodList() { + console.log("about to fetch some food..."); const response = await fetch('http://localhost:3000/groceries', { method:'GET', headers: { 'accept': 'application/json', } }); + console.log("got some food...", response); const newList: FoodObj[] = await response.json(); console.log('newList', newList); const newFoodList: FoodList = { @@ -90,7 +102,7 @@ export default function App() { React.useEffect(() => { fetchFoodList(); - }, [addModalIsOpen]); + }, [foodList]); return (