Skip to content

This app helps plan dinner parties by managing a recipe database, creating shopping lists, and checking off items already in your pantry | CodeInstitute Portfolio Project 3

Notifications You must be signed in to change notification settings

blahosyl/dinner-party

Repository files navigation

Dinner Party Planner

This app creates shopping lists for dinner parties from a recipe database.

It provides a structured flow with step-by-step questions that are straightforward to answer, does all necessary calculations in the background, and produces a tailor-made list depending on the ingredients needed, while also taking existing stock into account.

The result is more user-friendly and less prone to errors than using Google Sheets' built-in filtering and sorting for the same purpose.

The database contains recipes I routinely use, so this app does not only simulate a real-life process, it actually is usable in real life.

It was developed as my first Python project, as a requirement for my coursework for Code Institute (Portfolio Project 3).

Developer: Dr. Sylvia Blaho

Deployed site starting screen

Go to the deployed app

See the development progress and further plans on GitHub Projects.

GitHub last commit GitHub contributors GitHub language count

Table of Contents

Table of contents generated with readme-toc

User Experience (UX)

As an avid cook and dinner party host, I maintain several recipe databases in Google Sheets for different occasions, seasons and dietary preferences. These contain a range of dishes, ingredients and substitutions.

As it is not uncommon that I host a series of dinner parties within the same week, it is paramount to be able to pool all the ingredients needed for all these parties together, so that they can be purchased in a single grocery run or delivery.

Google Sheets is very well suited for storing data in a structured way, but it is much less capable of producing a shopping list tailored for a specific occasion. While it is technically possible to create such a list using conditionals, filtering and sorting, it is a cumbersome process prone to manual error.

The Dinner Party Planner app aims to solve this problem. It provides a structured flow with step-by-step questions that are straightforward to answer, does all necessary calculations in the background, and produces a tailor-made list depending on the ingredients needed, while also taking existing stock into account.

User goals

The same set of goals was determined for all users of the app regardless of whether they are first-time or returning users.

  • [UX1] I want to have a user-friendly and easy to use shopping list generator.
  • [UX2] I want to see what recipes are available at each stage of planning
  • [UX3] I want to choose one or more recipes to shop for
  • [UX4] I want to see the ingredients for the dish I selected
  • [UX5] I want to get a shopping list of all ingredients correctly added
  • [UX6] I want to adjust the shopping list based on the ingredients I already have
  • [UX7] I want clear instructions on the next step at each stage of the process
  • [UX8] I want to only see relevant information on the screen at each step of the process
  • [UX9] I want to be able to visually distinguish different kinds of information on the screen
  • [UX10] I want to know the result of each step/choice in the process

Creator goals

  • [CR1] I want to create my first Python application
  • [CR2] I want to create an application that fills a real-life need
  • [CR3] I want to make use of Object Oriented Programming
  • [CR4] I want to reduce code redundancy as much as possible
  • [CR5] I want the app to handle all possible user input
  • [CR6] I want the user to always be sure how to interact with the program
  • [CR7] I want the app to be as visually appealing as possible under the given constraints of the template
  • [CR8] I want to write code with future extensibility and scalability in mind

Design

Flow

Scope

From the outset, I had three main goals in mind: ease of use, proper error handling, and scalability.

The diagram below shows the Minimum Viable Product flow for the app. As discussed with my mentor, this satisfies the criteria of an MVP by being usable as is, and also a suitable project to finish in 2 weeks after just starting to learn Python.

Program flowchart: initial plan

The MVP program flow can be summarized as follows:

  • if the user answers Y to the initial question: start planning loop
  • if the user answers N to the initial question: end with message
  • when the user selects a dish:
    • remove it from the list of dishes
    • display its name and ingredients
    • add its ingredients to the shopping list
  • if the shopping list already contains an ingredient that is added: combine two items into one, sum the ingredient quantity
  • if the user wants to continue adding an ingredient: rerun planning loop
  • if the user does not want to add more ingredients:
    • end planning loop
    • print shopping list, end program
  • if the user selects all dishes:
    • end planning loop
    • print shopping list, end program

After the MVP was completed well ahead of the expected timeline, and there was more time left until the submission deadline than originally anticipated, I decided to add a new feature that was outside the scope of the MVP: the check_pantry() function and its accompanying logic.

This loops through each ingredient on the shopping list, asks the user how much of it they already have, and adjusts the quantities accordingly. When the resulting quantity is 0 or less, the item is deleted from the shopping list.

Even though this feature seemed like an easy add-on, it has nearly doubled the steps in the program flow.

The current version of the program flow is as follows (click here to view the full-size version on Miro):

