diff --git a/.env.copy b/.env.copy index 72fbdb2..43ebac8 100644 --- a/.env.copy +++ b/.env.copy @@ -1,7 +1,7 @@ # Database Configuration MYSQL_HOST=db MYSQL_PORT=3306 -MYSQL_USER=user -MYSQL_PASSWORD=password -MYSQL_DATABASE=lazygrocer_db -MYSQL_ROOT_PASSWORD=rootpassword \ No newline at end of file +MYSQL_USER=root +MYSQL_PASSWORD= +MYSQL_DATABASE=lazygrocer +MYSQL_ROOT_PASSWORD= \ No newline at end of file diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 450c560..8c99c00 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -17,7 +17,7 @@ jobs: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_PASSWORD }} - MYSQL_DATABASE: testgrocer + MYSQL_DATABASE: lazygrocer ports: - 3306:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 diff --git a/.test/gui.py b/.test/gui.py new file mode 100644 index 0000000..30b3daf --- /dev/null +++ b/.test/gui.py @@ -0,0 +1,56 @@ +import curses + +# Sample data - list of recipes and ingredients +recipes = [ + {"name": "Spaghetti Carbonara", "ingredients": ["spaghetti", "eggs", "bacon", "parmesan cheese"]}, + {"name": "Chicken Curry", "ingredients": ["chicken", "curry sauce", "rice", "onion", "garlic"]}, + {"name": "Caesar Salad", "ingredients": ["romaine lettuce", "croutons", "parmesan cheese", "caesar dressing"]} +] + +# Function to draw the header +def draw_header(stdscr): + stdscr.addstr(0, 0, "Recipe List - Commands: [A]dd [E]dit [D]elete [Q]uit") + stdscr.refresh() + +# Function to draw the list of recipes +def draw_recipes(stdscr, selected_row): + stdscr.clear() + draw_header(stdscr) + for i, recipe in enumerate(recipes): + x = 2 + y = i + 2 + if i == selected_row: + stdscr.attron(curses.color_pair(1)) + stdscr.addstr(y, x, f"{recipe['name']}") + stdscr.attroff(curses.color_pair(1)) + else: + stdscr.addstr(y, x, f"{recipe['name']}") + stdscr.refresh() + +# Function to handle user input +def main(stdscr): + # Initialize color pair for selected row + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) + # Turn off cursor display + curses.curs_set(0) + # Set up keyboard input + stdscr.keypad(True) + + current_row = 0 + draw_recipes(stdscr, current_row) + + while True: + key = stdscr.getch() + stdscr.clear() + + if key == curses.KEY_UP and current_row > 0: + current_row -= 1 + elif key == curses.KEY_DOWN and current_row < len(recipes) - 1: + current_row += 1 + elif key == ord('q'): + break + + draw_recipes(stdscr, current_row) + +curses.wrapper(main) + diff --git a/build.log b/build.log new file mode 100644 index 0000000..9aec7fe --- /dev/null +++ b/build.log @@ -0,0 +1,43 @@ +#0 building with "desktop-linux" instance using docker driver + +#1 [app internal] load build definition from dockerfile +#1 transferring dockerfile: 920B done +#1 DONE 0.0s + +#2 [app internal] load metadata for docker.io/library/python:3.10-slim +#2 DONE 0.2s + +#3 [app internal] load .dockerignore +#3 transferring context: 2B done +#3 DONE 0.0s + +#4 [app 1/6] FROM docker.io/library/python:3.10-slim@sha256:61912260e578182d00b5e163eb4cfb13b35fb8782c98d1df9ed584cec8939097 +#4 DONE 0.0s + +#5 [app internal] load build context +#5 transferring context: 7.65kB done +#5 DONE 0.0s + +#6 [app 5/6] RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt +#6 CACHED + +#7 [app 4/6] COPY requirements.txt /src/ +#7 CACHED + +#8 [app 3/6] RUN apt-get update && apt-get install -y --no-install-recommends default-libmysqlclient-dev build-essential && rm -rf /var/lib/apt/lists/* +#8 CACHED + +#9 [app 2/6] WORKDIR /src +#9 CACHED + +#10 [app 6/6] COPY src/ /src/ +#10 CACHED + +#11 [app] exporting to image +#11 exporting layers done +#11 writing image sha256:64875579faab298182143c44f6219647aba0fbab3f2c814260866b1cee3443c3 done +#11 naming to docker.io/library/lazygrocer-app done +#11 DONE 0.0s + +#12 [app] resolving provenance for metadata file +#12 DONE 0.0s diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..9b1c9dd --- /dev/null +++ b/compose.yml @@ -0,0 +1,34 @@ +services: + app: + build: + context: . + ports: + - "8000:8000" + environment: + MYSQL_HOST: db + MYSQL_USER: root + MYSQL_PASSWORD: lg-db-pd + MYSQL_DATABASE: lazygrocer + depends_on: + db: + condition: service_healthy + + db: + image: mysql:latest + environment: + MYSQL_ROOT_PASSWORD: lg-db-pd + MYSQL_DATABASE: lazygrocer + ports: + - "3306:3306" + volumes: + - lg-db-volume:/var/lib/mysql + - ./scripts:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + lg-db-volume: + driver: local \ No newline at end of file diff --git a/demo.mov b/demo.mov new file mode 100644 index 0000000..4346117 Binary files /dev/null and b/demo.mov differ diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6ddfae8..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: '3.8' - -services: - app: - build: . - container_name: lazygrocer_app - ports: - - "8000:8000" - env_file: - - .env - environment: - MYSQL_HOST: ${MYSQL_HOST} - MYSQL_PORT: ${MYSQL_PORT} - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} - MYSQL_DATABASE: ${MYSQL_DATABASE} - depends_on: - - db - volumes: - - .:/src - - db: - image: mysql:8.0 - container_name: lazygrocer_db - env_file: - - .env - environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE: ${MYSQL_DATABASE} - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} - ports: - - "3306:3306" - volumes: - - db_data:/var/lib/mysql - -volumes: - db_data: diff --git a/dockerfile b/dockerfile index 6a8e5d3..5edf69a 100644 --- a/dockerfile +++ b/dockerfile @@ -1,4 +1,3 @@ -# Use an official Python runtime as a parent image FROM python:3.10-slim # Set environment variables @@ -8,20 +7,26 @@ ENV PYTHONUNBUFFERED=1 # Set working directory in the container WORKDIR /src -# Install system dependencies for MySQL client +# Install system dependencies for MySQL client and MySQL server RUN apt-get update && apt-get install -y --no-install-recommends \ - default-libmysqlclient-dev build-essential && \ - rm -rf /var/lib/apt/lists/* +default-libmysqlclient-dev build-essential && \ +rm -rf /var/lib/apt/lists/* -# Copy the application code to the container -COPY . /src/ +# Copy only requirements to leverage Docker cache +COPY requirements.txt /src/ # Install Python dependencies RUN pip install --no-cache-dir --upgrade pip && \ pip install --no-cache-dir -r requirements.txt +# Copy the application code to the container (only the src folder) +COPY src/ /src/ + # Expose the port the app runs on (optional) EXPOSE 8000 -# Command to run the application -CMD ["python", "src/main.py"] +# Define a volume for persistent data +VOLUME /src/data + +# Start MySQL service and run the application +CMD ["python", "./main.py"] \ No newline at end of file diff --git a/docs/diagrams/LazyGrozerERDdiagram.png b/docs/diagrams/LazyGrozerERDdiagram.png index 73520e4..443b929 100644 Binary files a/docs/diagrams/LazyGrozerERDdiagram.png and b/docs/diagrams/LazyGrozerERDdiagram.png differ diff --git a/docs/plantuml/erd.wsd b/docs/plantuml/erd.wsd index 4af2a44..0042a8f 100644 --- a/docs/plantuml/erd.wsd +++ b/docs/plantuml/erd.wsd @@ -7,12 +7,20 @@ footer CS3200 Final Project: Michael Montanaro 2024 object Recipe { title <> description - rating - meal_timing - favorite? date_published } +object Rating { + score + description + date_added +} + +object Favorite { + date_added + description +} + object RecipeList { name <> description @@ -20,10 +28,14 @@ object RecipeList { object Instruction { cook_time + prep_time servings calories - steps - url +} + +object Step { + id <> + description } object Ingredient { @@ -32,29 +44,26 @@ object Ingredient { last_added } -object IngredientList { +object GroceryList { name <> description } -object Chef { - username <> - first_name - last_name - email -} - object includes { quantity } diamond DIncludes +diamond stepdiamond +Rating }o--o| Recipe +Favorite }o--|| Recipe Recipe }|-- DIncludes DIncludes --|{ Ingredient: > DIncludes . includes Recipe }|-o{ RecipeList: contains < -Ingredient }|-o{ IngredientList: for < -Chef ||--o{ Recipe: publishes > -Instruction ||-|| Recipe: explains > +Ingredient }|-o{ GroceryList: for < +Instruction ||- stepdiamond +stepdiamond -|| Recipe: explains > +stepdiamond --|{ Step: describes < @enduml \ No newline at end of file diff --git a/guide.md b/guide.md new file mode 100644 index 0000000..7a2fee1 --- /dev/null +++ b/guide.md @@ -0,0 +1,172 @@ +
+ + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] + + +
+
+

