Bonus: transactions

Teagan's Poet
Teagan's Poet

Bonus lessons are optional.

Let's write a basic transaction processing system (TPS - not that one). A TPS tracks business events, like sales, inventory control... the regular stuff of business operations.

The sitch

You run a small comedy club, with 50 seats. Jeff Arcuri is performing. He's one of my fave comedians. You sell tickets over the phone, and at the club box office. People can also return tickets.

You want to keep track of how many tickets you've sold, and how many are still available. Let's see how it's done. You can download a solution.

I/O sample

Most programs we've written so far run through once, then stop. Not this one.

The program shows a main menu:

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command?

The user enters a command, like S for sale. The program does whatever it needs to, the shows the main menu again. It keeps showing the menu until the user quits.

Here's an I/O session.

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? t
  •  
  • Status report
  • --------- ------ ------
  • Ticket inventory: 50
  • Tickets sold: 0
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 4
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? t
  •  
  • Status report
  • --------- ------ ------
  • Ticket inventory: 46
  • Tickets sold: 4
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? r
  • How many returned? 6
  • Sorry, we haven't sold that many tickets.
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? r
  • How many returned? 2
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? t
  •  
  • Status report
  • --------- ------ ------
  • Ticket inventory: 48
  • Tickets sold: 2
  •  
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 8
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 9
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 11
  • Sorry, please enter a number from 1 to 10.
  • How many sold? 9
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? t
  •  
  • Status report
  • --------- ------ ------
  • Ticket inventory: 22
  • Tickets sold: 28
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 9
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 10
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? t
  •  
  • Status report
  • --------- ------ ------
  • Ticket inventory: 3
  • Tickets sold: 47
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 4
  • Sorry, we don't have that many tickets.
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? q
  •  
  • OK, bye!

There's also the basic validation stuff we did before.

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? some
  • Sorry, you must enter a number.
  • How many sold? -1
  • Sorry, please enter a number from 1 to 10.
  • How many sold? 1
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? r
  • How many returned? some more
  • Sorry, you must enter a number.
  • How many returned? -44
  • Sorry, please enter a number from 1 to 10.
  • How many returned? 1

The main program

Let's write the program top-down. That is, start with the main program, before going into the deets.

Top-down helps identify the main chunks of an app. Each chunk often becomes a function, though not always.

Can anyone write some pseudocode for the main program?

Adela
Adela

Let's see. From the I/O, it's, like, show the menu, get a command, do it. Then show the menu, get a command, do it. Show the menu, get a command, do it. Keep going until the user quits.

That's a loop. So something like:

  • while user hasn't quit
  •     show the menu
  •     get user command
  •     if command is sale
  •         do sale thing
  •     if command is return
  •         do return thing
  •     if command is status report
  •         show a report

Good!

Can anyone Pythonize some of it?

Georgina
Georgina

We need a variable for the command. Let's make it... er, command.

The program loops while command isn't q. So maybe...

  • while command != 'q':
Ethan
Ethan

Gotta initialize command first?

Georgina
Georgina

Oh, right, thanks.

  • command = ''
  • while command != 'q':

Nice.

What's the first thing in the loop?

Ray
Ray

The first part of the pseudocode is:

  • while user hasn't quit
  •     show the menu
  •     get user command
  •     ...

We're gonna show the menu, ask the user for a command, and... test it's valid?

Maybe print()s for the menu, then an input(), then validation. That's another loop for validation, like we've done before.

This is gonna be a long program.

Right. It's easy to get lost in the weeds. It's best to think in big chunks first, and add the deets later.

Adela: Aha!
Adela

Oh! We should make a function to... well, get a valid command from the user, whatever that involves. So:

  • command = ''
  • while command != 'q':
  •     command = get_main_menu_choice()

Are we on the right track?

Yes! That's great!

Here's the pseudocode again, with some real Python.

  • command = ''
  • while command != 'q':
  •     command = get_main_menu_choice()
  •     if command is sale
  •         do sale thing
  •     if command is return
  •         do return thing
  •     if command is status report
  •         show a report
Georgina
Georgina

So, command is a single letter. We can use that in more of the code.

  • command = ''
  • while command != 'q':
  •     command = get_main_menu_choice()
  •     if command == 's':
  •         do sale thing
  •     if command == 'r':
  •         do return thing
  •     if command == 't':
  •         show a report
  • print()
  • print('OK, bye!')