Program flowchart: initial plan

The current program flow can be summarized as follows:

  • if the user answers Y to the initial question: start planning loop
  • if the user answers N to the initial question: end with message
  • when the user selects a dish:
    • remove it from the list of dishes
    • display its name and ingredients
    • add its ingredients to the shopping list
  • if the shopping list already contains an ingredient that is added: combine two items into one, sum the ingredient quantity
  • if the user wants to continue adding an ingredient: rerun planning loop
  • if the user does not want to add more ingredients:
    • end planning loop
    • ask if they want to check their pantry
  • if the user selects all dishes:
    • end planning loop
    • ask if they want to check their pantry
  • if the user does not want to check the pantry:
    • print shopping list, end program
  • if the user wants to check the pantry
    • ask how much of each shopping list ingredient they have
    • subtract answer from quantity on shopping list
      • if resulting quantity is 0 or less, remove item from the list
  • if there are shopping list items left, print shopping list
  • end program with message

This flow ensures an intuitive progression through the process, and eliminates the need for complicated navigation.

Interaction design

Minimal typing

The app is designed to work with as little typing as possible.

Yes/no questions only require a single character as an answer. They accept both capital and lowercase answers.

The list of dishes is presented as a numbered list, so that users only need to enter the dish number, rather than type the whole name of the dish.

The pantry checker function only requires the user to enter a number for each ingredient checked.

Information is adjusted for each step

The list of dishes changes dynamically, so only dishes that have not yet been added to the shopping list are shown to the user at each point.

The pantry checker flow only asks input for dishes that have been added to the shopping list.

Alphabetization

All lists shown to the user are alphabetized, which aids visual processing.

String transformations

The shopping list is presented twice during the flow of the program: at the end of planning, and during the pantry checking process.

Each of these is very different from each other as well as from how the data is stored, tailored to the specific purpose in the flow.

Both are

  • transformed into a readable string
  • alphabetized
  • measurement names are shown instead of abbreviations
  • singular/plural is handled based on the quantity
Input in error messages

When a user enters an invalid input, the input is repeated in the error message. This improves user interaction, and also avoids misunderstanding that could result from a user entering input before the prompt appears.

Tailored error messages

Error messages also show the reason why user input is rejected (not a valid number, out of range, not "Y"or "N"), and remind the user what type of input is accepted for the question on the screen.

Code design

When revising and refactoring code, I devoted special attention to code design, beyond merely getting the functionalities to work. I worked to reduce redundancy and improve readability by utilizing Object Oriented Programming (OOP), reusing functions/methods, and splitting up code into separate files where warranted.

OOP

The first version of the app consisted of independent functions being called in sequence. While this worked as intended, it lacked organization and scalability, so I decided to refactor it by creating a module with 2 classes, DishList and ShoppingList, along with their respective methods and related (but independent) functions:

  • the DishList class creates a list of dishes as an object. It has the following methods:
    • print_enum(): prints DishList as an enumerated list
    • select_dish(): lets the user select a dish to be added to the shopping list
  • the ShoppingList class creates a list of lists, consisting of an ingredient & measurement unit and a quantity. It has the following methods:
    • get_ingredients(): gets the ingredients of the selected dish from the database, adds them to the shopping list and prints them out
    • unify_ingredients(): checks the shopping list for items with the same ingredient and unifies these
  • the parse_string() function replaces the abbreviation of measurement units with their full name, and also returns some other information crucial for text transformation (see the docstring for details)
  • the print_formatted() function transforms the database string to be printed in a user-friendly format

These classes can be viewed in planner.py.

Pantry checker

Developing the pantry checker functionality inspired a lot of restructuring, refactoring, splitting up functions and rethinking the logic. On the one hand, this significantly expanded the scope of the project compared to the MVP, but it had the added benefit of resulting in much cleaner and better code, on top of the actual functionality (which is very relevant to real-life usage).

Pantry checker features
  • handle floats without upper bound
  • shown measurement names instead of abbreviations
  • remove ingredients with a quantity less than 0
  • handle singular/plural quantities in the output message

Y/N validator functions made general-purpose

A relatively easy piece of refactoring involved creating a general-purpose Y/N question validator function. This takes a single parameter, the prompt text to be shown in the input block, and only accepts "Y" or "N" (and their lowercase versions) as valid inputs.

Because I am using extensive color effects in this project, the content of the prompt can be quite complicated when it includes the colorama declarations. Handling it as a string resulted in a lot of unintended errors and bad human readability. This is why I chose the prompt parameter of this function to be defined as a single-item list instead of a string, which eliminated these errors.

