# Stage 1: user menu

Now that we have our database.py file written, let's go ahead and make use of it inside app.py!

Table of Contents (long chapter!)

# Review existing code

As a reminder, this is what our user menu code looks like at the moment:

import database

menu = """Please select one of the following options:
1) Add new movie.
2) View upcoming movies.
3) View all movies
4) Watch a movie
5) View watched movies.
6) Exit.

Your selection: """
welcome = "Welcome to the watchlist app!"


print(welcome)
database.create_tables()

while (user_input := input(menu)) != "6":
    if user_input == "1":
        pass
    elif user_input == "2":
        pass
    elif user_input == "3":
        pass
    elif user_input == "4":
        pass
    elif user_input == "5":
        pass
    else:
        print("Invalid input, please try again!")

We need to fill in those pass statements. For each one we must:

  1. Get any required data from our users
  2. Call the appropriate database. function, passing in the data
  3. If necessary, print out the output (e.g. when getting movies from the database)

# Adding new movies

As we did in the previous chapter, here we can create a function such as prompt_add_movie() that asks the user for some input and passes it to the database module.

Let's start by asking the user for the data we'll need:

def prompt_add_movie():
    title = input("Movie title: ")
    release_date = input("Release date (dd-mm-YYYY): ")

Next up, we need to process the release date. The users are going to give us human-readable dates, but we need to turn them into timestamps that we can easily store in the database.

We'll need to use the datetime module[1] for this. Import it at the top:

import datetime

And then we have to do two things:

  1. Turn the user's input into a datetime.datetime object. That entails parsing the date.
  2. Turn the datetime.datetime object into a timestamp.




 
 

def prompt_add_movie():
    title = input("Movie title: ")
    release_date = input("Release date (dd-mm-YYYY): ")

    parsed_date = datetime.datetime.strptime(release_date, "%d-%m-%Y")
    timestamp = parsed_date.timestamp()

Now we can pass that to the database module!








 

def prompt_add_movie():
    title = input("Movie title: ")
    release_date = input("Release date (dd-mm-YYYY): ")

    parsed_date = datetime.datetime.strptime(release_date, "%d-%m-%Y")
    timestamp = parsed_date.timestamp()

    database.add_movie(title, timestamp)

We can call this from the user menu:



 











while (user_input := input(menu)) != "6":
    if user_input == "1":
        prompt_add_movie()
    elif user_input == "2":
        pass
    elif user_input == "3":
        pass
    elif user_input == "4":
        pass
    elif user_input == "5":
        pass
    else:
        print("Invalid input, please try again!")

# Viewing upcoming movies

To get upcoming movies we have to call the database.get_movies(upcoming=True) function. We also need a way of presenting the output to the user.

Let's write a function that takes a list of movies as they come from the database and prints it out nicely:

def print_movie_list(movies):
    print("-- Upcoming movies --")
    for movie in movies:
        print(f"{movie[0]} (on {movie[1]})")
    print("---- \n")

Imagine we had a few upcoming movies in our table, and we retrieve them and pass them to that function. It would print out the following:

-- Upcoming movies --
The Matrix (on 929112141)
Gone Girl (on 1412261006)
Green Book (on 1549032206)
----

Given this, we can now call it in the user menu:





 
 









while (user_input := input(menu)) != "6":
    if user_input == "1":
        prompt_add_movie()
    elif user_input == "2":
        movies = database.get_movies(True)
        print_movie_list(movies)
    elif user_input == "3":
        pass
    elif user_input == "4":
        pass
    elif user_input == "5":
        pass
    else:
        print("Invalid input, please try again!")

# Improving the printing function

At the moment the print_movie_list function always prints -- Upcoming movies --, and also prints out a timestamp instead of a nicely-formatted date.

To fix the first problem, we can pass in another argument:

 





def print_movie_list(heading, movies):
    print(f"-- {heading} movies --")
    for movie in movies:
        print(f"{movie[0]} (on {movie[1]})")
    print("---- \n")

And when we call the function, pass the heading in:

...
elif user_input == "2":
    movies = database.get_movies(True)
    print_movie_list("Upcoming", movies)
...

The second problem though requires a bit more work.

From the database we get timestamps. We need to turn those into datetime.datetime objects and then format those into human-readable dates.

Let's first parse the timestamp:




 



def print_movie_list(heading, movies):
    print(f"-- {heading} movies --")
    for movie in movies:
        movie_date = datetime.datetime.fromtimestamp(movie[1])
        print(f"{movie[0]} (on {movie[1]})")
    print("---- \n")

Then let's format that date into a human-readable date:





 
 


def print_movie_list(heading, movies):
    print(f"-- {heading} movies --")
    for movie in movies:
        movie_date = datetime.datetime.fromtimestamp(movie[1])
        human_date = movie_date.strftime("%d %b %Y")
        print(f"{movie[0]} (on {human_date})")
    print("---- \n")

That'll now show as "5 Oct 1999". That's better than "05-10-1999" because Brits and Americans can't fight over who has the better date format. And it's certainly much better than printing the timestamp itself!

# Viewing all movies

Now that we've added the ability to view upcoming movies, we'll just do the same thing but without upcoming=True:








 
 







while (user_input := input(menu)) != "6":
    if user_input == "1":
        prompt_add_movie()
    elif user_input == "2":
        movies = database.get_movies(True)
        print_movie_list("Upcoming", movies)
    elif user_input == "3":
        movies = database.get_movies()
        print_movie_list("All", movies)
    elif user_input == "4":
        pass
    elif user_input == "5":
        pass
    else:
        print("Invalid input, please try again!")

# Mark a movie as "watched"

To mark a movie as watched, we need to find out which movie the user has watched and then pass it in to the appropriate query.

Let's create a function that does that!

def prompt_watch_movie():
    movie_title = input("Enter movie title you've watched: ")
    database.watch_movie(movie_title)

And call that from the menu:











 





while (user_input := input(menu)) != "6":
    if user_input == "1":
        prompt_add_movie()
    elif user_input == "2":
        movies = database.get_movies(True)
        print_movie_list("Upcoming", movies)
    elif user_input == "3":
        movies = database.get_movies()
        print_movie_list("All", movies)
    elif user_input == "4":
        prompt_watch_movie()
    elif user_input == "5":
        pass
    else:
        print("Invalid input, please try again!")

# View watched movies

To view watched movies, we'll do the same thing as we did for "all" and "upcoming", but getting the movies from the database.get_watched_movies() funtion:













 
 



while (user_input := input(menu)) != "6":
    if user_input == "1":
        prompt_add_movie()
    elif user_input == "2":
        movies = database.get_movies(True)
        print_movie_list("Upcoming", movies)
    elif user_input == "3":
        movies = database.get_movies()
        print_movie_list("All", movies)
    elif user_input == "4":
        prompt_watch_movie()
    elif user_input == "5":
        movies = database.get_watched_movies()
        print_movie_list("Watched", movies)
    else:
        print("Invalid input, please try again!")

That's about the gist of it!

Just like in the last chapter, because all of our data interactions are encapsulated in another file, creating the menu and making use of them requires much less work!


  1. Work with dates and times in Python using datetime (Teclado) (opens new window) ↩︎