LazyGrocer

+ +

+ Created for: CS3200 Database Design Final Project +
+ By: Michael Montanaro, Colby Hegarty, Sanay Doshi +

+
+ + +
+ Table of Contents +
    +
  1. About The Project
  2. +
  3. Getting Started
  4. +
  5. Usage
  6. +
  7. Structure
  8. +
  9. Tasks
  10. +
  11. Contributing
  12. +
  13. License
  14. +
  15. Contact
  16. +
  17. Acknowledgments
  18. +
+
+ + +## About The Project + +Automatically generate your shopping list for a selected number of recipes.Import or manually add recipes to a locally stored database. + +

(back to top)

+ + +## Getting Started +On Unix Systems: +```bash +$ python --version && python3 --version +# TODO: add installation instructions +$ lg --version +``` +

(back to top)

+ + +## Usage +```python +// Start app for UI + +lg + + +// Use CLI tools + +lg recipe // return list of recipes +lg ingredient // return list of ingredients +lg write [ *file* ] // create a recipe +lg url *link* // create recipe from valid link + +``` + +

(back to top)

+ + +## Structure +``` +${LAZYGROCER_ROOT} +├── lazyGrocer.py +├── docs +| ├── plantum1 +| | ├── class.wsd +| | └── erd.wsd +│ ├── diagrams +| | ├── class/LazyGrocerClassDiagram.png +| | └── erd/LazyGrocerERDdiagram.png +├── services +| ├── TODO +| └── TODO +├── artifcats +| ├── data +| | └── test.db +| ├── exports +| | └── example.json +├── README.md +├── __init__.py +├── .gitignore +└── LICENSE.txt +``` + +

(back to top)

+ + +## Tasks +- [ ] add exporting recipes as JSON +- [ ] add installation instructions +- [ ] add Structure section +- [ ] add Sanay and Colby information in Contact section + +See the [open issues](https://github.com/montymi/LazyGrocer/issues) for a full list of issues and proposed features. + +

(back to top)

+ + + + +## Contributing + +1. [Fork the Project](https://docs.github.com/en/get-started/quickstart/fork-a-repo) +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. [Open a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) + +

(back to top)

+ + + + +## License + +*Documentation must include a license section in which the type of license and a link or reference to the full license in the repository is given.* + +Distributed under the MIT License. See `LICENSE.txt` for more information. + +

(back to top)

+ + + + +## Contact + +Michael Montanaro - [LinkedIn](https://www.linkedin.com/in/michael-montanaro/) - montanaro.m@northeastern.edu + +Project Link: [https://github.com/montymi/LazyGrocer](https://github.com/montymi/LazyGrocer) + +

(back to top)

+ + + +## Acknowledgments + +* [Choose an Open Source License](https://choosealicense.com) +* [GitHub Pages](https://pages.github.com) + +

(back to top)