Range validator function made general purpose

The range validator function was much more labor intensive to generalize, as this also involved handling both integers and floats, ranges without an upper bound, and extensive customization of the output message.

The range validation function in its current form takes the following parameters:

  • prompt - same function and usage as with the Y/N validator
  • num_type - whether the input must be an integer or a float
  • lower - the lower bound of the allowed range (inclusive)
  • upper - the upper bound of the allowed range (inclusive) - optional parameter, default is infinity

File organization

At the suggestion of my mentor and @nobe4, I split up my run.py file further, and created 2 dedicated files:

  • utilities.py containing general-purpose functions (the input validators and clear)
  • gsheet.py containing all functionalities related to interfacing with Google Sheets.

Docstrings and code comments

Having worked in Technical Writing for nearly a decade, and with IT security teams for over half of that time, I have seen that proper and sufficient documentation not only helps development and collaboration, but also does a lot to eliminate the human error factor from security vulnerabilities. Furthermore, I have also learned that the "proper" and "sufficient" level of code documentation very much depends on the expertise of those working on said code.

Since this is my very first Python project after having started to learn the language just a few days before development started, I have consciously erred on the side of caution when documenting code. Each function has docstrings with parameter types and descriptions. In addition, individual lines are also commented with what exactly they do and why. This has proven to be very beneficial when refactoring code and modifying functionalities during the project.

CRUD functionalities

Create
  • dataset from Google Sheets
  • list of dishes
  • shopping list
  • human-readable version of shopping list
  • human-readable version of ingredients
Read
  • dataset from Google Sheets
  • list of dishes
  • shopping list
  • ingredients of a dish
Update
  • list of dishes
  • shopping list quantities
  • shopping list items
Delete
  • items from the shopping list

Database

The matrix of dishes, ingredients and quantities are stored in this Google Sheet.

A new test Google account was created for this purpose for security reasons, as I did not consider it good practice to use my real-life credentials for developing my first app of this kind, where the potential for human error is large.

However, the contents of the database are actually recipes I routinely use, so this app does not only simulate a real-life process, it actually is usable in real life.

The database was designed with extensibility in mind, and already contains data structures for features outdie the MVP:

The app interfaces with the database in a way that does not rely on the order of elements. This means that the Google worksheet remains usable, and can be modified or sorted without affecting the interaction with the app.

The crucial properties of the database in terms of compatibility with the app are only the following:

  • dishes are in the second row
  • ingredients are in the first column
  • each ingredient cell contains ( and )
  • quantity cells only contain floats

Visual design

Since using a terminal-based app can be daunting for people who are not used to it, I took extra care to structure and time the appearance of the information presented to the user.

Clear screen

I cleared the screen after each step, so that the user is not distracted by information no longer relevant to them.

Pause

I inserted pauses after confirmation messages, so that the user has enough time to read them.

Emojis

I added emojis to confirmation messages to break up the monotony and aid visual recognition.

ASCII art

I used ASCII art to signify the beginning and end of the program.

Color

The main colors chosen (magenta and green) harmonize with the background image of the site.

In addition, red is used for validation error messages.

However, the use of color in this project goes beyond aesthetic purposes: it also serves to aid the user experience, and is deliberately used to distinguish different functional elements from each other:

style function
colored background user input needed
red background user entered invalid data
green text information that the user needs to proceed
magenta ASCII text start and end of program

In addition, important information within messages is highlighted with a contrasting color.

Website design

As this project is focused on Python rather than HTML/CSS, designing/altering the site itself was an optional extra.

Nevertheless, I chose to lightly alter the provided HTML/CSS code to make the deployed page more unique and appealing to users (as a terminal window on a plain white background is alienating to many people).

Background image

I chose a background image of a colorful dish of sweet potatoes, purple onions and thyme being prepared, to illustrate the joy and labor that goes into throwing a dinner party.

Button design

I changed the background color of the Run Program button to the purple color of the onions from the background image (with the help of ImageColorPicker). This color was chosen to harmonize with the image but still stand out from the rest of the elements on the page.

The color contrast with the white text was checked for accessibility/legibility (see Color contrasts for more details).

I have also made the button and the text on it larger and increased the font weight. To balance out these changes, I also increased the button width and its margin.

Finally, I added a hover cursor effect.

Alignment of elements

I horizontally centered all elements on the page and added some top margin for a more pleasing look.

Favicon

I added a favicon showing a vector drawing of two wine glasses clinking, to symbolize the social nature of dinner parties. The color of the graphic is a darker shade of the purple color chosen for the button.

Meta tags

