Author: David C. O'Gara
Pazuzu Pizza is a command line application. Built in python, that enables users an easy way to keep up to date with sales and stock of their business. The app is created specifically for Pazuzu Pizza, a small pizza counter within a supermarket, and offers its users the ability to input sales data and wastage while automating their production plan and offering access to other useful information such as pizza recipes and menu lists.
- Pazuzu Pizza
Pazuzu Pizza Repository on Github
Pazuzu Pizza, is a small company that rents a counter in the corner of a larger supermarket. They pre-make and wrap a selection of pizzas in various styles and in one of two sizes (small or large). Customers can then purchase these pizzas cold to cook at home, or have them cooked in-store to take-away hot. The owner wants an application to simplify and in some ways automate some of the process of keeping track of sales and stock. The users of this app will be the staff members working on the counter on a given day.
- The user is first asked to log in more detail. This is done by choosing one of these pre-made user accounts:
- The user is then asked to select an option. This is done by inputting the option number. more detail
- Selecting 1. 'Display Menu' brings up a list of pizza styles the company makes. more detail
- Selecting 2. 'Input Sales' asks the user to input a number for each pizza. more detail
- Selecting 3. 'Input Disosals' asks user to input disposal number for each pizza ingredient. more detail
- Selecting 4. 'View Production Plan' shows the user to amount of pizzas to be made for today's date. more detail
- Selecting 5. 'View Pizza Recipe' asks the user to choose a pizza by number and then displays the recipe for the chosen pizza. more detail
After the introduction the user is asked to Log in to the app. Employees of Pazuzu Pizza are given log-in information by their management to prevent non-employees accessing and altering the information. This is stored in the employee database which is a worksheet named 'employees' in the Pazuzu_Pizza Google spreadsheet.
First the user enters a username.
Followed by the password. The characters on the password input are hidden via the python module 'maskpass'.
The app then retrieves the username and password and checks them against the 'employees' worksheet. The username is checked first. If this doesn't exist on the worksheet a warning is given and the user is asked to enter the information again.
Then, if the username is recognised but the password is not a different warning is issued and the user is asked to enter the information again.
This applies too if the inputs are left blank.
Once the username and password are entered correctly the user is taken to the main menu part of the app.
In the main menu the user is asked to select from a list of numbered options. Only the option number is required.
If anything other than a whole number is entered a warning is given and the options are displayed again.
This includes a blank input.
Selecting option 1 displays a list of the pizza styles currently made by Pazuzu Pizza. This is drawn from the Pazuzu Pizza spreadsheet and the pizza_menu worksheet. This is included as reminder should any staff members need to check on the current list of pizza styles as changes may occur on a seasonal basis.
The pizzas are then displayed in a numbered list.
Selecting option 2 will ask the user to input the days pizza sales one by one. This is to be done at the end of the shift when the business is closed for the day although they can be entered as many times as the user wishes should a mistake be made.
If something other than a whole number is entered a warning is given and the user is asked to enter the number again.
Once succesful the worksheet 'pizza_sales' will be updated in the column coresponding to the day of the week.
Side by side video demonstration here.
The 'pizza_production' worksheet will also be updated at the end of this function by calling the calculated_production_plan function. This takes today's sales, entered by the user and adds 10% to the number then rounded to an integer.
Side by side video demonstration here.
from datetime import datetime
The date used in these functions is determined by the datetime python module. The module is imported and then used to display the date at the start of some functions and also used to take today's day and translate it to an uppercase letter for use in determining the cell to update.
def convert_day():
"""
Function imports todays day from python's datetime module.
Then converts it into a letter for use as column reference.
This function will be called within other functions.
"""
day = datetime.now().strftime("%a")
if day == "Mon":
return "B"
elif day == "Tue":
return "C"
elif day == "Wed":
return "D"
elif day == "Thu":
return "E"
elif day == "Fri":
return "F"
elif day == "Sat":
return "G"
elif day == "Sun":
return "H"
The formatted string literal bellow then used the letter returned from the convert_day function with an index determined in a for loop to create an A1 notation cell reference. In this instance the {sold} reference is the value input from the user entered in the input_sales function.
pizza_sales.update(
f"{convert_day()}{i + 2}",
f"{sold}",
value_input_option='USER_ENTERED'
)
Selecting option 3 will ask the user to input the disposal numbers for each pizza ingredient. This is done at the end of the shift also, as there are often ingredients/toppings that reach the end of their allotted life or use by date and must be disposed of. The user is asked to input an integer of the amount of units of each ingredient. To the staff member of Pazuzu Pizza, this unit will relate to different quantities depending on the item. For example, 2 units may mean 2 4oz spoons of grated mozzarella, 2 2oz spoons of passata sauce or just 2 slices of pepperoni. The amount entered will be added to the 'pizza_disposal' worksheet.
Side by side video demonstration here. The number of disposals entered will also be subtracted from the values in the 'pizza_stock' worksheet.
Side by side video demonstration here.
The 'pizza_stock' worksheet is also updated via a function called 'delivery'. First, the function 'weekly_delivery', checks to see if today is Monday and proceeds to call the delivery function if this is true.
def weekly_delivery():
"""
Get day today from datetime
Check if day is Mon
Call delivery function if true
"""
day = datetime.now().strftime("%a")
if day == "Mon":
delivery()
The delivery function then adds a set amount of units to the 'pizza_stock' worksheet. These pre-set delivery units are accessed from the 'pizza_delivery' worksheet.
def delivery():
"""
Fetch pizza_stock worksheet
Fetch pizza_delivery worksheet
Create list of values from col 1 of pizza_delivery
for every item in stocklist
fetch each delivery value
fetch each stock value
check if current stock value is greater than delivery value
break if true
add delivery value to stock value if false
"""
pizza_stock = SHEET.worksheet('pizza_stock')
pizza_delivery = SHEET.worksheet('pizza_delivery')
stock_list = pizza_delivery.col_values(1)
for i, stock in enumerate(stock_list[1:]):
delivery_value = int(pizza_delivery.acell(f'B{i + 2}').value)
current_stock = int(pizza_stock.acell(f'B{i + 2}').value)
if current_stock >= delivery_value:
red_text()
print('\nNOTICE: No weekly delivery received.')
reset_color()
time.sleep(0.5)
break
else:
pizza_stock.update(
f'B{i + 2}', f'{current_stock + delivery_value}',
value_input_option='USER_ENTERED'
)
red_text()
print('\nNOTICE: Weekly delivery has been received.')
time.sleep(0.5)
print('Stock list updated.')
reset_color()
Selecting option 4 displays for the user today's production plan. This tells the staff member how many pizzas to make for today based on last weeks sales with an aim to make and sell 10% more than last week. This data is retrieved from the pizza_production worksheet with the column reference chosen through the convert_day function.
Selecting option 5 displays a chosen pizza recipe for the user. Staff members can use this function to remind themselves what the standard is when creating pizzas, thus helping to create consistency in the pizzas produced by different colleagues. Users are asked first to select the size of pizza they wish to view the recipe for. This is simply to reduce the size of the list and make it easy for the user to see the options within the limited view space. To choose, the user must input either 's' or 'l' in lower case letters.
Choosing the size will then display a numbered list of pizzas.
Small:
Large:
Users are asked to input the number of the pizza they wish to view. The numbers are between 0 and 13. Doing so displays the pizza information.
Number 5 - Small Hot & Spicy:
Number 11 - Large Veggi Feast:
Entering anything other than a whole number will result in a warning and the user will be asked to enter their choice again.
Given production time and knowledge this app could provide a number of extra and more advanced features. Here is just a few:
-
The pizza stock list that keeps track of ingredients could be updated according the amound of pizzas made. This could be achieved by multiplying the amount of a particular topping needed for a pizza by the amount of that pizza produced. For example the amount of pepperoni used on a small pepperoni pizza is 8. If 15 small pepperoni pizzas are made that day than 120 slices of pepperoni will need to be subtracted from the stock of pepperoni. This will further simplify the stock ordering process for the company.
Flow chart for 'input sales' function:
- gspread
- gspread was used to access and update google sheets.
- datetime more detail
- Google Drive
- google drive credentials were needed to access google sheets.
- colorama
- colorama Fore was used to change the text colour in various placed to add emphasis to inputs and warnings making the experience better for the customer.
- colorama Style was used to reset any colour effects when needed.
- pyfiglet
- pyfiglet provided the title font used to make the app introduction more eye catching.
- sys
- sys was imported to set up the sys.exit function for the the user wishes to quit.
- maskpass
- maskpass was installed to hide the user's password input.
- warnings more detail
Various manual test were done to check that the app works as expected. This includes deliberately entering the wrong inputs and checking the data given against the google sheets.
Function | Intention | Action | No Terminal Error | Works as Expected |
---|---|---|---|---|
Login | Request and validate username and password. | Blank username input gives warning. Wrong user name gives warning. Blank password gives warning. Wrong password gives warning. |
[x] | [x] |
Options | Request input of option number. | Blank entry gives warning. String entered gives warning. Float entered gives warning. |
[x] | [x] |
Display Menu | Display list of pizzas | Pizza list checked against worksheet. | [x] | [x] |
Input Sales | Request input of whole number for each item. | String input gives warning. Blank input gives warning. |
[x] | [x] |
Input Disposals | Request input of whole number for each item. | String input gives warning. Blank input gives warning. |
[x] | [x] |
View Production Plan | Display list of pizzas for today's date. | List matches today. | [x] | [x] |
View Pizza Recipe | 1.Request input of s or l. 2.Request input of number between 1 and 13. |
1.Blank input gives warning. 1.Wrong letter gives warning. 1.Number entered gives warning. 2.Number above 13 gives warning. 2.Blank input gives warning. |
[x] | [x] |
Bug: A warning kept being printed the first time a google sheet was updated in a session. From some investigation it appeared the warning was for some changes being implimented in a future version of gspread.
UserWarning: [Deprecated][in version 6.0.0]: method signature will change to: 'Worksheet.update(value = [[]], range_name=)' arguments 'range_name' and 'values' will swap, values will be mandatory of type: 'list(list(...))'
warnings.warn(
Fix: The problem was solved by importing the warnings module and using it to ignore UserWarnings.
warnings.filterwarnings("ignore", category=UserWarning)
- (error occured because I had Sun in lowercase)
Bug: This error kept occuring when trying to update any of the day-based google sheets:
Error: An unexpected error occurred - {'code': 400, 'message': "Unable to parse range: 'pizza_production'!NONE2", 'status': 'INVALID_ARGUMENT'}
Fix: It happened to be a Sunday when this error appeared and I found "Sun" in the convert_day function had been mistakenly written in lower case.
No known bugs remaining.
PEP8 validator passed with no issues.
PEP8 Validator/ CI Python Linter
As mentioned throughout Pazuzu Pizza is connected to Google Sheets through an Application Programming Interface.
The Spreadsheet name is Pazuzu_Pizza. link
Sheet names:
- pizza_sales
- pizza_production
- pizza_disposals
- pizza_stock
- pizza_delivery
- pizza_recipe
- pizza_menu
- employees
Instructions for setting this up:
(create google account use personal account as more secure file - make a copy)
Method of setting up API
- In order for the app to work correctly with all the installed modules the mock terminal needs to know which modules to install ahead of time.
- To do this we need the file 'requirements.txt'.
- This file will contain a list of modules and their version.
- In my case this is the full list:
cachetools==5.3.1
certifi==2023.7.22
charset-normalizer==3.3.0
colorama==0.4.6
google-auth==2.23.2
google-auth-oauthlib==1.1.0
gspread==5.11.3
idna==3.4
maskpass==0.3.7
oauthlib==3.2.2
pyasn1==0.5.0
pyasn1-modules==0.3.0
pyfiglet==1.0.2
requests==2.31.0
requests-oauthlib==1.3.1
rsa==4.9
urllib3==2.0.6
Python can populate this list for you automatically with this command:
pip3 freeze > requirements.txt
The project was deployed on Heroku using Code Institute's mock terminal.
Details for deployment:
For the code used to authorize and access Google Drive and Google sheets I followed the Code Institute Love-Sandwiches walkthrough project. This was due to the code dealing with complicated concepts that needed to be written accurately for the whole project to work.
Here's the code used:
SCOPE = [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/drive"
]
CREDS = Credentials.from_service_account_file('creds.json')
SCOPED_CREDS = CREDS.with_scopes(SCOPE)
GSPREAD_CLIENT = gspread.authorize(SCOPED_CREDS)
SHEET = GSPREAD_CLIENT.open('pazuzu_pizza')
Table of contents generated with markdown-toc
Malia Havlicek
David C. O'Gara 2023