+ + + + + +[contributors-shield]: https://img.shields.io/github/contributors/montymi/ClearDocs.svg?style=for-the-badge +[contributors-url]: https://github.com/montymi/ClearDocs/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/montymi/ClearDocs.svg?style=for-the-badge +[forks-url]: https://github.com/montymi/ClearDocs/network/members +[stars-shield]: https://img.shields.io/github/stars/montymi/ClearDocs.svg?style=for-the-badge +[stars-url]: https://github.com/montymi/ClearDocs/stargazers +[issues-shield]: https://img.shields.io/github/issues/montymi/ClearDocs.svg?style=for-the-badge +[issues-url]: https://github.com/montymi/ClearDocs/issues +[license-shield]: https://img.shields.io/github/license/montymi/ClearDocs.svg?style=for-the-badge +[license-url]: https://github.com/montymi/ClearDocs/blob/master/LICENSE.txt +[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 +[linkedin-url]: https://linkedin.com/in/michael-montanaro diff --git a/report.pdf b/report.pdf new file mode 100644 index 0000000..0c19513 Binary files /dev/null and b/report.pdf differ diff --git a/requirements.txt b/requirements.txt index 488079e..644bade 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ +mysql.connector +pymysql +cryptography mysql-connector-python diff --git a/src/commands/Command.py b/src/commands/Command.py new file mode 100644 index 0000000..110bf9e --- /dev/null +++ b/src/commands/Command.py @@ -0,0 +1,3 @@ +class Command: + def execute(self): + raise NotImplementedError("execute() method must be implemented.") diff --git a/src/commands/create/CreateGroceryList.py b/src/commands/create/CreateGroceryList.py new file mode 100644 index 0000000..cb9a159 --- /dev/null +++ b/src/commands/create/CreateGroceryList.py @@ -0,0 +1,10 @@ +from commands.Command import Command + +class CreateGroceryListCommand(Command): + def __init__(self, grocery_list_title, description, dc): + self.grocery_list_title = grocery_list_title + self.description = description + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_grocery_list', (self.grocery_list_title, self.description))) diff --git a/src/commands/create/CreateRecipe.py b/src/commands/create/CreateRecipe.py new file mode 100644 index 0000000..ae544f3 --- /dev/null +++ b/src/commands/create/CreateRecipe.py @@ -0,0 +1,13 @@ +from datetime import datetime + +from commands.Command import Command + +class CreateRecipeCommand(Command): + def __init__(self, recipe_title, description, dc): + self.recipe_title = recipe_title + self.description = description + self.date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_recipe', (self.recipe_title, self.description, self.date))) diff --git a/src/commands/create/CreateRecipeIngredient.py b/src/commands/create/CreateRecipeIngredient.py new file mode 100644 index 0000000..96ff8b3 --- /dev/null +++ b/src/commands/create/CreateRecipeIngredient.py @@ -0,0 +1,13 @@ +from commands.Command import Command +from datetime import datetime + +class CreateRecipeIngredientCommand(Command): + def __init__(self, recipe_title, ingredient_title, quantity, dc): + self.recipe_title = recipe_title + self.ingredient_title = ingredient_title + self.quantity = quantity + self.date = datetime.now().strftime('%Y-%m-%d') + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_ingredient_to_recipe', (self.recipe_title, self.ingredient_title, self.quantity, self.date))) diff --git a/src/commands/create/CreateRecipeInstruction.py b/src/commands/create/CreateRecipeInstruction.py new file mode 100644 index 0000000..56c2f74 --- /dev/null +++ b/src/commands/create/CreateRecipeInstruction.py @@ -0,0 +1,13 @@ +from commands.Command import Command + +class CreateRecipeInstructionCommand(Command): + def __init__(self, recipe_title, cook_time, prep_time, servings, calories, dc): + self.recipe_title = recipe_title + self.cook_time = cook_time + self.prep_time = prep_time + self.servings = servings + self.calories = calories + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_recipe_instruction', (self.recipe_title, self.cook_time, self.prep_time, self.servings, self.calories))) diff --git a/src/commands/create/CreateRecipeList.py b/src/commands/create/CreateRecipeList.py new file mode 100644 index 0000000..b1d5830 --- /dev/null +++ b/src/commands/create/CreateRecipeList.py @@ -0,0 +1,10 @@ +from commands.Command import Command + +class CreateRecipeListCommand(Command): + def __init__(self, recipe_list_title, description, dc): + self.recipe_list_title = recipe_list_title + self.description = description + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_recipe_list', (self.recipe_list_title, self.description))) diff --git a/src/commands/create/CreateRecipeRating.py b/src/commands/create/CreateRecipeRating.py new file mode 100644 index 0000000..d8ae411 --- /dev/null +++ b/src/commands/create/CreateRecipeRating.py @@ -0,0 +1,13 @@ +from datetime import datetime +from commands.Command import Command + +class CreateRecipeRatingCommand(Command): + def __init__(self, recipe_title, score, description, dc): + self.recipe_title = recipe_title + self.score = score + self.description = description + self.date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_recipe_rating', (self.recipe_title, self.score, self.description, self.date))) diff --git a/src/commands/create/CreateRecipeStep.py b/src/commands/create/CreateRecipeStep.py new file mode 100644 index 0000000..41646ff --- /dev/null +++ b/src/commands/create/CreateRecipeStep.py @@ -0,0 +1,10 @@ +from commands.Command import Command + +class CreateRecipeStepCommand(Command): + def __init__(self, recipe_title, description, dc): + self.recipe_title = recipe_title + self.description = description + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_recipe_step', (self.recipe_title, self.description))) diff --git a/src/commands/delete/DeleteFavorite.py b/src/commands/delete/DeleteFavorite.py new file mode 100644 index 0000000..1de84f6 --- /dev/null +++ b/src/commands/delete/DeleteFavorite.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class DeleteFavoriteCommand(Command): + def __init__(self, recipe_title, dc): + self.recipe_title = recipe_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('delete_recipe_from_favorites', (self.recipe_title, ))) diff --git a/src/commands/delete/DeleteGroceryList.py b/src/commands/delete/DeleteGroceryList.py new file mode 100644 index 0000000..8749a15 --- /dev/null +++ b/src/commands/delete/DeleteGroceryList.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class DeleteGroceryListCommand(Command): + def __init__(self, grocery_list_title, dc): + self.grocery_list_title = grocery_list_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('delete_grocery_list', (self.grocery_list_title, ))) diff --git a/src/commands/delete/DeleteRecipe.py b/src/commands/delete/DeleteRecipe.py new file mode 100644 index 0000000..fe0ce4a --- /dev/null +++ b/src/commands/delete/DeleteRecipe.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class DeleteRecipeCommand(Command): + def __init__(self, recipe_title, dc): + self.recipe_title = recipe_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('delete_recipe', (self.recipe_title, ))) diff --git a/src/commands/delete/DeleteRecipeList.py b/src/commands/delete/DeleteRecipeList.py new file mode 100644 index 0000000..69bd089 --- /dev/null +++ b/src/commands/delete/DeleteRecipeList.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class DeleteRecipeListCommand(Command): + def __init__(self, recipe_list_title, dc): + self.recipe_list_title = recipe_list_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('delete_recipe_list', (self.recipe_list_title, ))) diff --git a/src/commands/read/ReadAllGroceryLists.py b/src/commands/read/ReadAllGroceryLists.py new file mode 100644 index 0000000..78eb131 --- /dev/null +++ b/src/commands/read/ReadAllGroceryLists.py @@ -0,0 +1,8 @@ +from commands.Command import Command + +class ReadAllGroceryListsCommand(Command): + def __init__(self, dc): + self.dc = dc + + def execute(self): + print(self.dc.procedure('read_all_grocery_lists')) diff --git a/src/commands/read/ReadAllIngredients.py b/src/commands/read/ReadAllIngredients.py new file mode 100644 index 0000000..2ac87d8 --- /dev/null +++ b/src/commands/read/ReadAllIngredients.py @@ -0,0 +1,8 @@ +from commands.Command import Command + +class ReadAllIngredientsCommand(Command): + def __init__(self, dc): + self.dc = dc + + def execute(self): + print(self.dc.procedure('read_all_ingredients')) diff --git a/src/commands/read/ReadAllRecipes.py b/src/commands/read/ReadAllRecipes.py new file mode 100644 index 0000000..99a29a5 --- /dev/null +++ b/src/commands/read/ReadAllRecipes.py @@ -0,0 +1,8 @@ +from commands.Command import Command + +class ReadAllRecipesCommand(Command): + def __init__(self, dc): + self.dc = dc + + def execute(self): + print(self.dc.procedure('read_all_recipes')) diff --git a/src/commands/read/ReadFavorites.py b/src/commands/read/ReadFavorites.py new file mode 100644 index 0000000..2e23448 --- /dev/null +++ b/src/commands/read/ReadFavorites.py @@ -0,0 +1,8 @@ +from commands.Command import Command + +class ReadFavoritesCommand(Command): + def __init__(self, dc): + self.dc = dc + + def execute(self): + print(self.dc.procedure('get_all_favorites')) diff --git a/src/commands/read/ReadGroceryList.py b/src/commands/read/ReadGroceryList.py new file mode 100644 index 0000000..f957275 --- /dev/null +++ b/src/commands/read/ReadGroceryList.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class ReadGroceryListCommand(Command): + def __init__(self, grocery_list_title, dc): + self.grocery_list_title = grocery_list_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('read_all_grocery_lists', (self.grocery_list_title, ))) diff --git a/src/commands/read/ReadIngredient.py b/src/commands/read/ReadIngredient.py new file mode 100644 index 0000000..e7f4460 --- /dev/null +++ b/src/commands/read/ReadIngredient.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class ReadIngredientCommand(Command): + def __init__(self, ingredient_title, dc): + self.ingredient_title = ingredient_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('read_ingredient', (self.ingredient_title, ))) diff --git a/src/commands/read/ReadRecipe.py b/src/commands/read/ReadRecipe.py new file mode 100644 index 0000000..8c2b61d --- /dev/null +++ b/src/commands/read/ReadRecipe.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class ReadRecipeCommand(Command): + def __init__(self, recipe_title, dc): + self.recipe_title = recipe_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('read_recipe', (self.recipe_title, ))) diff --git a/src/commands/read/ReadRecipeIngredients.py b/src/commands/read/ReadRecipeIngredients.py new file mode 100644 index 0000000..d46ffdf --- /dev/null +++ b/src/commands/read/ReadRecipeIngredients.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class ReadRecipeIngredientsCommand(Command): + def __init__(self, recipes, dc): + self.recipes = recipes + self.dc = dc + + def execute(self): + print(self.dc.procedure('get_ingredients_for_recipes', (self.recipes, ))) diff --git a/src/commands/read/ReadRecipeList.py b/src/commands/read/ReadRecipeList.py new file mode 100644 index 0000000..c99f82f --- /dev/null +++ b/src/commands/read/ReadRecipeList.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class ReadRecipeListCommand(Command): + def __init__(self, recipe_list_title, dc): + self.recipe_list_title = recipe_list_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('get_recipes_by_list_name', (self.recipe_list_title, ))) diff --git a/src/commands/read/ReadSteps.py b/src/commands/read/ReadSteps.py new file mode 100644 index 0000000..02a3388 --- /dev/null +++ b/src/commands/read/ReadSteps.py @@ -0,0 +1,9 @@ +from commands.Command import Command + +class ReadStepsCommand(Command): + def __init__(self, recipe_title, dc): + self.recipe_title = recipe_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('get_recipe_steps', (self.recipe_title, ))) diff --git a/src/commands/update/UpdateFavorite.py b/src/commands/update/UpdateFavorite.py new file mode 100644 index 0000000..42956ba --- /dev/null +++ b/src/commands/update/UpdateFavorite.py @@ -0,0 +1,12 @@ +from commands.Command import Command +from datetime import datetime + +class UpdateFavoriteCommand(Command): + def __init__(self, recipe_title, description, dc): + self.recipe_title = recipe_title + self.description = description + self.date = datetime.now().strftime('%Y-%m-%d') + self.dc = dc + + def execute(self): + print(self.dc.procedure('update_favorite', (self.recipe_title, self.description, self.date))) diff --git a/src/commands/update/UpdateGroceryList.py b/src/commands/update/UpdateGroceryList.py new file mode 100644 index 0000000..f800fc6 --- /dev/null +++ b/src/commands/update/UpdateGroceryList.py @@ -0,0 +1,10 @@ +from commands.Command import Command + +class UpdateGroceryListCommand(Command): + def __init__(self, ingredient_title, grocery_list_title, dc): + self.ingredient_title = ingredient_title + self.grocery_list_title = grocery_list_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_ingredient_to_grocery_list', (self.ingredient_title, self.grocery_list_title))) diff --git a/src/commands/update/UpdateRecipe.py b/src/commands/update/UpdateRecipe.py new file mode 100644 index 0000000..9fa6213 --- /dev/null +++ b/src/commands/update/UpdateRecipe.py @@ -0,0 +1,13 @@ +from datetime import datetime +from commands.Command import Command + +class UpdateRecipeCommand(Command): + def __init__(self, old_recipe_title, recipe_title, description, dc): + self.old_recipe = old_recipe_title + self.recipe_title = recipe_title + self.description = description + self.date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.dc = dc + + def execute(self): + print(self.dc.procedure('update_recipe', (self.old_recipe, self.recipe_title, self.description, self.date))) diff --git a/src/commands/update/UpdateRecipeInstruction.py b/src/commands/update/UpdateRecipeInstruction.py new file mode 100644 index 0000000..e708e95 --- /dev/null +++ b/src/commands/update/UpdateRecipeInstruction.py @@ -0,0 +1,13 @@ +from commands.Command import Command + +class UpdateRecipeInstructionCommand(Command): + def __init__(self, recipe_title, cook_time, prep_time, servings, calories, dc): + self.recipe_title = recipe_title + self.cook_time = cook_time + self.prep_time = prep_time + self.servings = servings + self.calories = calories + self.dc = dc + + def execute(self): + print(self.dc.procedure('update_instruction', (self.recipe_title, self.cook_time, self.prep_time, self.servings, self.calories))) diff --git a/src/commands/update/UpdateRecipeList.py b/src/commands/update/UpdateRecipeList.py new file mode 100644 index 0000000..6943649 --- /dev/null +++ b/src/commands/update/UpdateRecipeList.py @@ -0,0 +1,10 @@ +from commands.Command import Command + +class UpdateRecipeListCommand(Command): + def __init__(self, recipe_title, recipe_list_title, dc): + self.recipe_title = recipe_title + self.recipe_list_title = recipe_list_title + self.dc = dc + + def execute(self): + print(self.dc.procedure('add_recipe_to_list', (self.recipe_title, self.recipe_list_title))) diff --git a/src/commands/update/UpdateRecipeRating.py b/src/commands/update/UpdateRecipeRating.py new file mode 100644 index 0000000..632bd53 --- /dev/null +++ b/src/commands/update/UpdateRecipeRating.py @@ -0,0 +1,13 @@ +from datetime import datetime +from commands.Command import Command + +class UpdateRecipeRatingCommand(Command): + def __init__(self, recipe_title, score, description, dc): + self.recipe_title = recipe_title + self.score = score + self.description = description + self.date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.dc = dc + + def execute(self): + print(self.dc.procedure('update_rating', (self.recipe_title, self.score, self.description, self.date))) diff --git a/src/commands/update/UpdateRecipeStep.py b/src/commands/update/UpdateRecipeStep.py new file mode 100644 index 0000000..50980ed --- /dev/null +++ b/src/commands/update/UpdateRecipeStep.py @@ -0,0 +1,11 @@ +from commands.Command import Command + +class UpdateRecipeStepCommand(Command): + def __init__(self, recipe_title, step_id, description, dc): + self.recipe_title = recipe_title + self.step_id = step_id + self.description = description + self.dc = dc + + def execute(self): + print(self.dc.procedure('update_step', (self.recipe_title, self.step_id, self.description))) diff --git a/src/controller/__init__.py b/src/controller/__init__.py index 8f01b51..abde30b 100644 --- a/src/controller/__init__.py +++ b/src/controller/__init__.py @@ -1,7 +1,7 @@ # src/controller/__init__.py # Import controllers -from .dataController import DataController +from .dataControllerv2 import DataController2 as DataController # from .appController import AppController # from .listController import ListControler diff --git a/src/controller/appController.py b/src/controller/appController.py index e69de29..2e46979 100644 --- a/src/controller/appController.py +++ b/src/controller/appController.py @@ -0,0 +1,238 @@ +import logging + +from commands.create.CreateRecipe import CreateRecipeCommand +from commands.create.CreateRecipeIngredient import CreateRecipeIngredientCommand +from commands.create.CreateRecipeRating import CreateRecipeRatingCommand +from commands.create.CreateRecipeStep import CreateRecipeStepCommand +from commands.create.CreateRecipeInstruction import CreateRecipeInstructionCommand +from commands.create.CreateRecipeList import CreateRecipeListCommand +from commands.create.CreateGroceryList import CreateGroceryListCommand +from commands.read.ReadAllIngredients import ReadAllIngredientsCommand +from commands.read.ReadAllRecipes import ReadAllRecipesCommand +from commands.read.ReadAllGroceryLists import ReadAllGroceryListsCommand +from commands.read.ReadFavorites import ReadFavoritesCommand +from commands.read.ReadIngredient import ReadIngredientCommand +from commands.read.ReadRecipe import ReadRecipeCommand +from commands.read.ReadRecipeIngredients import ReadRecipeIngredientsCommand +from commands.read.ReadRecipeList import ReadRecipeListCommand +from commands.read.ReadSteps import ReadStepsCommand +from commands.read.ReadGroceryList import ReadGroceryListCommand +from commands.update.UpdateRecipe import UpdateRecipeCommand +from commands.update.UpdateFavorite import UpdateFavoriteCommand +from commands.update.UpdateRecipeInstruction import UpdateRecipeInstructionCommand +from commands.update.UpdateRecipeRating import UpdateRecipeRatingCommand +from commands.update.UpdateRecipeStep import UpdateRecipeStepCommand +from commands.delete.DeleteRecipe import DeleteRecipeCommand +from commands.delete.DeleteFavorite import DeleteFavoriteCommand +from commands.delete.DeleteRecipeList import DeleteRecipeListCommand +from commands.delete.DeleteGroceryList import DeleteGroceryListCommand + +logging.basicConfig() + + +class AppController: + def __init__(self, dc): + self.dc = dc + self._create_services_ = ['Add Recipe', 'Add Ingredient to Recipe', 'Add Rating', 'Add Step', 'Add Instruction', 'Add Recipe List', 'Add Grocery List'] + self._read_services_ = ['All Recipes', 'All Ingredients', 'Recipe', 'Ingredient', 'Recipe List', 'All Grocery Lists', 'Grocery List', 'Recipe Steps', 'Favorites', 'Recipe/s Ingredients'] + self._update_services_ = ['update_recipe', 'update_rating', 'update_instruction', 'update_step', 'update_favorite'] + self._delete_services_ = ['delete_recipe', 'delete_recipe_list', 'delete_grocery_list', 'delete_recipe_from_favorites'] + self.dc.connect() + + def start(self): + print("Select Service:\n1. Create\n2. Read\n3. Update\n4. Delete\n5. Quit") + service = input("Service ID: ") + self._handle_service_(service) + + def _handle_service_(self, service): + if int(service) == 1: # create + self.create() + elif int(service) == 2: # read + self.read() + elif int(service) == 3: # update + self.update() + elif int(service) == 4: # delete + self.delete() + elif int(service) == 5: + quit() + else: + logging.error("Invalid Input") + self.start() + + def create(self): + print("Select CREATE Service") + for index, service in enumerate(self._create_services_): + print(f'{index+1}. {service}') + c_service = input("CREATE ID: ") + self._handle_create_service_(c_service) + + def _handle_create_service_(self, service): + iService = int(service) + if iService == 1: # recipe + recipe_title = input("Recipe Title: ") + desc = input("Description: ") + command = CreateRecipeCommand(recipe_title, desc, self.dc) + command.execute() + elif iService == 2: # ingredient to recipe + recipe_title = input("Recipe Title: ") + num_ingredients = int(input("Number of ingredients to add: ")) + for _ in range(0, num_ingredients): + ingredient = input("Ingredient: ") + quantity = input("Quantity: ") + command = CreateRecipeIngredientCommand(ingredient, recipe_title, quantity, self.dc) + command.execute() + elif iService == 3: # rating + recipe_title = input("Recipe Title: ") + score = int(input("Score (int): ")) + desc = input("Description: ") + command = CreateRecipeRatingCommand(recipe_title, score, desc, self.dc) + elif iService == 4: # step + recipe_title = input("Recipe Title: ") + num_steps = int(input("Number of steps to add: ")) + for index in range(1, num_steps+1): + step_id = index + desc = input("Description: ") + command = CreateRecipeStepCommand(recipe_title, step_id, desc) + command.execute() + elif iService == 5: # instruction + recipe_title = input("Recipe Title: ") + cook_time = input("Cook Time: ") + prep_time = input("Prep Time: ") + servings = int(input("Servings: ")) | 0 + calories = int(input("Calories: ")) | 0 + command = CreateRecipeInstructionCommand(recipe_title, cook_time, prep_time, servings, calories, self.dc) + command.execute() + elif iService == 6: # recipe list + rl_name = input("Recipe List Name: ") + desc = input("Description: ") + command = CreateRecipeListCommand(rl_name, desc, self.dc) + command.execute() + elif iService == 7: # grocery list + gl_name = input("Grocery List Name: ") + desc = input("Description: ") + command = CreateGroceryListCommand(gl_name, desc, self.dc) + command.execute() + else: + logging.error("Invalid Input") + self.create() + + def read(self): + print("Select READ Service") + for index, service in enumerate(self._read_services_): + print(f'{index+1}. {service}') + serv = input("READ ID: ") + self._handle_read_service_(serv) + + def _handle_read_service_(self, service): + iService = int(service) + if iService == 1: # all recipes + command = ReadAllRecipesCommand(self.dc) + command.execute() + elif iService == 2: # all ingredients + command = ReadAllIngredientsCommand(self.dc) + command.execute() + elif iService == 3: # recipe + recipe_title = input("Recipe Title: ") + command = ReadRecipeCommand(recipe_title, self.dc) + command.execute() + elif iService == 4: # ingredient + ingredient = input("Ingredient: ") + command = ReadIngredientCommand(ingredient, self.dc) + command.execute() + elif iService == 5: # recipe list + rl_name = input("Recipe List Name: ") + command = ReadRecipeListCommand(rl_name, self.dc) + command.execute() + elif iService == 6: # all grocery lists + command = ReadAllGroceryListsCommand(self.dc) + command.execute() + elif iService == 7: # grocery list + gl_name = input("Grocery List: ") + command = ReadGroceryListCommand(gl_name, self.dc) + command.execute() + elif iService == 8: # steps + recipe = input("Recipe Title: ") + command = ReadStepsCommand(recipe, self.dc) + elif iService == 9: # favorites + command = ReadFavoritesCommand(self.dc) + command.execute() + elif iService == 10: # recipe/s to ingredient list + recipes = input("Recipe/s (comma separated): ") + command = ReadRecipeIngredientsCommand(recipes, self.dc) + command.execute() + else: + logging.error("Invalid Input") + self.create() + + def update(self): + print("Select UPDATE Service") + for index, service in enumerate(self._update_services_): + print(f'{index+1}. {service}') + u_service = input("UPDATE ID: ") + self._handle_update_service_(u_service) + + def _handle_update_service_(self, service): + iService = int(service) + if iService == 1: + old_recipe_title = input("Old Recipe Title: ") + new_recipe_title = input("New Recipe Title: ") + new_recipe_description = input("New Description: ") + command = UpdateRecipeCommand(old_recipe_title, new_recipe_title, new_recipe_description, self.dc) + command.execute() + elif iService == 2: + recipe_title = input("Recipe Title: ") + new_score = input("New Score: ") + new_rating_description = input("New Description: ") + command = UpdateRecipeRatingCommand(recipe_title, new_score, new_rating_description, self.dc) + command.execute() + elif iService == 3: + recipe_title = input("Recipe Title: ") + new_cook_time = input("New Cook Time: ") + new_prep_time = input("New Prep Time: ") + new_servings = input("New Servings: ") + new_calories = input("New Calories: ") + command = UpdateRecipeInstructionCommand(recipe_title, new_cook_time, new_prep_time, new_servings, new_calories, self.dc) + command.execute() + elif iService == 4: + recipe_title = input("Recipe Title: ") + step_id = input("Step ID: ") + new_step_description = input("New Description: ") + command = UpdateRecipeStepCommand(recipe_title, step_id, new_step_description, self.dc) + command.execute() + elif iService == 5: + recipe_title = input("Recipe Title: ") + new_description = input("Description: ") + command = UpdateFavoriteCommand(recipe_title, new_description, self.dc) + command.execute() + else: + logging.error("Invalid Input") + self.update() + + def delete(self): + print("Select DELETE Service") + for index, service in enumerate(self._delete_services_): + print(f'{index+1}. {service}') + d_service = input("DELETE ID: ") + self._handle_delete_service_(d_service) + + def _handle_delete_service_(self, service): + iService = int(service) + if iService == 1: + del_recipe_title = input("Recipe Title: ") + deleteRecipeCommand = DeleteRecipeCommand(del_recipe_title, self.dc) + deleteRecipeCommand.execute() + elif iService == 2: + list_name = input("Recipe List Name: ") + deleteRecipeCommand = DeleteRecipeListCommand(list_name, self.dc) + deleteRecipeCommand.execute() + elif iService == 3: + list_name = input("Recipe List Name: ") + deleteRecipeCommand = DeleteGroceryListCommand(list_name, self.dc) + deleteRecipeCommand.execute() + elif iService == 4: + del_recipe_title = input("Recipe Title: ") + deleteRecipeCommand = DeleteFavoriteCommand(del_recipe_title, self.dc) + deleteRecipeCommand.execute() + else: + logging.error("Invalid Input") + self.delete() diff --git a/src/controller/dataControllerv2.py b/src/controller/dataControllerv2.py index ef0e602..ea6de81 100644 --- a/src/controller/dataControllerv2.py +++ b/src/controller/dataControllerv2.py @@ -1,29 +1,34 @@ from mysql.connector import connect, Error -import getpass import logging +import os import time from model.enums.scripts import InsertScripts, SelectScripts from model.enums.tables import Tables -PASS = 'YOUR_PASSWORD_HERE' -USER = 'root' -HOST = 'localhost' -DB = 'lazygrocer' +db_config = { + 'host': os.getenv('MYSQL_HOST', 'db'), + 'port': int(os.getenv('MYSQL_PORT', 3306)), + 'user': os.getenv('MYSQL_USER', 'root'), + 'password': os.getenv('MYSQL_PASSWORD', 'lg-db-pd'), + 'database': os.getenv('MYSQL_DATABASE', 'lazygrocer'), +} class DataController2: - def __init__(self, database=DB, host=HOST, user=USER, password=PASS): + def __init__(self, database=db_config.get("database"), host=db_config.get("host"), user=db_config.get("user"), password=db_config.get("password")): self.host = host self.user = user self.password = password self.database = database self.connection = None self.cursor = None - self._init_database_() def clean(self): if not self._is_connected_(): - self.connect() + try: + self.connect() + except Error as e: + logging.error(f"Error connecting to {self.database}:", e) try: self.cursor.execute(f"DROP DATABASE IF EXISTS {self.database};") self.connection.commit() @@ -90,6 +95,7 @@ def fetch(self, query: SelectScripts, params: tuple=()): self.cursor.execute(query.script, params) else: self.cursor.execute(query.script) + self.connection.commit() return self.cursor.fetchall() except Error as e: logging.error("Error fetching data: %s", e) @@ -109,6 +115,28 @@ def read(self, script="init"): except Error as e: logging.error("Error executing %s: %s", script, e) + def procedure(self, procedure_name, args=None): + if not self._is_connected_(): + logging.debug("No active connection") + return None + else: + try: + if args is None: + self.cursor.callproc(procedure_name) + else: + self.cursor.callproc(procedure_name, args) + # Fetch output parameters + results = [] + for result in self.cursor.stored_results(): + results.append(result.fetchall()) + + self.connection.commit() # Commit changes (if any) + logging.debug("Procedure %s called successfully", procedure_name) + return results + except Error as e: + logging.error("Error executing procedure %s: %s", procedure_name, e) + return None + def _get_tables_(self): if not self._is_connected_(): self.connect() diff --git a/src/main.py b/src/main.py index caf083d..bb6c70d 100644 --- a/src/main.py +++ b/src/main.py @@ -1,10 +1,13 @@ import logging import argparse -from controller.dataController import DataController as DataController +from controller.dataControllerv2 import DataController2 as DataController +from controller.appController import AppController def main(db): - print(type(db)) + ac = AppController(db) + while True: + ac.start() if __name__ == '__main__': parser = argparse.ArgumentParser(description='Run unit tests with different logging levels') @@ -25,5 +28,5 @@ def main(db): else: logging.basicConfig(level=logging.INFO) - db = DataController() + db = DataController('lazygrocer') main(db) diff --git a/src/model/chef.py b/src/model/chef.py deleted file mode 100644 index 4b3f849..0000000 --- a/src/model/chef.py +++ /dev/null @@ -1,40 +0,0 @@ -from controller.dataControllerv2 import DataController2 as DataController - -USER='default' -DB='lazygrocer' - -class Chef: - def __init__(self, dc: DataController, username: str=USER, database: str=DB): - self.dc = dc - self.username = username - self.database = database - - # favorites - def read_favorites(self): - pass - - # recipe list - def create_recipe_list(self, name: str, desc: str='', recipes: tuple=()): - pass - - def read_recipe_list(self, name: str): - pass - - def update_recipe_list(self, name: str, desc: str='', recipes: tuple=()): - pass - - def delete_recipe_list(self, name: str): - pass - - # ingredient list - def create_ingredient_list(): - pass - - def read_ingredient_list(): - pass - - def update_ingredient_list(): - pass - - def delete_ingredient_list(): - pass \ No newline at end of file diff --git a/src/model/enums/tables.py b/src/model/enums/tables.py index 2b66ecf..59c23cf 100644 --- a/src/model/enums/tables.py +++ b/src/model/enums/tables.py @@ -2,10 +2,13 @@ class Tables(Enum): RECIPE = 'Recipe' + RATING = 'Rating' + FAVORITE = 'Favorite' RECIPELIST = 'RecipeList' INSTRUCTION = 'Instruction' + STEPS = 'Steps' INGREDIENT = 'Ingredient' - INGREDIENTLIST = 'IngredientList' + GROCERYLIST = 'GroceryList' RINCLUDESI = 'RincludesI' RINRL = 'RinRL' ILFORI = 'ILforI' diff --git a/src/scripts/.lazygrocer.sql b/src/scripts/.lazygrocer.sql new file mode 100644 index 0000000..96bdcf6 --- /dev/null +++ b/src/scripts/.lazygrocer.sql @@ -0,0 +1,313 @@ +-- MySQL dump 10.13 Distrib 8.0.36, for macos14 (x86_64) +-- +-- Host: 127.0.0.1 Database: lazygrocer +-- ------------------------------------------------------ +-- Server version 8.3.0 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `Favorite` +-- + +DROP TABLE IF EXISTS `Favorite`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `Favorite` ( + `recipe_title` varchar(50) DEFAULT NULL, + `date_added` date DEFAULT NULL, + `description` text, + KEY `recipe_title` (`recipe_title`), + CONSTRAINT `favorite_ibfk_1` FOREIGN KEY (`recipe_title`) REFERENCES `Recipe` (`title`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `Favorite` +-- + +LOCK TABLES `Favorite` WRITE; +/*!40000 ALTER TABLE `Favorite` DISABLE KEYS */; +INSERT INTO `Favorite` VALUES ('Pizza','2024-04-17','Its grown on me'),('Mac','2024-04-17','Incredible dish, would recommend'); +/*!40000 ALTER TABLE `Favorite` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `GroceryList` +-- + +DROP TABLE IF EXISTS `GroceryList`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `GroceryList` ( + `name` varchar(50) NOT NULL, + `description` text, + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `GroceryList` +-- + +LOCK TABLES `GroceryList` WRITE; +/*!40000 ALTER TABLE `GroceryList` DISABLE KEYS */; +INSERT INTO `GroceryList` VALUES ('Busy Week','For when time is running low'),('GL2','For pizza'); +/*!40000 ALTER TABLE `GroceryList` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ILforI` +-- + +DROP TABLE IF EXISTS `ILforI`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ILforI` ( + `grocery_list_name` varchar(50) DEFAULT NULL, + `ingredient_name` varchar(50) DEFAULT NULL, + KEY `grocery_list_name` (`grocery_list_name`), + KEY `ingredient_name` (`ingredient_name`), + CONSTRAINT `ilfori_ibfk_1` FOREIGN KEY (`grocery_list_name`) REFERENCES `GroceryList` (`name`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `ilfori_ibfk_2` FOREIGN KEY (`ingredient_name`) REFERENCES `Ingredient` (`name`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ILforI` +-- + +LOCK TABLES `ILforI` WRITE; +/*!40000 ALTER TABLE `ILforI` DISABLE KEYS */; +INSERT INTO `ILforI` VALUES ('GL2','Dough'),('GL2','Mozzarella Cheese'),('Busy Week','Parmesan Cheese'); +/*!40000 ALTER TABLE `ILforI` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `Ingredient` +-- + +DROP TABLE IF EXISTS `Ingredient`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `Ingredient` ( + `name` varchar(50) NOT NULL, + `last_added` date DEFAULT NULL, + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `Ingredient` +-- + +LOCK TABLES `Ingredient` WRITE; +/*!40000 ALTER TABLE `Ingredient` DISABLE KEYS */; +INSERT INTO `Ingredient` VALUES ('Ambrosia','2024-04-17'),('Black Pepper','2024-04-17'),('Cheddar Cheese','2024-04-17'),('Chicken','2024-04-17'),('Dough','2024-04-17'),('garlic','2024-04-18'),('Mozzarella Cheese','2024-04-17'),('olive oil','2024-04-18'),('Parmesan Cheese','2024-04-17'),('Pasta','2024-04-17'),('Pizza Sauce','2024-04-17'),('Spaghetti','2024-04-17'); +/*!40000 ALTER TABLE `Ingredient` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `Instruction` +-- + +DROP TABLE IF EXISTS `Instruction`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `Instruction` ( + `recipe_title` varchar(50) NOT NULL, + `cook_time` varchar(50) DEFAULT NULL, + `prep_time` varchar(50) DEFAULT NULL, + `servings` int DEFAULT NULL, + `calories` int DEFAULT NULL, + PRIMARY KEY (`recipe_title`), + CONSTRAINT `instruction_ibfk_1` FOREIGN KEY (`recipe_title`) REFERENCES `Recipe` (`title`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `Instruction` +-- + +LOCK TABLES `Instruction` WRITE; +/*!40000 ALTER TABLE `Instruction` DISABLE KEYS */; +INSERT INTO `Instruction` VALUES ('Chicken Parm','20 mins','15 mins',3,1000),('Mac','12 min','5 min',5,1500),('Pizza','25 mins','10',3,1000); +/*!40000 ALTER TABLE `Instruction` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `Rating` +-- + +DROP TABLE IF EXISTS `Rating`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `Rating` ( + `recipe_title` varchar(50) DEFAULT NULL, + `score` int DEFAULT NULL, + `description` text, + `date_added` date DEFAULT NULL, + KEY `recipe_title` (`recipe_title`), + CONSTRAINT `rating_ibfk_1` FOREIGN KEY (`recipe_title`) REFERENCES `Recipe` (`title`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `Rating` +-- + +LOCK TABLES `Rating` WRITE; +/*!40000 ALTER TABLE `Rating` DISABLE KEYS */; +INSERT INTO `Rating` VALUES ('Chicken Parm',5,'Best meal','2024-04-16'),('Pizza',3,'Bad for me','2024-04-17'),('Mac',5,'Greater recipe!','2024-04-17'); +/*!40000 ALTER TABLE `Rating` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `Recipe` +-- + +DROP TABLE IF EXISTS `Recipe`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `Recipe` ( + `title` varchar(50) NOT NULL, + `description` text, + `date_published` date DEFAULT NULL, + PRIMARY KEY (`title`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `Recipe` +-- + +LOCK TABLES `Recipe` WRITE; +/*!40000 ALTER TABLE `Recipe` DISABLE KEYS */; +INSERT INTO `Recipe` VALUES ('Chicken Parm','So good!','2024-04-16'),('Mac','Cheesier and delicious!','2024-04-17'),('Pizza','Cheat Meal','2024-04-17'); +/*!40000 ALTER TABLE `Recipe` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `RecipeList` +-- + +DROP TABLE IF EXISTS `RecipeList`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `RecipeList` ( + `name` varchar(50) NOT NULL, + `description` text, + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `RecipeList` +-- + +LOCK TABLES `RecipeList` WRITE; +/*!40000 ALTER TABLE `RecipeList` DISABLE KEYS */; +INSERT INTO `RecipeList` VALUES ('Pastas','For the italians'),('Uno','Healthy foods!'); +/*!40000 ALTER TABLE `RecipeList` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `RincludesI` +-- + +DROP TABLE IF EXISTS `RincludesI`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `RincludesI` ( + `recipe_title` varchar(50) DEFAULT NULL, + `ingredient_name` varchar(50) DEFAULT NULL, + `quantity` varchar(50) DEFAULT NULL, + KEY `recipe_title` (`recipe_title`), + KEY `ingredient_name` (`ingredient_name`), + CONSTRAINT `rincludesi_ibfk_1` FOREIGN KEY (`recipe_title`) REFERENCES `Recipe` (`title`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `rincludesi_ibfk_2` FOREIGN KEY (`ingredient_name`) REFERENCES `Ingredient` (`name`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `RincludesI` +-- + +LOCK TABLES `RincludesI` WRITE; +/*!40000 ALTER TABLE `RincludesI` DISABLE KEYS */; +INSERT INTO `RincludesI` VALUES ('Chicken Parm','Chicken','2 Breasts'),('Chicken Parm','Parmesan Cheese','0.5 lbs'),('Pizza','Dough','1 Bag'),('Pizza','Pizza Sauce','1 Jar'),('Pizza','Mozzarella Cheese','1 lb'),('Mac','Spaghetti','Half a package'),('Mac','Parmesan Cheese','1 cup'),('Mac','Black Pepper','1 teaspoon'); +/*!40000 ALTER TABLE `RincludesI` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `RinRL` +-- + +DROP TABLE IF EXISTS `RinRL`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `RinRL` ( + `recipe_title` varchar(50) DEFAULT NULL, + `recipe_list_name` varchar(50) DEFAULT NULL, + KEY `recipe_title` (`recipe_title`), + KEY `recipe_list_name` (`recipe_list_name`), + CONSTRAINT `rinrl_ibfk_1` FOREIGN KEY (`recipe_title`) REFERENCES `Recipe` (`title`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `rinrl_ibfk_2` FOREIGN KEY (`recipe_list_name`) REFERENCES `RecipeList` (`name`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `RinRL` +-- + +LOCK TABLES `RinRL` WRITE; +/*!40000 ALTER TABLE `RinRL` DISABLE KEYS */; +INSERT INTO `RinRL` VALUES ('Chicken Parm','Uno'),('Mac','Pastas'); +/*!40000 ALTER TABLE `RinRL` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `Steps` +-- + +DROP TABLE IF EXISTS `Steps`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `Steps` ( + `recipe_title` varchar(50) DEFAULT NULL, + `id` int DEFAULT NULL, + `description` text, + KEY `recipe_title` (`recipe_title`), + CONSTRAINT `steps_ibfk_1` FOREIGN KEY (`recipe_title`) REFERENCES `Recipe` (`title`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `Steps` +-- + +LOCK TABLES `Steps` WRITE; +/*!40000 ALTER TABLE `Steps` DISABLE KEYS */; +INSERT INTO `Steps` VALUES ('Chicken Parm',1,'Cook chicken'),('Chicken Parm',2,'Cook sauce'),('Chicken Parm',3,'Melt cheese on top'),('Pizza',1,'Spread dough'),('Pizza',2,'Add toppings'),('Pizza',3,'Put in oven'),('Mac',1,'Bring water to boil.'),('Mac',2,'Add pasta.'); +/*!40000 ALTER TABLE `Steps` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2024-04-18 0:10:10 diff --git a/src/scripts/init.sql b/src/scripts/init.sql index c5629b3..dc1023b 100644 --- a/src/scripts/init.sql +++ b/src/scripts/init.sql @@ -1,4 +1,5 @@ -CREATE DATABASE lazygrocer; +DROP DATABASE IF EXISTS lazygrocer; +CREATE DATABASE IF NOT EXISTS lazygrocer; use lazygrocer; -- tables @@ -509,7 +510,7 @@ DELIMITER ; DELIMITER // CREATE PROCEDURE update_rating( - IN recipe_title VARCHAR(50), + IN r_title VARCHAR(50), IN new_score INTEGER, IN new_rating_description TEXT, IN new_date_added DATE @@ -519,7 +520,7 @@ BEGIN SET score = new_score, description = new_rating_description, date_added = new_date_added - WHERE recipe_title = recipe_title; + WHERE recipe_title = r_title; END // DELIMITER ; @@ -529,7 +530,7 @@ DELIMITER ; DELIMITER // CREATE PROCEDURE update_instruction( - IN recipe_title VARCHAR(50), + IN r_title VARCHAR(50), IN new_cook_time VARCHAR(50), IN new_prep_time VARCHAR(50), IN new_servings INTEGER, @@ -541,7 +542,7 @@ BEGIN prep_time = new_prep_time, servings = new_servings, calories = new_calories - WHERE recipe_title = recipe_title; + WHERE recipe_title = r_title; END // DELIMITER ; @@ -551,14 +552,14 @@ DELIMITER ; DELIMITER // CREATE PROCEDURE update_step( - IN recipe_title VARCHAR(50), + IN r_title VARCHAR(50), IN step_id INTEGER, IN new_step_description TEXT ) BEGIN UPDATE Steps SET description = new_step_description - WHERE recipe_title = recipe_title AND id = step_id; + WHERE recipe_title = r_title AND id = step_id; END // DELIMITER ; @@ -568,14 +569,14 @@ DELIMITER ; DELIMITER // CREATE PROCEDURE update_favorite( - IN recipe_title VARCHAR(50), + IN r_title VARCHAR(50), IN new_date_added DATE, IN new_description TEXT ) BEGIN -- Insert the recipe into the Favorite table INSERT INTO Favorite (recipe_title, date_added, description) - VALUES (recipe_title, new_date_added, new_description); + VALUES (r_title, new_date_added, new_description); END // DELIMITER ; @@ -649,7 +650,8 @@ END // DELIMITER ; -/* + + CALL add_recipe('Mac & Cheese', 'Cheesy and delicious!', '2024-04-15'); CALL add_recipe('Chicken Parm', 'So good!', '2024-04-16'); CALL add_recipe('Pizza', 'Cheat Meal', '2024-04-17'); @@ -701,7 +703,7 @@ CALL delete_grocery_list('GL1'); CALL delete_recipe_from_favorites('Mac & Cheese v2'); CALL read_all_recipes(); -CALL read_recipe('Mac & Cheese'); +CALL read_recipe('Mac & Cheese v2'); CALL read_recipe('Chicken Parm'); CALL read_recipe('Pizza'); CALL read_all_recipe_lists(); @@ -713,7 +715,7 @@ CALL read_grocery_list('GL2'); CALL read_all_ingredients(); CALL read_ingredient('Cheddar Cheese'); CALL read_ingredient('Dough'); -CALL get_recipe_steps('Mac & Cheese'); +CALL get_recipe_steps('Mac & Cheese v2'); CALL get_recipe_steps('Chicken Parm'); CALL get_recipe_steps('Pizza'); CALL get_all_favorites(); @@ -722,4 +724,3 @@ CALL get_recipes_by_list_name('Dos'); CALL get_ingredients_for_recipes('Mac & Cheese,Pizza,Chicken Parm'); CALL get_ingredients_for_recipes('Mac & Cheese,Chicken Parm'); CALL get_ingredients_for_recipes('Mac & Cheese'); -*/ diff --git a/src/testing/test_data_inserts.py b/src/testing/test_data_inserts.py index bc7b387..0f0e865 100644 --- a/src/testing/test_data_inserts.py +++ b/src/testing/test_data_inserts.py @@ -5,73 +5,94 @@ from controller.dataControllerv2 import DataController2 as DataController from model.enums.scripts import InsertScripts -TESTDB = 'testgrocer' +TESTDB = 'lazygrocer' class TestDataInserts(unittest.TestCase): @classmethod def setUpClass(self): self.dc = DataController(TESTDB) - self.dc.clean() self.dc.connect() + self.date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.ingredients = [ - ('Spaghetti', "Half a package", '2024-03-05'), - ('Bacon', "3 strips", '2024-03-05'), - ('Eggs', "3 whole", '2024-03-05'), - ('Parmesan Cheese', "1 cup", '2024-03-05'), - ('Black Pepper', "1 teaspoon", '2024-03-05') + ('Spaghetti', 'Mac', "Half a package", self.date), + ('Parmesan Cheese', 'Mac', "1 cup", self.date), + ('Black Pepper', 'Mac', "1 teaspoon", self.date) ] - def date(date_string): - try: - date_obj = datetime.strptime(date_string, '%Y-%m-%d').date() - return (date_obj,) - except ValueError: - logging.error("Invalid date format. Please provide a date in the format YYYY-MM-DD.") - return None + def test_Ainsert_recipe(self): + recipe_data = ('Mac', 'Cheesy and delicious!', self.date) + result = self.dc.procedure('add_recipe', recipe_data) + assert result != -1 + def test_Binsert_rating(self): + rating_data = ('Mac', 4, 'Great recipe!', self.date) + result = self.dc.procedure('add_rating', rating_data) + assert result != -1 - def test_Ainsert_recipe(self): - recipe_data = ('Pasta Carbonara', 'Classic Italian pasta dish with bacon, eggs, and cheese sauce', 4, 'DINNER', True, '2024-03-05') - result = self.dc.execute(InsertScripts.RECIPE, recipe_data) + def test_Cinsert_step(self): + step_data = [('Mac', 1, 'Bring water to boil.'), ('Mac', 2, 'Add pasta.')] + result = [] + for step in step_data: + result.append(self.dc.procedure('add_step', step)) + assert result != -1 + + def test_Dinsert_instruction(self): + inst_data = ('Mac', '10 min', '5 min', 4, 1200) + result = self.dc.procedure('add_instruction', inst_data) + assert result != -1 + + def test_Einsert_recipe_list(self): + rl_data = ('Pastas', 'For the italians') + result = self.dc.procedure('add_recipe_list', rl_data) + assert result != -1 + + def test_Finsert_recipe_to_list(self): + rl_data = ('Mac', 'Pastas') + result = self.dc.procedure('add_recipe_to_list', rl_data) assert result != -1 - def test_Binsert_ingredient(self): + def test_Ginsert_grocery_list(self): + gl_data = ('Busy Week', 'For when time is running low') + result = self.dc.procedure('add_grocery_list', gl_data) + assert result != -1 + + def test_Hinsert_ingredient(self): for ingredient in self.ingredients: - result = self.dc.execute(InsertScripts.INGREDIENT, ingredient) + result = self.dc.procedure('add_ingredient_to_recipe', ingredient) assert result != -1 logging.debug("Ingredient added: %s", str(ingredient[0])) - def test_Cinsert_instruction(self): - instruction_data = ('Pasta Carbonara', 20, 4, 500, '1. Cook pasta. 2. Fry bacon. 3. Mix eggs and cheese sauce.', 'https://www.allrecipes.com/recipe/11973/spaghetti-carbonara-ii/') - result = self.dc.execute(InsertScripts.INSTRUCTION, instruction_data) + def test_Iinsert_ingredient_to_list(self): + gl_data = ('Parmesan Cheese', 'Busy Week') + result = self.dc.procedure('add_ingredient_to_grocery_list', gl_data) assert result != -1 - def test_Dinsert_recipe_list(self): - recipe_list_data = ('Italian', 'List of italian recipes') - result = self.dc.execute(InsertScripts.RECIPELIST, recipe_list_data) + + def test_Jupdate_recipe(self): + recipe_data = ('Mac', 'Mac', 'Cheesier and delicious!', self.date) + result = self.dc.procedure('update_recipe', recipe_data) assert result != -1 - def test_Einsert_ingredient_list(self): - ingredient_list_data = ('Essentials', 'List of essential ingredients') - result = self.dc.execute(InsertScripts.INGREDIENTLIST, ingredient_list_data) + def test_Kupdate_rating(self): + rating_data = ('Mac', 5, 'Greater recipe!', self.date) + result = self.dc.procedure('update_rating', rating_data) assert result != -1 - def test_Finsert_r_includes_i(self): - for data in self.ingredients: - result = self.dc.execute(InsertScripts.RINCLUDESI, ('Pasta Carbonara', data[0], data[1])) - assert result != -1 - - def test_Ginsert_r_in_rl(self): - rl_contains_r_data = ('Pasta Carbonara', 'Italian') - result = self.dc.execute(InsertScripts.RINRL, rl_contains_r_data) + def test_Lupdate_instruction(self): + inst_data = ('Mac', '12 min', '5 min', 5, 1500) + result = self.dc.procedure('update_instruction', inst_data) + assert result != -1 + + def test_Mupdate_step(self): + step_data = ('Mac', 1, 'Bring water to boil.') + result = self.dc.procedure('update_step', step_data) assert result != -1 - def test_Hinsert_il_for_i(self): - il_for_i_data = ('Essentials', 'Spaghetti') - result = self.dc.execute(InsertScripts.ILFORI, il_for_i_data) + def test_Nupdate_favorite(self): + fav = ('Mac', self.date, 'Incredible dish, would recommend') + result = self.dc.procedure('update_favorite', fav) assert result != -1 @classmethod def tearDownClass(self): self.dc.disconnect() - diff --git a/src/testing/test_data_selects.py b/src/testing/test_data_selects.py index 416d56b..c377669 100644 --- a/src/testing/test_data_selects.py +++ b/src/testing/test_data_selects.py @@ -1,29 +1,32 @@ import unittest import logging -from datetime import datetime +import datetime from controller.dataControllerv2 import DataController2 as DataController from model.enums.scripts import SelectScripts -TESTDB = 'testgrocer' +TESTDB = 'lazygrocer' class TestDataSelects(unittest.TestCase): @classmethod def setUpClass(self): self.ingredients = [ - ('Spaghetti', "Half a package", '2024-03-05'), - ('Bacon', "3 strips", '2024-03-05'), - ('Eggs', "3 whole", '2024-03-05'), - ('Parmesan Cheese', "1 cup", '2024-03-05'), - ('Black Pepper', "1 teaspoon", '2024-03-05')] + ('Spaghetti', 'Mac', "Half a package"), + ('Parmesan Cheese', 'Mac', "1 cup"), + ('Black Pepper', 'Mac', "1 teaspoon") + ] self.dc = DataController(TESTDB) self.dc.connect() def test_select_recipe(self): - result = self.dc.fetch(SelectScripts.RECIPE) + result = self.dc.procedure('read_recipe', ('Mac', )) logging.debug(result) - assert result == [('Pasta Carbonara', 'Classic Italian pasta dish with bacon, eggs, and cheese sauce', 4, 'DINNER', True, '2024-03-05')] - + assert result == [[('Mac', 'Cheesier and delicious!', datetime.date(2024, 4, 17), 5, 'Greater recipe!', '12 min', '5 min', 5, 1500)]] + + @classmethod + def tearDownClass(self): + self.dc.disconnect() +''' def test_select_ingredient(self): result = self.dc.fetch(SelectScripts.INGREDIENT) assert sorted(result) == sorted(self.ingredients) @@ -57,7 +60,4 @@ def test_select_il_for_i(self): result = self.dc.fetch(SelectScripts.ILFORI) logging.debug(result) assert result == [('Essentials', 'Spaghetti')] - - @classmethod - def tearDownClass(self): - self.dc.disconnect() +''' diff --git a/src/view/__init__.py b/src/view/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/chefView.py b/src/view/chefView.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/ingredientView.py b/src/view/ingredientView.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/listIngredientView.py b/src/view/listIngredientView.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/listRecipeView.py b/src/view/listRecipeView.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/loginView.py b/src/view/loginView.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/recipeView.py b/src/view/recipeView.py deleted file mode 100644 index e69de29..0000000 diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..e784f0f --- /dev/null +++ b/start.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Function to show usage +show_usage() { + echo "Usage: $0 [-i]" + echo " -i Run the application in interactive mode" + exit 1 +} + +# Parse command-line options +INTERACTIVE_MODE=false +while getopts "i" opt; do + case ${opt} in + i ) + INTERACTIVE_MODE=true + ;; + * ) + show_usage + ;; + esac +done + +# Stop and remove any existing containers +echo "Stopping and removing existing containers..." +docker compose down + +# Build the Docker images +echo "Building Docker images..." +docker compose build | tee build.log +echo "Docker images built." + +# Start up the application stack +if [ "$INTERACTIVE_MODE" = true ]; then + echo "Starting up the application stack in interactive mode..." + docker compose up db -d + echo "Waiting for MySQL to start..." + until docker exec lazygrocer-db-1 mysqladmin ping --silent; do + sleep 1 + done + echo "MySQL started." + docker compose run --rm -it app +else + echo "Starting up the application stack in detached mode..." + docker compose up -d + echo "Application stack started." + echo "Waiting for MySQL to start..." + until docker exec lazygrocer-db-1 mysqladmin ping --silent; do + sleep 1 + done + echo "MySQL started." + echo "Following logs..." + docker compose logs -f +fi \ No newline at end of file diff --git a/src/artifacts/recipe.db b/up.log similarity index 100% rename from src/artifacts/recipe.db rename to up.log