Georgina
Georgina

I added the last two lines, too.

Ethan
Ethan

Hey, Georgina, there's nothing like command == 'q'.

Georgina
Georgina

Doesn't need to be.

Here's the code again.

  1. command = ''
  2. while command != 'q':
  3.     command = get_main_menu_choice()
  4.     if command == 's':
  5.         do sale thing
  6.     if command == 'r':
  7.         do return thing
  8.     if command == 't':
  9.         show a report
  10. print()
  11. print('OK, bye!')

When the user gives the quit command (line 3), a q will be in command.

command doesn't match in the ifs on lines 4, 6, or 8. So, the program loops back up to line 2.

command is q. Since the loop runs while command is not q, the loop exits.

Ethan
Ethan

OK, I see it now. Thanks.

Ray
Ray

I'll add some comments for the missing code.

  1. command = ''
  2. while command != 'q':
  3.     command = get_main_menu_choice()
  4.     if command == 's':
  5.         # Sale
  6.         # Get number tickets sold
  7.         # Reduce inventory
  8.     if command == 'r':
  9.         # Return
  10.         # Get number tickets returned
  11.         # Add to inventory
  12.     if command == 't':
  13.         # Show a report
  14. print()
  15. print('OK, bye!')

Drilling down

Ray is doing it right. The command loop is done(ish). He can drill down on the pieces inside it, thinking about them one at a time.

Part of being an effective programmer is managing your own cognitive load, that is, the number of things you need to think about at one time.

Adela
Adela

OK, I see what you did. Nice.

Should we have a function to ask the user how many tickets sold?

The function will return something to the main program. Maybe:

num_sold = ticket_sale()

Georgina
Georgina

Ooo! Then...

  1. command = ''
  2. while command != 'q':
  3.     command = get_main_menu_choice()
  4.     if command == 's':
  5.         # Sale
  6.         # Get number tickets sold
  7.         num_sold = ticket_sale()
  8.         # Reduce inventory
  9.         current_inv -= num_sold
  10.     if command == 'r':
  11.         # Return
  12.         # Get number tickets returned
  13.         # Add to inventory
  14.     if command == 't':
  15.         # Show a report
  16. print()
  17. print('OK, bye!')
Georgina
Georgina

I just made up the variable name. It could be something else.

Ethan
Ethan

current_inv needs a value, though, or crashy crashy.

Hmm, not sure what to do... Oh! There are 50 seats in the club.

I'll add stuff for the other commands, too.

  1. current_inv = 50
  2. command = ''
  3. while command != 'q':
  4.     command = get_main_menu_choice()
  5.     if command == 's':
  6.         # Sale
  7.         # Get number tickets sold
  8.         num_sold = ticket_sale()
  9.         # Reduce inventory
  10.         current_inv -= num_sold
  11.     if command == 'r':
  12.         # Return
  13.         # Get number tickets returned
  14.         num_returned = ticket_return(total_sold)
  15.         # Add to inventory
  16.         current_inv += num_returned
  17.     if command == 't':
  18.         # Show a report
  19.         show_status_report(current_inv)
  20. print()
  21. print('OK, bye!')
Reflect

What is a function's signature?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ray
Ray

It's the name, params, and return type of a function. Kinda tells you what it takes, and what it returns. The name should hint at what it does.

Top-down FTW!

The Scoobies wrote the main program first, adding calls to functions that made sense to them. They've specified the purpose and signature of each function. Now, when the Scoobies write the functions, the functions will fit with what the main program wants them to do.

Georgina
Georgina

Hey, that feels pretty good!

Ethan
Ethan

Yeah, it's like writing the main program has given us a plan for the functions.

Adela
Adela

So cool!

Work on the menu function next?

The menu

Ray
Ray

We've already done string validation loops a few times. Ask the user for input, if it's invalid, show an error message, and ask again.

Here's some code for a Y/N input, from the past.

  • choice = ''
  • while choice != 'y' and choice != 'n':
  •     choice = input('Something (Y=yes, N=no)? ')
  •     choice = choice.strip().lower()
  •     if choice != 'y' and choice != 'n':
  •         print('Please enter Y or N.')
Ethan
Ethan

I once had trouble understanding that. Seems easy now.

It's good to see that. Helps you remind yourself you're learning Good Stuff.

Adela
Adela

Let's replace some of that.

As you do, let's add another goal: make it easy to add other commands to the menu.

Adela
Adela