I added some SEO meta tags to the HTML file, so that the site can be found more easily.

Design implementation credits

I followed the American Pizza Order System project by Iasmina Pal in implementing the changes above.

Rerolled design elements

I made some additional changes based on the American Pizza Order System project that I have decided to reroll: although the modified layout.html file passed validation, the deployed page did not.

Click to see the details of the reroll

In addition to some styling in the head element, the American Pizza Order System project added the following code to @body in layout.html:

<body>
    <main id="main-container">
        <h1>AMERICAN PIZZA ORDER SYSTEM</h1>
        @{body}
    </main>
</body>

I had adopted this code, and also added a logo to the header and my name and a link to the GitHub repository.

Header

I added the name of the app as an h1 element before the Run button in the template. For its background, I used the purple color of the onions from the background image (with the help of ImageColorPicker).

The heading text color is white, so that it provides sufficient contrast with the background (see Color contrasts for more details).

Logo

I put the image used for the favicon as a logo on each side of the header. Its design mirrors that of the box containing the header (box, border radius and shadow), but the colors are inverted.

Author information and GitHub link

I added my name and the link to the application's repository under the terminal window, using the colors already defined above.

The rerolled site design

Validating the resulting html file in itself passed without errors.

layout.html passes validation

However, running HTML validation for the deployed site, produced an error of there being multiple <body> tags. I have deduced that the reason for this is that the Code Institute template adds a <body> tag as well, resulting in a duplicate.

The deployed site did not pass HTML validation

The code of the deployed site that did not pass HTML validation

Accordingly, I have rerolled the changes to layout.html that involved adding html elements, and kept the styling to modifications in the head. After this, both the modified layout.html file and the the deployed site passed the W3C validator.

The deployed site passes HTML validation

Font

I changed the font used from Arial to Verdana. This font is considered the most legible of the popular web-safe fonts, especially for small screen sizes.

Features

Start screen

The start screen displays the name of the app in a large font styled with ASCII art, followed by the developer name and repository link, the instructions for use, and finally, the initial question asking the user if they want to start planning.

Start screen

"Stop program" confirmation

If the user answers "N" to the initial question, the corresponding confirmation message appears, before clearing the screen and showing the end screen.

End the planning flow

"Start planning" confirmation

If the user answers "Y" to the initial question, the corresponding confirmation message appears, before clearing the screen and starting the planning loop.

Start the planning flow

Dish selection screen

This shows the available dishes in an alphabetized list. The user can choose a dish by selecting the corresponding number.

Showing the list of dishes, alphabetized

Dish selection confirmation

Once the user selected a dish, a confirmation message appears showing the name of the dish.

Confirming the selected dish

Selected dish and ingredients

The next screen shows the ingredients of the selected dish, followed by the question asking the user if they want to select another dish.

The ingredients of the selected dish

Modified list of dishes

If the user answers "Y" to the previous question, the modified list of dishes is shown with the corresponding confirmation message. The new list does not contain dishes that have previously been selected.

Show the modified list of dishes

Automatically end planning loop after last dish

The planning flow automatically ends when we run out of dishes to select.

The last dish on the list

In this case, the following message is shown:

The user has selected all dishes

Continue without checking the pantry

If the user chooses not to check their pantry for ingredients, a confirmation message appears, followed by the shopping list.

Don't start pantry check

Start checking the pantry

If the user chooses to start the pantry tracking flow, the following confirmation message appears.

Start pantry check

Pantry check

The program then goes through all the ingredients on the shopping list, and asks the user how much they have.

Pantry check in progress

Pantry check done

After all ingredients on the shopping list have been checked, the following confirmation message appears:

Pantry check done

No items needed

If the user has all the required ingredients in sufficient quantities, the following message appears:

No items needed after pantry check

This is followed by the end screen.

No items needed after pantry check and end screen

Shopping list

If there are items that the user needs to buy, the shopping list appears after checking the pantry.

Shopping list

End screen

After a short pause, the shopping list is followed by the end screen, to let the user know that the program has ended.

Shopping list with end screen

"Start again" message

Because the "run program" button is outside the terminal, the end screen also includes instructions on how to start the program again.

End screen

Future features

There are several extra features and extensions planned for this project that were outside the MVP and were unrealistic to complete in the allotted time of 2 weeks.

They are viewable in GitHub Issues, including extensive mock code for possible implementation.

Accessibility

Color contrasts

The WebAIM contrast checker was used to ensure that the text and background color of the heading provides sufficient contrast for legibility.

The colors used in the project are as follows:

color name HEX code Comment
white #FFFFFF
onion-purple #7A2F40
thyme-green #5A5A26 Not used in the final version, but still visible in the screenshot here.

The paired colors have the following contrasts:

color 1 color 2 contrast WCAG AAA
onion-purple white 9.09:1
thyme-green white 7.17:1

Technologies used

Languages used

  • Python: to create the app
  • Markdown: to create the documentation
  • HTML/CSS: to modify the deployment template supplied by Code Institute

Python libraries used

  • os: used in the function that clears the terminal
  • time: use the sleep method to pause the running of the app, so that the user has time to process information
  • gspread: get data from Google Sheets
  • google.oauth2.service_account: authenticate with Google Sheets
  • itertools: get combinations from a list
  • colorama: add color to the terminal text, both for design and UX purposes (see the section Color for more detail)
  • pyfiglet: display text using ASCII art

Tools used

Development process

The project was developed with Pycharm, using GitHub for version control.

The README was written with MacDown, Spck and GitHub web editor.

GitHub Projects and Issues were used to track project development, new features and bugs.

Significant changes to the codebase were developed on separate branches, which were deleted when the addition/change was completed and tested.

Deployment

Create a database

Create a worksheet in Google Sheets following this template.

Make sure to keep the following format:

  • dishes are in the second row
  • ingredients are in the first column
  • each ingredient cell contains ( and )
  • quantity cells only contain floats

Connect to Google Drive & Google Sheets API

Follow the process described in the Love Sandwiches walkthrough project, helpfully replicated here.

Fork the repository

You can fork the repository by following these steps:

  1. Log in to GitHub (if you don't have a GitHub account yet, you can create one for free).
  2. Navigate to the project website https://github.com/blahosyl/dinner-party.
  3. Click on Fork in the upper right part of the screen.
  4. On the next page you have the possibility to change the repository name. To do this, simply write your desired name in the text field in the center part of the screen. You can also leave the name as it is.
  5. Click Fork in the bottom right part of the screen.

Tip

If you do rename the repository, make sure to keep the GitHub naming conventions in mind.

Deploy to Heroku

  1. Create a list of requirements by going to the terminal and typing pip3 freeze > requirements.txt. This popuplates your requirements.txt file with the list of required files.
    Push your changes to GitHub.
  2. Under Settings > Config Vars in Heroku, add a new var with the key CREDS and the value equal to the contents of your creds.json file.
  3. Under Settings > Config Vars in Heroku, add a new var with the key PORT with the key PORT and the value 8000.
  4. Under Settings > Buildpacks in Heroku, add Python to Heroku Buildpacks.
  5. Under Settings > Buildpacks in Heroku, add NodeJS to Heroku Buildpacks.
  6. Under Deploy > Deployment method in Heroku, select GitHub and connect Heroku to your GitHub account.
    Type in your repository name, then click Search. When your repository appears, click Connect next to it.
  7. Under Deploy > Manual deploy in Heroku, select Deploy branch to deploy manually.
    Once the process is finished, the following message will appear:
    Your app was successfully deployed
    Click View under the message, and a new tab will appear with your deployed app.
  8. (optional) Under Deploy > Automatic deploy in Heroku, select Enable Automatic Deploys if you want your app to be rebuilt each time you push to the main branch of your GitHub repository.

Testing

See the document TESTING.md for details.

Credits

Study/lookup sources

The following resources were used to learn/double check general, atomic functionalities/syntax:

Code credits

The following sources contributed code or suggestions to specific functions within the project:

Rory Patrick Sheridan, my mentor, gave suggestions, helped me solve or spotted bugs described in these Issues (see the Issue descriptions and comments for details).

Modifying the layout.html file in the Code Institute template to change some CSS styling was done following but also revising the method used in the American Pizza Order System project by Iasmina Pal (see the section Rerolled design elements for details).

@nobe4 and Zerina Johansson helped me solve the issue of printing out the user input in a try/except loop.

@nobe4 also gave me valuable advice on organizing code, and helped me solve the bug in displaying the correct user input in a try/except block.

I am very grateful to Peter Litauszki who helped me set up GitHub and Spck on an Android tablet, so I could work on the README while on the go.

Finally, I would like to thank an enthusiastic alpha tester who wished to remain anonymous.

Content

All text content was written by me.

Media

Images

Background image by me, converted to webp with CloudConvert.

Image for the favicon and logo from Vecteezy, converted to png format with Preview, converted to ico format with Favicon.io, converted to webp with CloudConvert.

Readme

About

This app helps plan dinner parties by managing a recipe database, creating shopping lists, and checking off items already in your pantry | CodeInstitute Portfolio Project 3

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published