OK. Hmm. If we're gonna do that, maybe think about the things that would change if we added a new menu entry?

There'd be another option in the menu, and the validity test could change, the thing that checks for Y and N. It would have to check for the new option as well.

Oh, the error message would change.

  • choice = ''
  • while Change validity check. Not choice != 'y' and choice != 'n' anymore:
  •     choice = input('Change the prompt Something (Y=yes, N=no)? ')
  •     choice = choice.strip().lower()
  •     if Change validity check again choice != 'y' and choice != 'n':
  •         print('Please enter Options.')
Adela
Adela

Before, when we had repeated code, we made a function for it. Maybe put the validity check in a function.

Ethan
Ethan

Yeah, makes sense! Like is_menu_choice_valid(choice).

I don't know what the return value would be. A string? A float?

Adela: Aha!
Adela

Hey! The user's choice is either valid, or not, right? That's a boolean! A value that's either true or false, and nothing else.

We could even call the function directly in the while.

  • choice = ''
  • while not is_menu_choice_valid(choice):
  •     choice = input('Prompt')
  •     choice = choice.strip().lower()
  •     if not is_menu_choice_valid(choice):
  •         print('Error message')

Good! Putting the validity check in a function eliminates duplicate code, but it does something else, too.

Reflect

How does having the function is_menu_choice_valid(choice) help make it easier to add new menu items?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ray
Ray

I think I know. We add the new validity check in one place.

Before, when we had this:

  • choice = ''
  • while Change validity check choice != 'y' and choice != 'n':
  •     choice = input('Something (Y=yes, N=no)? ')
  •     choice = choice.strip().lower()
  •     if Change validity check choice != 'y' and choice != 'n':
  •         print('Please enter Y or N.')

If we wanted to add an option, we'd have to change the check twice. More chance of error.

Now we have:

  • choice = ''
  • while not is_menu_choice_valid(choice):
  •     choice = input('Prompt')
  •     choice = choice.strip().lower()
  •     if not is_menu_choice_valid(choice):
  •         print('Error message')

The validity check is in one place, is_menu_choice_valid. Change that, one and done.

Right! Good thinking, Ray! Less chance of Buggy McBugFace.

OK, we have:

  • choice = ''
  • while not is_menu_choice_valid(choice):
  •     choice = input('Prompt')
  •     choice = choice.strip().lower()
  •     if not is_menu_choice_valid(choice):
  •         print('Error message')
Ethan
Ethan

Should we add the function stuff, to remind ourselves what we're working on?

Maybe we should have done this before.

Ethan's right

When you're writing a function, type in the signature, including return value, first. Helps you keep the goal in mind.

  • def get_main_menu_choice():
  • choice = ''
  • while not is_menu_choice_valid(choice):
  •     choice = input('Prompt')
  •     choice = choice.strip().lower()
  •     if not is_menu_choice_valid(choice):
  •         print('Error message')
  •     return choice
Georgina
Georgina

Ya know... it kinda feels right to make another function that shows the menu and gets the user's choice. It's, like, one chunk of logic, that does one thing. We can give it a name that makes sense, telling you what it does.

Puts the change-the-options-shown in one place.

Maybe this? Oh, I'll make the error message more generic, too.

  • def get_main_menu_choice():
  •     choice = ''
  •     while not is_menu_choice_valid(choice):
  •         choice = input_menu_choice()
  •         if not is_menu_choice_valid(choice):
  •             print('Please enter a valid command.')
  •     return choice
Ethan
Ethan

We could put the normalization in there, too! Keep the deets together.

Note

The Scoobies are developing good programming instincts.

"Here's some functionality that does one thing, we can give it a name, and wrap the deets inside it."

The more you look at good code, the better you'll be at packaging functionality.

Here's the program so far. I've removed some comments to take less screen space, but we'll put them back later.

  • def get_main_menu_choice():
  •     choice = ''
  •     while not is_menu_choice_valid(choice):
  •         choice = input_menu_choice()
  •         if not is_menu_choice_valid(choice):
  •             print('Please enter a valid command.')
  •     return choice
  •  
  • command = ''
  • while command != 'q':
  •     command = get_main_menu_choice()
  •     if command == 's':
  •         num_sold = ticket_sale()
  •         current_inv -= num_sold
  •     if command == 'r':
  •         # Return
  •         # Get number tickets returned
  •         # Add to inventory
  •     if command == 't':
  •         # Show a report
  • print()
  • print('OK, bye!')

Functions for get_main_menu_choice()

Adela
Adela

We need two functions for get_main_menu_choice():

  • is_menu_choice_valid(choice)
  • input_menu_choice()

The second might be easier to think about.

Ethan
Ethan

OK, how about this?

  • def input_menu_choice():
  •     print()
  •     print('Menu')
  •     print('----')
  •     print(' S: sale')
  •     print(' R: return')
  •     print(' T: sTatus report')
  •     print(' Q: quit')
  •     choice = input('Command? ')
  •     choice = choice.strip().lower()
  •     return choice

Note

Ethan included the signature right at the start this time. Makes things easier.

Ray
Ray

Oh, dude, that's, like... easy to understand. You took the I/O we saw before, printed the menu, an input, and normalization.

Note

The best programs are made of small functions that are easy to write, test, and debug.

To know what the functions should do, you need to design the program's structure first. Top-down FTW!

Georgina
Georgina

I'll try the other one, is_menu_choice_valid(choice). It returns true if choice is valid, false if not.

  • def is_menu_choice_valid(choice):
  •     if choice == 's' or choice == 'r' or choice == 't' or choice == 'q':
  •         return True
  •     else:
  •         return False
Georgina
Georgina

It's a bit hard to follow, though. What if we used the backslash thing to break up the long line?

  • def is_menu_choice_valid(choice):
  •     if choice == 's' \
  •         or choice == 'r' \
  •         or choice == 't' \
  •         or choice == 'q':
  •         return True
  •     else:
  •         return False

Note

Human eyes are better at scanning down, than across. That's what makes this easier to understand.

Georgina
Georgina

That's OK, but something's bugging me. It's, like, if the logical expression (the stuff after if) is true, return true. If it's false, return false.

Could we... I dunno... return the value of the logical expression itself? Do we even need the if? Kieran?

Good thinking! You don't need the if. This thing...

  • choice == 's' \
  •         or choice == 'r' \
  •         or choice == 't' \
  •         or choice == 'q'

... is either true or false already. It returns a boolean, and that's what the function should return.

Georgina
Georgina

Oh, I see! How about this?

  • def is_menu_choice_valid(choice):
  •     valid_choice = \
  •         choice == 's' \
  •         or choice == 'r' \
  •         or choice == 't' \
  •         or choice == 'q'
  •     return valid_choice

Right! If choice is one of the allowed values, valid_choice will be true, or else it will be false. Just return it! That fits with what the function is supposed to do.

Ethan
Ethan

Wow, good thinking, Georgina!

Ray
Ray

Yeah, so elegant!

Adela
Adela

Yup, that's primo thunking!

The Scoobies make a good team.

The program so far:

  • def get_main_menu_choice():
  •     choice = ''
  •     while not is_menu_choice_valid(choice):
  •         choice = input_menu_choice()
  •         if not is_menu_choice_valid(choice):
  •             print('Please enter a valid command.')
  •     return choice
  •  
  • def input_menu_choice():
  •     print()
  •     print('Menu')
  •     print('----')
  •     print(' S: sale')
  •     print(' R: return')
  •     print(' T: sTatus report')
  •     print(' Q: quit')
  •     choice = input('Command? ')
  •     choice = choice.strip().lower()
  •     return choice
  •  
  • def is_menu_choice_valid(choice):
  •     valid_choice = \
  •         choice == 's' \
  •         or choice == 'r' \
  •         or choice == 't' \
  •         or choice == 'q'
  •     return valid_choice
  •  
  • command = ''
  • while command != 'q':
  •     command = get_main_menu_choice()
  •     if command == 's':
  •         num_sold = ticket_sale()
  •         current_inv -= num_sold
  •     if command == 'r':
  •         # Return
  •         # Get number tickets returned
  •         # Add to inventory
  •     if command == 't':
  •         # Show a report
  • print()
  • print('OK, bye!')

Testy testy

Adela
Adela

We've got an entire... not sure what to call it... big chunk now. Let's test it.

I'd call it a subsystem. A menu subsystem. Testing now is a good idea.

Ray
Ray

OK, I put the whole thing into the Spyder IDE. Now run it...

I get an error on these lines:

  •         num_sold = ticket_sale()
  •         current_inv -= num_sold

Oh, that makes sense! We haven't written that stuff yet. I'll just comment them out for now.

  •         # num_sold = ticket_sale()
  •         # current_inv -= num_sold

Commenting out

Adding #s to the lines tells Python to ignore them, but the lines are still there. That called commenting out.

Ethan
Ethan

Maybe add another line to show what the user's choice was?

Ray
Ray

OK, good idea.

  • command = ''
  • while command != 'q':
  •     command = get_main_menu_choice()
  •     print('Command: ' + command)
  •     if command == 's':
  •         # num_sold = ticket_sale()
  •         # current_inv -= num_sold
  •     if command == 'r':
  •         # Return
  •         # Get number tickets returned
  •         # Add to inventory
  •     if command == 't':
  •         # Show a report
  • print()
  • print('OK, bye!')
Ray
Ray

Now, let's give it a whirl.

Here's the I/O:

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? S
  • Command: s
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • Command: s
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? llamas
  • Plese enter a valid command.
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? T
  • Command: t
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? q
  • Command: q
  •  
  • OK, bye!
Adela
Adela

That's perfect! Yeehah!

Yeehah indeed.

Sale!

We have:

  1. command = ''
  2. while command != 'q':
  3.     command = get_main_menu_choice()
  4.     if command == 's':
  5.         num_sold = ticket_sale()
  6.         current_inv -= num_sold
  7.     if command == 'r':
  8.         # Return
  9.         # Get number tickets returned
  10.         # Add to inventory
  11.     if command == 't':
  12.         # Show a report
  13. print()
  14. print('OK, bye!')
Ethan
Ethan

So ticket_sale() just gets a number, right? Just a validation loop?

Adela
Adela

What is someone wants to buy more tickets than we have?

Ray
Ray

That's a problem. This is from the I/O from before.

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? 4
  • Sorry, we don't have that many tickets.
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command?
Georgina
Georgina

It might be easier if we changed the main program, tested inventory there?

Maybe:

  •     if command == 's':
  •         num_sold = ticket_sale()
  •         if num_sold > current_inv:
  •             print("Sorry, we don't have that many tickets.")
  •         else:
  •             current_inv -= num_sold
  •             print('Cha-ching!')

Note

It's common to go back and revise your work, when you discover something you haven't allowed for. I do that all the time.

Georgina
Georgina

I like it!

Hey, we have the ticket returns, too. I bet that's similar code. Maybe:

  1.     elif command == 'r':
  2.         num_returned = ticket_return()
  3.         if num_returned > number of tickets sold:
  4.             print("Sorry, we haven't sold that many tickets.")
  5.         else:
  6.             current_inv += num_returned
Georgina
Georgina

I don't know what the missing thing in line 3 is, though.

Adela: Aha!
Adela

Idea! We need a new variable!

We've got current_inv. We subtract from that when people buy tickets, and add to it when they return tickets.

How about total_sold, or something? Add to it when someone buys tickets, subtract when they return tickets.

  1.     elif command == 'r':
  2.         num_returned = ticket_return()
  3.         if num_returned > total_sold:
  4.             print("Sorry, we haven't sold that many tickets.")
  5.         else:
  6.             current_inv += num_returned
Ethan
Ethan

That works! Although... we'd need to add the add/subtract code. I think?

  •     if command == 's':
  •         num_sold = ticket_sale()
  •         if num_sold > current_inv:
  •             print("Sorry, we don't have that many tickets.")
  •         else:
  •             current_inv -= num_sold
  •             total_sold += num_sold
  •             print('Cha-ching!')
  •     elif command == 'r':
  •         num_returned = ticket_return()
  •         if num_returned > total_sold:
  •             print("Sorry, we haven't sold that many tickets.")
  •         else:
  •             current_inv += num_returned
  •             total_sold -= num_returned

Good work!

One thing missing, though. A tiny thing, but the program won't work without it.

Hint: what does the program do with current_inv, first thing?

Reflect

What's missing?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ethan
Ethan

Oh! I got it.

Need to initialize the new variable. The top of the main program will be...

  • current_inv = 50
  • total_sold = 0

Right!

Numeric input

Georgina
Georgina

Hey, I was thinking about ticket_sale() and ticket_return(). They ask the user how many tickets were sold and returned.

Here's stuff from the I/O. I've marked some things.

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? some
  • Sorry, you must enter a number.
  • How many sold? -1
  • Sorry, please enter a number from 1 to 10.
  • How many sold? 1
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? r
  • How many returned? some more
  • Sorry, you must enter a number.
  • How many returned? -44
  • Sorry, please enter a number from 1 to 10.
  • How many returned? 1
Adela
Adela

Ooo! I see what you mean, Georgina. Most everything is the same. The error messages, the 1 to 10 range.

The only stuff that's different is the prompts.

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? s
  • How many sold? some
  • Sorry, you must enter a number.
  • How many sold? -1
  • Sorry, please enter a number from 1 to 10.
  • How many sold? 1
  • Cha-ching!
  •  
  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? r
  • How many returned? some more
  • Sorry, you must enter a number.
  • How many returned? -44
  • Sorry, please enter a number from 1 to 10.
  • How many returned? 1
Ray
Ray

Oh, snap, dawg! We write one function for sales and returns! The only thing different is the prompt, so we pass that in as a parameter!

Let me try it. I'm gonna use what we did before.

Oh, I'll change the main program, too.

  • def get_tickets_amount(prompt):
  •     is_input_ok = False
  •     while not is_input_ok:
  •         is_input_ok = True
  •         user_input = input(prompt)
  •         try:
  •             amount = int(user_input)
  •         except ValueError:
  •             print('Sorry, you must enter a number.')
  •             is_input_ok = False
  •         if is_input_ok:
  •             if amount < 1 or amount > 10:
  •                 print('Sorry, please enter a number from 1 to 10.')
  •                 is_input_ok = False
  •     return amount
  • ...
  •         num_sold = get_tickets_amount('How many sold? ')
  • ...
  •         num_returned = get_tickets_amount('How many returned? ')
Georgina
Georgina

Ooo, some spicy code, Ray, nom nom nom.

Status report

The only thing left is the status report.

  • Menu
  • ----
  •  S: sale
  •  R: return
  •  T: sTatus report
  •  Q: quit
  • Command? t
  •  
  • Status report
  • --------- ------ ------
  • Ticket inventory: 48
  • Tickets sold: 2
Adela
Adela

The main program will need some more stuff. A call to a new function. Like:

  • ...
  •     elif command == 't':
  •         show_status_report()
  • ...
Reflect

What's missing from the function call?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Georgina
Georgina

The function will need to show inventory and sales.

Adela
Adela

Oh, right.

  • ...
  •     elif command == 't':
  •         show_status_report(current_inv, total_sold)
  • ...
Reflect

Try writing the function show_status_report. Type your code in here.

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ethan
Ethan

Maybe... like this?

  • def show_status_report(inv, sold):
  •     print()
  •     print('Status report')
  •     print('------ ------')
  •     print('Ticket inventory:' + str(inv))
  •     print('Tickets sold:' + str(sold))

Looks good!

More testy testy

The Scoobies tested and tested and tested. And...

Yay!

Yeeha! It worked!

The Scoobies were a team so bright,
Their code was a pure delight.
With logic so clear,
No bugs would appear,
And their programs ran smooth day and night!

Summary

  • Top-down is good. Start with an overall plan of the program. That will tell you what the functions will do.
  • The best programs are made of small functions that are easy to write, test, and debug.
  • Use the \ to break up long code lines, to make them easier to scan.
  • Commenting out: Adding #s to the lines tells Python to ignore them, but the lines are still there.
  • The Scoobies made one function to handle both numeric inputs. It takes a prompt as a parameter.

Exercise

Exercise

Rock, paper, scissors cheater

Write a program to play the game rock, paper, scissors. Make sure the game wins every turn.

Here's some I/O:

Rock, paper, scissors
=====================

Rock (r), paper (p), or scissors (s) (Q to quit)? something
Please type r, p, s, or q.
Rock (r), paper (p), or scissors (s) (Q to quit)?
Please type r, p, s, or q.
Rock (r), paper (p), or scissors (s) (Q to quit)? r
My move: paper
I win!

Your points: 0
My points: 1

---------------
Rock (r), paper (p), or scissors (s) (Q to quit)? P
My move: scissors
I win!

Your points: 0
My points: 2

---------------
Rock (r), paper (p), or scissors (s) (Q to quit)? S
My move: rock
I win!

Your points: 0
My points: 3

---------------
Rock (r), paper (p), or scissors (s) (Q to quit)? q
OK, bye!

The program validates user input, as shown. It loops until the user exits. It reports the scores after each turn.

Use at least two functions. Use more if you want.

Upload a zip of your project folder. The usual coding standards apply.