Example #1
0
def main():
    """
    Contains a menu with four capabilities; displaying a movie list, adding to the movie list,
    watching a movie from the list and quitting the program.
    """
    MENU = "Menu:\nL - List movies\nA - Add new movie\nW - Watch a movie\nQ - Quit"

    movie_collection = MovieCollection()
    movie_collection.load_movies("movies.csv")

    print("Movies To Watch 2.0 - by Jonache Hilton\n{} movies loaded\n\n{}".format(len(movie_collection.movies), MENU))
    menu_choice = input(">>>").upper()
    while menu_choice != "Q":
        if menu_choice == "L":
            display_list(movie_collection.movies, calculate_dynamic_width(movie_collection.movies), movie_collection)

        elif menu_choice == "A":
            movie_name = ""
            is_valid_input = False
            while not is_valid_input:
                print("Title:")
                movie_name = input(">>>")
                is_valid_input = check_string_error(movie_name)

            print("Year:")
            movie_year = check_integer_error()

            print("Category:")
            movie_category = ""
            is_valid_input = False
            while not is_valid_input:
                movie_category = input(">>>")
                is_valid_input = check_string_error(movie_category)

            movie_to_add = Movie(movie_name, movie_year, movie_category, False)
            movie_collection.add_movie(movie_to_add)
            print("{} ({} from {}) added to movie list".format(movie_name, movie_category, movie_year))

        elif menu_choice == "W":
            if movie_collection.get_number_of_watched_movies() == len(movie_collection.movies):
                print("No more movies to watch!")
            else:
                print("Enter the number of a movie to mark as watched")
                watch_movie(movie_collection.movies)
        else:
            print("Invalid menu choice")

        print(MENU)
        menu_choice = input(">>>").upper()

    movie_collection.save_movies("movies.csv")
Example #2
0
def run_tests():
    """Test MovieCollection class."""

    # Test empty MovieCollection (defaults)
    print("Test empty MovieCollection:")
    movie_collection = MovieCollection()
    print(movie_collection)
    assert not movie_collection.movies  # an empty list is considered False

    # Test loading movies
    print("Test loading movies:")
    movie_collection.load_movies('movies.csv')
    print(movie_collection)
    assert movie_collection.movies  # assuming CSV file is non-empty, non-empty list is considered True

    # Test adding a new Movie with values
    print("Test adding new movie:")
    movie_collection.add_movie(Movie("Amazing Grace", 2006, "Drama", False))
    print(movie_collection)

    # Test sorting movies
    print("Test sorting - year:")
    movie_collection.sort("year")
    print(movie_collection)

    print("Test sorting - title:")
    movie_collection.sort("title")
    print(movie_collection)

    print("Test sorting - category:")
    movie_collection.sort("category")
    print(movie_collection)

    print("Test sorting - watched:")
    movie_collection.sort("is_watched")
    print(movie_collection)

    print("Test getting number of watched movies:")
    print(movie_collection.get_number_of_watched_movies())

    print("Test getting number of unwatched movies:")
    print(movie_collection.get_number_of_unwatched_movies())

    print("Test saving movies (check file):")
    movie_collection.save_movies("movies.csv")
def run_tests():
    """Test MovieCollection class."""

    # Test empty MovieCollection (defaults)
    print("Test empty MovieCollection:")
    movie_collection = MovieCollection()
    print(movie_collection)
    assert not movie_collection.movies  # an empty list is considered False

    # Test loading movies
    print("Test loading movies:")
    movie_collection.load_movies('movies.csv')
    print(movie_collection)
    assert movie_collection.movies  # assuming CSV file is non-empty, non-empty list is considered True

    # Test adding a new Movie with values
    print("\nTest adding new movie:")
    movie_collection.add_movie(Movie("Amazing Grace", 2006, "Drama", False))
    print(movie_collection)

    # Test sorting movies
    print("\nTest sorting - year:")
    movie_collection.sort("year")
    print(movie_collection)
    # TODO: Add more sorting tests
    print("\nTest sorting - title:")
    movie_collection.sort("title")
    print(movie_collection)

    print("\nTest sorting - category:")
    movie_collection.sort("category")
    print(movie_collection)

    print("\nTest sorting - is_watched:")
    movie_collection.sort("is_watched")
    print(movie_collection)

    # TODO: Test saving movies (check CSV file manually to see results)
    print("\nTest saving movies:")
    movie_collection.save_movies("movies.csv")

    # TODO: Add more tests, as appropriate, for each method
    print("\nTest count movies:")
    movie_collection.count_watched_movies()
    movie_collection.count_unwatched_movies()
def run_tests():
    """Test MovieCollection class."""

    # Test empty MovieCollection (defaults)
    print("Test empty MovieCollection:")
    movie_collection = MovieCollection()
    print(movie_collection)
    assert not movie_collection.movies  # an empty list is considered False

    # Test loading movies
    print("Test loading movies:")
    movie_collection.load_movies('movies.csv')
    print(movie_collection)
    assert movie_collection.movies  # assuming CSV file is non-empty, non-empty list is considered True

    # Test adding a new Movie with values
    print("Test adding new movie:")
    movie_collection.add_movie(Movie("Chinatown", 1974, "Film Noir", False))
    print(movie_collection)

    # Test sorting movies
    print("Test sorting - year:")
    sorted_list = movie_collection.sort_movies("year", desc=True)
    print(MovieCollection.format_data(sorted_list))
    # TODO: Add more sorting tests

    # TODO: Test saving movies (check CSV file manually to see results)
    print("Test saving movie:")
    movie_collection.save_movies('movies.csv')

    # TODO: Add more tests, as appropriate, for each method
    print("Test unwatched count:")
    print(movie_collection.count_unwatch())
    print("Test watched count:")
    print(movie_collection.count_watch())

    # Add filter method
    print('Test filter method:')
    movie_collection.search_movies('star', 1977, 'action')
    print(MovieCollection.format_data(movie_collection.search_list))
def run_tests():
    """Test MovieCollection class."""

    # Test empty MovieCollection (defaults)
    print("Test empty MovieCollection:")
    movie_collection = MovieCollection()
    print(movie_collection)
    assert not movie_collection.movies  # an empty list is considered False

    # Test loading movies
    print("Test loading movies:")
    movie_collection.load_movies('movies.csv')
    print(movie_collection)
    assert movie_collection.movies  # assuming CSV file is non-empty, non-empty list is considered True

    # Test adding a new Movie with values
    print("Test adding new movie:")
    movie_collection.add_movie(Movie("Amazing Grace", 2006, "Drama", False))
    print(movie_collection)

    # Test sorting movies
    print("Test sorting - year:")
    movie_collection.sort("year")
    print(movie_collection)
Example #6
0
def run_tests():
    """Test MovieCollection class."""

    # Test empty MovieCollection (defaults)
    print("Test empty MovieCollection:")
    movie_collection = MovieCollection()
    print(movie_collection)
    assert not movie_collection.movies  # an empty list is considered False

    # Test loading movies
    print("Test loading movies:")
    movie_collection.load_movies('movies.csv')
    print(movie_collection)
    assert movie_collection.movies

    # Test adding a new Movie with values
    print("Test adding new movie:")
    movie_collection.add_movie(Movie("Cats", 2019, "Musical", "u"))     # adding new movies
    print(movie_collection)

    # Test sorting movies
    """
    print("Test sorting - year:") 
    movie_collection.sort_movie("year"). 7                
    print(movie_collection)
    """
    # Test to get number of unwatched movies
    print("Test getting number of unwatched movies:")
    print(movie_collection.get_unwatched_movies())

    # Test to get number of watched movies
    print("Test getting number of watched movies:")
    print(movie_collection.get_watched_movies())

    print("Test saving movies into the file")
    movie_collection.save_file('movies.csv')
Example #7
0
class MoviesToWatchApp(App):
    """ Kivy app for movie app"""
    display_sort = StringProperty()
    sort_type = ListProperty()

    def display_sorting(self, text):
        """ show sorting based on the user want"""
        self.current_display_sorting = text
        self.show_movie()

    def __init__(self):
        """ construct things needed for class"""
        self.movie_collection = MovieCollection()  # call movie collection
        self.movie_collection.load_movies('movies.csv')  # to open csv file
        self.movie_collection.get_unwatched_movies(
        )  # getting the number of unwatched movies
        self.movie_collection.get_watched_movies(
        )  # getting the number watch movies
        self.num_of_unwatch = self.movie_collection.unwatch_movie  # count unwatched movies
        self.num_of_watch = self.movie_collection.watch_movie  # count watched movie
        self.movie_dict = {
            movie.title: movie
            for movie in self.movie_collection.movies
        }  # to initialize the dictionary
        self.btn_movie = []  # list data for button
        self.sorting_type = list(sorting.keys())  # sorting type
        self.current_display_sorting = self.sorting_type[
            +2]  # set default sorting
        super().__init__()

    def build(self):
        """Build the Kivy app from the kv file."""
        self.title = "Movie to Watch 2.0"  # title for the kivy app
        self.root = Builder.load_file('app.kv')  # opening kivy with file name
        self.show_movie()  # to show movies
        self.root.ids.status_watch.text = (
            "To watch: {}. Watched: {}".format(self.num_of_unwatch,
                                               self.num_of_watch)
        )  # display how many movies watched and left to watch
        return self.root

    def sort_display(self, text):
        """display sorting based on the user want"""
        self.current_display_sorting = text
        self.show_movie()

    def clear(self):
        """ this is to clear data so that the previous movie not added again"""
        for value in self.btn_movie:
            self.root.ids.display_movie.remove_widget(value)

    def show_movie(self):
        """  displaying movies from list"""
        self.clear()  # clear the input value from widget
        self.movie_collection.sort_movie(sorting[self.current_display_sorting])
        self.btn_movie = []
        for movie in self.movie_collection.movies:
            color = COLORS[movie.is_watched]  # the color of the button
            text = "{} ({} from {})".format(movie.title, movie.category,
                                            movie.year)  # writing on button
            if movie.is_watched:  # if movie is watched, add this string
                text += " (Watched)"
            btn = Button(text=text, id=movie.title, background_color=color)
            btn.bind(on_release=self.change_watch)
            self.btn_movie.append(btn)
            self.root.ids.display_movie.add_widget(btn)

    def change_watch(self, instance):
        """ Change interfaces an texts on kivy when the users watch or not watch the movie"""
        watch = self.movie_dict[instance.id]
        if watch.is_watched:
            watch.check_unwatched()
            if watch.year_level():
                self.root.ids.message.text = ("You need to watch {}".format(
                    watch.title))
            else:
                watch.check_unwatched()
                self.root.ids.message.text = ("You need to watch {}".format(
                    watch.title))
        else:
            watch.check_watched()
            if watch.year_level():
                self.root.ids.message.text = ("You have watched {}".format(
                    watch.title))
            else:
                watch.check_watched()
                self.root.ids.message.text = (
                    "you have watched {}.".format(watch.title)
                )  # if the movie is watched, display this message
        self.movie_collection.get_unwatched_movies(
        )  # get number of unwatched movies
        self.movie_collection.get_watched_movies()
        # update and display number of movie watched and need to watch
        self.root.ids.status_watch.text = ("To watch: {}. Watched: {}".format(
            self.movie_collection.unwatch_movie,
            self.movie_collection.watch_movie))
        instance.background_color = COLORS[
            watch.is_watched]  # change the button colour
        self.show_movie()  # display new buttons

    def add_movie(self):
        """ method to add new movies from users"""
        title = self.root.ids.title.text  # add input
        title = title.capitalize()
        year = self.root.ids.year.text
        category = self.root.ids.category.text
        error_check = self.error_checker()  # error check input
        # after error checking done
        if not error_check:
            m = Movie(title, year, category)  # add input to the list
            self.movie_collection.add_movie(m)
            self.movie_dict[title] = m  # add into the dictionary
            self.show_movie()
            self.movie_collection.get_unwatched_movies(
            )  # to get update number of unwatched movie
            self.root.ids.status_watch.text = (
                "To watch: {}. Watched: {}".format(
                    self.movie_collection.unwatch_movie,
                    self.movie_collection.watch_movie))
            self.clear_input_movie(
            )  # clear input when the movie successfully added
            self.root.ids.message.text = ''  # check the input

    def error_checker(self):
        """ error checking user input"""
        error_check = False
        title = self.root.ids.title.text  # add input from the user
        title = title.capitalize(
        )  # auto capitalize so the input will not interfere with sort
        category = self.root.ids.category.text
        category = category.capitalize()
        year = self.root.ids.year.text

        if category not in ('Action', 'Comedy', 'Documentary', 'Drama',
                            'Fantasy', 'Thriller'):
            # prevent user from entering invalid words other than categories
            self.root.ids.message.text = (
                'Category must be one of Action, Comedy,'
                ' Documentary, Drama, Fantasy, and Thriller')
            error_check = True
            return error_check
        if not (title and year
                and category):  # asks user to fill in all fields
            self.root.ids.message.text = 'All fields must be completed.'
            error_check = True
        elif not year.isdigit(
        ):  # prevents user from inputting things other than digits
            self.root.ids.message.text = 'Please enter a valid number.'
            error_check = True
        elif len(str(year)
                 ) < 4:  # prevent user inputting numbers less than 4 digits
            self.root.ids.message.text = 'Year must be 4 digits.'
            error_check = True
        return error_check

    def clear_input_movie(self):
        """ delete all user inputs when the user clicks clear button"""
        self.root.ids.title.text = ""
        self.root.ids.year.text = ""
        self.root.ids.category.text = ""

    def on_stop(self):
        """saves updated movies into the csv file"""
        self.movie_collection.save_file("movies.csv")
Example #8
0
class MoviesToWatchApp(App):
    """MoviesToWatchApp(App) with Kivy APP following movies """
    current_spinner = StringProperty()
    message = StringProperty()
    watched_status = StringProperty()
    sorting_keys = ListProperty()

    # Construct the main app.
    def __init__(self, **kwargs):
        super(MoviesToWatchApp, self).__init__(**kwargs)
        self.movies = MovieCollection()

    # Create the main app
    def build(self):
        Window.size = (800, 500)
        self.title = "Movies Watch system 3.1 by Kyaw Soe Naing "
        self.root = Builder.load_file('app.kv')
        self.sorting_keys = SORTING_KEY.keys()
        self.current_spinner = self.sorting_keys[0]
        self.movies.load_movies("movies.csv")
        self.show_movie()
        return self.root

    # Show the movie list
    def show_movie(self):
        count = 0
        print(self.current_spinner)
        self.movies.sort(SORTING_KEY[self.current_spinner])
        self.root.ids.movies_box.clear_widgets()
        for movie in self.movies.movies:
            if movie.is_watched:
                color = [0.3, 0.7, 0.9, 0.5]
                mark = "watched"
            else:
                color = [0.8, 0.3, 1.0, 1]
                mark = ""
            movie_text = "{} ({} from {:d}) {}".format(movie.title, movie.category, movie.year, mark)
            movie_button = Button(text=movie_text, id=str(count), background_color=color)
            movie_button.bind(on_release=self.press_movie)
            self.root.ids.movies_box.add_widget(movie_button)
            count += 1
        unwatched_number = self.movies.get_unwatched()
        watched_number = self.movies.get_watched()
        self.watched_status = "To watch: {:d}. Watched: {:d}".format(unwatched_number, watched_number)

    # check watch and unwatch
    def press_movie(self, instance):
        """   """
        movie_id = int(instance.id)
        movie_data = self.movies.movies[movie_id]
        movie_title = movie_data.title

        if movie_data.is_watched:
            movie_data.mark_unwatched()
            self.message = "You need to watch {:s}".format(movie_title)
        else:
            movie_data.mark_watched()
            self.message = "You have watched {:s}".format(movie_title)
        self.movies.sort(SORTING_KEY[self.current_spinner])
        self.show_movie()

    # sort key for sorting
    def choose_key(self, key):
        self.current_spinner = key
        self.show_movie()

    # add new movie to movies
    def add_movie(self):
        m_title = self.root.ids.input_title.text
        m_year = self.root.ids.input_year.text
        m_category = self.root.ids.input_cate.text

        m_title = m_title.strip()
        m_year = m_year.strip()
        m_category = m_category.strip().upper()

        if (m_title == "") or (m_year == "") or (m_category == ""):
            self.message = "All fields must be completed"
            return

        if not m_year.isdigit():
            self.message = "Please enter a valid number"
            return

        m_year = int(m_year)
        if m_year < 0:
            self.message = "Year must be more than 0"
        found = False
        count = 0
        for i in range(len(CATEGORY_LIST)):
            if m_category == CATEGORY_LIST[i].upper():
                found = True
                count = i
                break
        if not found:
            self.message = "The category must be one of Action, Comedy, Documentary, Drama, Fantasy, Thriller"
            return
        else:
            m_category = CATEGORY_LIST[count]

        new_movie = Movie(m_title, m_year, m_category)
        self.movies.add_movie(new_movie)
        self.all_clear()
        self.show_movie()

    # clear all the fill box
    def all_clear(self):
        self.root.ids.input_title.text = ""
        self.root.ids.input_cate.text = ""
        self.root.ids.input_year.text = ""
        self.message = ""

    def stop_now(self):
        self.movies.save_movies("movies.csv")
Example #9
0
class MoviesToWatchApp(App):
    """MoviesToWatchApp is a Kivy App for keeping track of movies from a file """
    bottom_status_text = StringProperty()
    top_status_text = StringProperty()

    def __init__(self, **kwargs):
        """Construct main app."""
        super().__init__(**kwargs)
        self.movie_collection = MovieCollection()
        self.movie_collection.load_movies("movies.csv")
        self.sorted_by = "category"

    def build(self):
        """
        Build the Kivy GUI.
        :return: reference to the root Kivy widget
        """
        self.title = "Movies To Watch 2.0 - by Jonache Hilton"
        self.root = Builder.load_file('app.kv')
        self.sort_movies(self.sorted_by)
        return self.root

    def create_widgets(self):
        """
        Create buttons from list of Movie objects and add them to the GUI.
        :return: None
        """
        self.clear_movie_widgets()
        self.top_status_text = "To watch: {}. Watched: {}".format(
            self.movie_collection.get_number_of_unwatched_movies(),
            self.movie_collection.get_number_of_watched_movies())
        for movie in self.movie_collection.movies:
            # Create a button for each Movie object, specifying the text
            temp_button = Button(text=str(movie))
            temp_button.bind(on_release=self.press_movie)
            # Store a reference to the movie object in the button object
            temp_button.movie = movie
            self.root.ids.movie_box.add_widget(temp_button)
            if movie.is_watched:
                temp_button.background_color = GREEN_COLOUR
            else:
                temp_button.background_color = RED_COLOUR

    def press_movie(self, instance):
        """
        Handle pressing movie buttons, changing watched status of a Movie and updating display.
        :param instance: the Kivy button instance
        :return: None
        """
        # Each button is given its own ".movie" object reference, to get it directly
        movie = instance.movie
        if movie.is_watched:
            movie.unwatch()
            watched_string = "You need to watch"
        else:
            movie.watch()
            watched_string = "You have watched"
        # Update button text
        instance.text = str(movie)
        self.sort_movies(self.sorted_by)
        self.bottom_status_text = "{} {}".format(watched_string, movie.title)

    def press_add_movie(self):
        """
        Handle pressing add movie button, adding Movie object to list and updating display.
        :return: None
        """
        # Check if any errors occur before continuing
        is_valid_input = self.check_text_input_errors()
        if is_valid_input:
            movie_to_add = Movie(self.root.ids.title_input.text,
                                 int(self.root.ids.year_input.text),
                                 self.root.ids.category_input.text.title(),
                                 False)
            self.movie_collection.add_movie(movie_to_add)
            self.clear_fields()
            self.sort_movies(self.sorted_by)
            self.clear_bottom_status_text()

    def check_text_input_errors(self):
        """Check text inputs for a range of errors and returns True if no errors have occurred."""
        try:
            # Check if any of the text inputs are blank
            if self.root.ids.title_input.text == "" or self.root.ids.year_input.text == "" or \
                    self.root.ids.category_input.text == "":
                self.bottom_status_text = "All fields must be completed"
                return False

            elif int(self.root.ids.year_input.text) < 0:
                self.bottom_status_text = "Year must be >= 0"
                return False

            elif self.root.ids.category_input.text.title() not in [
                    "Action", "Comedy", "Documentary", "Drama", "Fantasy",
                    "Thriller"
            ]:
                self.bottom_status_text = "The category must be one of the following: Action, Comedy, Documentary," \
                                          " Drama, Fantasy, Thriller"
                return False
        except ValueError:
            self.bottom_status_text = "Please enter a valid number"
        else:
            return True

    def sort_movies(self, sorted_by):
        """
        Sort the movie widgets based on the sorted_by parameter.
        :param sorted_by: the selected sorting method from the GUI spinner
        :return: None
        """
        self.sorted_by = sorted_by.lower()
        # Convert "watched" to "is_watched" as "watched" is not a valid for attrgetter
        if self.sorted_by == "watched":
            self.sorted_by = "is_watched"
        self.movie_collection.sort(self.sorted_by)
        self.create_widgets()

    def clear_fields(self):
        """
        Clear the text input fields.
        :return: None
        """
        self.root.ids.title_input.text = ""
        self.root.ids.category_input.text = ""
        self.root.ids.year_input.text = ""

    def clear_movie_widgets(self):
        """
        Clear all of the widgets that are children of the "movie_box" layout widget.
        :return: None
        """
        self.root.ids.movie_box.clear_widgets()

    def clear_bottom_status_text(self):
        """
        Clear the bottom status text.
        :return: None
        """
        self.bottom_status_text = ""

    def on_stop(self):
        """
        Save movies back to file when GUI is closed.
        :return: None
        """
        self.movie_collection.save_movies("movies.csv")
class MoviesToWatchApp(App):
    """Watchmovies app GUI version"""
    sort_by = StringProperty()
    category = ListProperty()
    order = ListProperty()
    current_order = StringProperty()

    def __init__(self, **kwargs):
        """Creat the core of Watchmovies app"""
        Window.size = (900, 700)
        super(MoviesToWatchApp, self).__init__(**kwargs)
        self.my_collection = MovieCollection()
        self.movie_to_show = []

    def build(self):
        """Build kivy GUI file"""
        self.title = "Movie To Watch 2.0 by JialeTang "
        self.root = Builder.load_file('app.kv')
        # Setting category lists
        self.category = ['Title', 'Year', 'Category']
        self.sort_by = self.category[0]
        return self.root

    def on_start(self):
        self.my_collection.load_movies('movies.csv')
        self.movie_to_show = self.my_collection.movies
        self.root.ids.movie_list.bind(
            minimum_height=self.root.ids.movie_list.setter('height'))
        # Show the message
        self.root.ids.message.text = 'Let\'s watch some movies :)'
        # Load movies
        self.load_movies()
        # Showing watched or unwatch
        self.count_watch()

    def on_stop(self):
        """Saving the data to csv when close the app"""
        self.my_collection.save_movies('movies.csv')

    def count_watch(self):
        self.root.ids.watch_count.text = 'To watch: {}. Watched: {}'.format(
            self.my_collection.count_unwatch(),
            self.my_collection.count_watch())

    def sort_movies(self, key):
        """Sort movie based on key"""
        self.sort_by = key
        self.load_movies()

    def handle_order(self, element):
        """Sort movie based on order"""
        self.current_order = element
        self.load_movies()

    def handle_add_movie(self, title, year, category):
        """Add movies to the movies list"""
        # Only add movie when title, year, category are provided
        if title and year and category:
            title_check = self.handle_input(title, is_title=True)
            category_check = self.handle_input(category, is_category=True)
            year_check = self.handle_input(year, is_year=True)
            if year_check and category_check and title_check:
                clean_title = ' '.join(title.split())
                pretty_title = capwords(clean_title)
                if self.check_exist(title_check, year_check, category_check):
                    self.show_popup_message('The movie is already exist')
                else:
                    self.my_collection.add_movie(
                        Movie(title_check, year_check, category_check))
                    self.load_movies()
                    self.show_popup_message(
                        '{} have been add to movie list'.format(pretty_title))
                    self.handle_clear_button(is_add=True)

        else:
            # Showing error message if any field is blank
            self.show_popup_message('All fields are required')

    def load_movies(self):
        self.root.ids.movie_list.clear_widgets()
        desc = self.current_order == 'Descending Order'
        self.movie_to_show = self.my_collection.sort_movies(self.sort_by, desc)
        for index, movie in enumerate(self.movie_to_show):
            watch_mark = 'watched' if movie.is_watched else ''
            btn = Button(text='{} ({} from {}) {}'.format(
                movie.title, movie.category, movie.year, watch_mark),
                         size_hint_y=None,
                         height=200)
            btn.movie = movie
            btn.bind(on_press=self.handle_watch_movie)
            # background color
            if watch_mark:
                btn.background_color = (0.5, 0.25, 1.0, 1.0)
            else:
                btn.background_color = (1, 0, 0, 5)
            self.root.ids.movie_list.add_widget(btn)

    def handle_watch_movie(self, instance):
        """Handle watch movie if user click on movie"""
        current_movie = instance.movie
        current_movie.is_watched = not current_movie.is_watched
        self.load_movies()
        watch_mark = 'watched' if current_movie.is_watched else 'unwatched'

        self.root.ids.message.text = 'You have {} {}'.format(
            watch_mark, current_movie.title)
        self.count_watch()

    def show_popup_message(self, text):
        """show popup message"""
        self.root.ids.popup_message.text = text
        self.root.ids.popup.open()

    def handle_close_popup(self):
        """Close the popup"""
        self.root.ids.popup_message.text = ''
        self.root.ids.popup.dismiss()

    def handle_clear_button(self, is_add=False, is_search=False):
        """Clear input when pressed"""
        if is_add:
            self.root.ids.title.text = ''
            self.root.ids.year.text = ''
            self.root.ids.category.text = ''
        elif is_search:
            self.root.ids.title_search.text = ''
            self.root.ids.year_search.text = ''
            self.root.ids.category_search.text = ''
            self.root.ids.watch_search.text = ''
        # Else clear all
        else:
            self.root.ids.title.text = ''
            self.root.ids.year.text = ''
            self.root.ids.category.text = ''
            self.root.ids.title_search.text = ''
            self.root.ids.year_search.text = ''
            self.root.ids.category_search.text = ''
            self.root.ids.watch_search.text = ''

    def check_exist(self, title, year, category):
        """Check if movie is existed"""
        def find_duplicate(movie):
            filter_title = title.lower() == movie.title.lower()
            filter_year = int(movie.year) == int(year)
            filter_category = movie.category.lower() == category.lower()
            return filter_title and filter_year and filter_category

        return list(filter(find_duplicate, self.my_collection.movies))

    def handle_input(self,
                     input_data,
                     is_title=False,
                     is_year=False,
                     is_category=False,
                     is_watch=False,
                     blank=False):
        """Handle input data"""
        if blank and not input_data:
            return True
        else:
            if is_year:
                try:
                    year = int(input_data)
                    if year < 0:
                        raise ValueError()
                    return input_data.strip()
                except ValueError:
                    self.show_popup_message(
                        'Your year must be a number and greater than 0')

            elif is_category:
                if input_data.lower().strip() not in [
                        'action', 'comedy', 'documentary', 'drama', 'fantasy',
                        'thriller'
                ]:
                    self.show_popup_message(
                        'Please enter a correct category '
                        '(Action, Comedy, Documentary, Drama, Fantasy, Thriller)'
                    )
                else:
                    return input_data.strip()
            elif is_watch:
                if input_data.lower() not in ['y', 'n']:
                    self.show_popup_message('Your watch field must be Y or N')
                else:
                    return True
            elif not input_data.strip() and is_title:
                self.show_popup_message('Your title must not be blank')
            else:
                return input_data.strip()
Example #11
0
def run_tests():
    """Test MovieCollection class."""

    # Test empty MovieCollection (defaults)
    print("Test empty MovieCollection:")
    movie_collection = MovieCollection()
    print(movie_collection)
    assert not movie_collection.movies  # an empty list is considered False

    # Test loading movies
    print("\nTest loading movies:")
    movie_collection.load_movies('movies.csv')
    print(movie_collection)
    assert movie_collection.movies  # assuming CSV file is non-empty, non-empty list is considered True

    # Test adding a new Movie with values
    print("\nTest adding new movie:")
    movie_collection.add_movie(Movie("Amazing Grace", 2006, "Drama", False))
    print(movie_collection)

    # Test sorting movies
    print("\nTest sorting - year:")
    movie_collection.sort_movies("year")
    print(movie_collection)

    print("\nTest sorting - title:")
    movie_collection.sort_movies("title")
    print(movie_collection)

    # Test saving movies (check CSV file manually to see results)
    movie_collection.save_movies('test.csv')

    # test number of un/watched movies
    print("\nTest get_number_watched:")
    print("expected 2 got {}".format(movie_collection.get_number_watched()))

    print("\nTest get_number_un_watched")
    print("expected 4 got {}".format(movie_collection.get_number_un_watched()))

    # Test length of movies
    print("\nTest __len__:")
    print("Expected 6 got {}".format(len(movie_collection)))

    print("\nTest calculate_longest_title:")
    print("Expected 34 got {}".format(
        movie_collection.calculate_longest_title()))

    print("\nTest list_movies prints to console:")
    longest_title_length = 35
    movie_collection.sort_movies('year')
    movie_collection.list_movies(longest_title_length)

    print("\nTest set_movie_watched:")
    movie_collection.set_movie_watched(0)
    movie_collection.list_movies(35)
    movie_collection.set_movie_watched(0)

    print("\nTest bool_to_status:")
    print(movie_collection)
    movie_collection.bool_to_status()
    print(movie_collection)
Example #12
0
class MainApp(App):
    """ MainApp is a Kivy App for managing watched/unwatched films """
    current_state = StringProperty()
    state_codes = ListProperty()

    def build(self):
        self.initCollection()
        """ Method to save the movies when quiting..."""
        Window.bind(on_request_close=self.on_request_close)
        """ build Kivy app from the kv file """
        self.title = "Movies to Watch 2.1"
        self.root = Builder.load_file('app.kv')
        self.state_codes = sorted(STATES.keys())
        self.current_state = self.state_codes[0]
        self.update_status_label()
        return self.root

    def initCollection(self):
        """ Method to initialize movie and movie collection instances"""
        self.movie = Movie()
        self.movie_collection = MovieCollection()
        self.movie_collection.load_movies(FILE_NAME)
        self.movie_collection.sort("category")

    def change_state(self, state_code):
        """ handle change of spinner selection, output result to sort dinamic widgets """
        self.Sort(STATES[state_code])

    def update_event_label(self, text):
        self.root.ids.event_label.text = text

    def update_status_label(self):
        watched = self.movie_collection.movie_watched_count()
        unwatched = self.movie_collection.movie_unwatched_count()
        self.root.ids.status_label.text = "To watch: {}, Watched: {}".format(
            unwatched, watched)

    def Sort(self, key):
        """ Method to sort movies
        *when sorting key is 'watched(GUI)/is_watched(app)' it will do reverse sort """
        self.root.ids.entries_box.clear_widgets()
        if key == "is_watched":
            self.movie_collection.sort(key, reverse=True)
        else:
            self.movie_collection.sort(key)

        self.create_movie_buttons()

    def clear(self):
        """Method to clears texts and inputs"""
        self.root.ids.input_title.text = ""
        self.root.ids.input_category.text = ""
        self.root.ids.input_year.text = ""
        self.update_event_label("")
        self.root.ids.status_label.text = ""

    def add_movie(self):
        """Method to add a new movie and set the statuses"""
        title = self.root.ids.input_title.text
        category = self.root.ids.input_category.text
        year = self.root.ids.input_year.text

        if not title or not category or not year:
            self.update_event_label("All fields must be completed")
            return
        if category not in ALLOWED_CATEGORIES:
            self.update_event_label("Allowed categories are " +
                                    ", ".join(ALLOWED_CATEGORIES))
            return

        self.movie_collection.add_movie((title, year, category, False))
        self.root.ids.entries_box.clear_widgets()
        self.create_movie_buttons()

        self.root.ids.input_title.text = ""
        self.root.ids.input_category.text = ""
        self.root.ids.input_year.text = ""

    def create_movie_buttons(self):
        """
        Create the movie buttons and add them to the GUI
        """
        for movie in self.movie_collection.movies:
            # create a button for each phonebook entry
            temp_button = Button(text=str(movie))
            temp_button.bind(on_release=self.press_entry)
            temp_button.state = 'down' if movie.is_watched else 'normal'
            temp_button.movie = movie
            temp_button.background_color = WATCHED_COLOR if temp_button.state == 'down' else UN_WATCHED_COLOR
            self.root.ids.entries_box.add_widget(temp_button)

    def press_entry(self, instance):
        """ Method to set triggered button's state (activity, color, watched),
        Method sets status about marked movie,
        Method called when movie buttons triggered on_release signal"""
        if instance.state == 'down':
            instance.state = 'normal'
        else:
            instance.state = 'down'

        name = instance.text

        if instance.state == 'down':
            instance.movie.mark_watched()
            instance.background_color = WATCHED_COLOR
            self.update_event_label("You have {} {}".format("watched", name))
        else:
            instance.movie.mark_unwatched()
            instance.background_color = UN_WATCHED_COLOR

        self.update_status_label()

    def on_request_close(self, *args, **kwargs):
        # print cd u("------closeing"*2)
        self.movie_collection.save_movies(FILE_NAME)
class MoviesToWatchApp(App):
    """Kivy app constructor class to create GUI for assignment 2."""
    movies_to_watch_text = StringProperty()
    status_text = StringProperty()
    current_selection = StringProperty()
    sort_options = ListProperty()

    def __init__(self, **kwargs):
        """Load movies from file."""
        super().__init__(**kwargs)
        self.movie_collection = MovieCollection()
        self.movie_collection.load_movies(FILE_NAME)

    def build(self):
        """Build the Kivy GUI."""
        self.title = "Movies To Watch 2.0"
        self.root = Builder.load_file('app.kv')
        self.sort_options = sorted(SPINNER_OPTIONS_TO_KEYWORD.keys())
        self.current_selection = self.sort_options[0]
        self.movies_to_watch_text = "To watch: {} Watched: {}".format(
            self.movie_collection.get_number_un_watched(),
            self.movie_collection.get_number_watched())
        return self.root

    def create_widgets(self):
        """Create buttons from MovieCollection entries and add them to the GUI."""
        for movie in self.movie_collection.movies:
            display_color = self.set_button_color(movie)
            # create a button for each data entry, specifying the text and id
            temp_button = Button(text=self.display_watched(movie),
                                 id=movie.title,
                                 background_color=display_color)
            temp_button.bind(on_release=self.handle_press_movie)
            # Store a reference to the movie object in the button object
            temp_button.movie = movie
            # add the button to the "entries_box" layout widget
            self.root.ids.entries_box.add_widget(temp_button)

    @staticmethod
    def set_button_color(movie):
        """Set color code depending on movie.is_watched."""
        display_color = UN_WATCHED_COLOR
        if movie.is_watched:
            display_color = WATCHED_COLOR
        return display_color

    def handle_press_movie(self, instance):
        """Handle pressing movie buttons."""
        # toggle watched / un watched
        if instance.movie.is_watched:
            instance.movie.un_watch_movie()
        else:
            instance.movie.watch_movie()
        # update button colour
        instance.background_color = self.set_button_color(instance.movie)
        # update status text
        unwatched_string = 'need to watch'
        if instance.movie.is_watched:
            unwatched_string = 'have watched'
        # Change status text if movie is unwatched
        self.status_text = "You {} {}".format(unwatched_string,
                                              instance.movie.title)
        instance.text = self.display_watched(instance.movie)
        self.update_movie_buttons()
        # update movies to watch text
        self.movies_to_watch_text = "To watch: {} Watched: {}".format(
            self.movie_collection.get_number_un_watched(),
            self.movie_collection.get_number_watched())

    def change_spinner_selection(self, new_sort_selection):
        """Handle changing spinner sort condition."""
        self.current_selection = new_sort_selection
        self.update_movie_buttons()

    def update_movie_buttons(self):
        """Update movie button order in GUI."""
        self.movie_collection.sort_movies(
            SPINNER_OPTIONS_TO_KEYWORD[self.current_selection])
        self.root.ids.entries_box.clear_widgets()
        self.create_widgets()

    def handle_press_add_movie(self, new_title, new_year, new_category):
        """Handle adding a new movie object."""
        if self.is_valid_inputs(new_title, new_year, new_category):
            self.movie_collection.add_movie(
                Movie(new_title, int(new_year), new_category, False))
            # create a button for new movie
            temp_button = Button(text="{} ({} from {})".format(
                new_title, new_category, new_year),
                                 id=new_title,
                                 background_color=UN_WATCHED_COLOR)
            temp_button.bind(on_release=self.handle_press_movie)
            # Store a reference to the movie object in the button object
            temp_button.movie = self.movie_collection.movies[-1]
            # clear text fields in entry boxes
            self.clear_fields()
            self.update_movie_buttons()

    def is_valid_inputs(self, title, year, category):
        """Check if user inputs meet requirements."""
        input_fields = [title, year, category]
        # check no empty input fields
        for field in input_fields:
            if field == '':
                self.status_text = "All fields must be completed"
                return False
        # check year is a number
        try:
            year = int(year)
        except ValueError:
            self.status_text = "Please enter a valid number"
            return False
        # check year is >=0
        if not year >= 0:
            self.status_text = "Year must be >=0"
            return False
        # check valid category
        valid_category_options = [
            "Action", "Comedy", "Documentary", "Drama", 'Fantasy', 'Thriller'
        ]
        if category not in valid_category_options:
            self.status_text = "Category must be one of Action, Comedy, Documentary, Drama, Fantasy, Thriller"
            return False
        return True

    def clear_fields(self):
        """Clear text inputs and status bar on press."""
        self.root.ids.new_title.text = ''
        self.root.ids.new_year.text = ''
        self.root.ids.new_category.text = ''
        self.status_text = ''

    @staticmethod
    def display_watched(instance):
        """Display 'watched' after titles that have been watched."""
        watched_string = 'watched'
        if not instance.is_watched:
            watched_string = ''
        button_display_text = instance.text = "{} ({} from {}) {}".format(
            instance.title, instance.category, instance.year, watched_string)
        return button_display_text

    def on_stop(self):
        """Run when the app exits and save movies to file."""
        self.movie_collection.bool_to_status()
        self.movie_collection.save_movies(FILE_NAME)
Example #14
0
class MoviesToWatchApp(App):
    """Class for GUI movie app"""
    # Initiate string and list properties
    sort_by = StringProperty()
    category = ListProperty()
    order = ListProperty()
    current_order = StringProperty()

    def __init__(self, **kwargs):
        """Constructor that initiate my collection and movie list"""
        super(MoviesToWatchApp, self).__init__(**kwargs)
        self.my_collection = MovieCollection()
        # Create another list to not TOUCH the original list, in case for back-up or error
        self.movie_to_show = []

    def build(self):
        """Build from the root kv file"""
        Window.size = (1000, 800)
        self.title = "Movie To Watch 2.0 by Van Phuong Nguyen"
        self.root = Builder.load_file('app.kv')
        # Set category list
        self.category = ['Title', 'Year', 'Category', 'Unwatch']
        # Default sorting option
        self.sort_by = self.category[0]
        # Set order list
        self.order = ['Ascending Order', 'Descending Order']
        # Set default order
        self.current_order = self.order[0]
        return self.root

    def on_start(self):
        """Start when program start"""
        self.my_collection.load_movies('movies.csv')
        # Separate movie list to not change to original list
        self.movie_to_show = self.my_collection.movies
        # Make sure the height is such that there is something to scroll.
        self.root.ids.movie_list.bind(
            minimum_height=self.root.ids.movie_list.setter('height'))
        # Show welcome message
        self.root.ids.message.text = 'Let\'s watch some movies :)'
        # Load movies
        self.load_movies()
        # Show watch and unwatch count
        self.count_watch()

    def on_stop(self):
        """Close program and save movies to the file"""
        self.my_collection.save_movies('movies.csv')

    def count_watch(self):
        """Count movie based on watch and unwatch"""
        self.root.ids.watch_count.text = 'To watch: {}. Watched: {}'.format(
            self.my_collection.count_unwatch(),
            self.my_collection.count_watch())

    def sort_movies(self, key):
        """Sort movie based on key"""
        # Change current sort_by key based on the key from the sort spinner
        self.sort_by = key
        # Load movies based on key provided
        self.load_movies()

    def handle_order(self, element):
        """Sort movie based on order"""
        # Change current order for loading
        self.current_order = element
        # Load movie based on current order
        self.load_movies()

    def handle_add_movie(self, title, year, category):
        """Add movie to movie list"""
        # Only add movie when title, year, category are provided
        if title and year and category:
            # Make sure that title input is correct
            title_check = self.handle_input(title, is_title=True)
            # Make sure that category is on category list
            category_check = self.handle_input(category, is_category=True)
            # Make sure that year is a number >= 0
            year_check = self.handle_input(year, is_year=True)
            if year_check and category_check and title_check:
                # Make the input prettier
                clean_title = ' '.join(title.split())
                pretty_title = capwords(clean_title)
                pretty_category = capwords(category)
                # Check if  movie is already exist
                if self.check_exist(title_check, year_check, category_check):
                    self.show_popup_message('The movie is already exist')
                else:
                    # Add movie to list, then reload movie list
                    self.my_collection.add_movie(
                        Movie(title_check, year_check, category_check))
                    self.load_movies()
                    self.show_popup_message(
                        '{} have been add to movie list'.format(pretty_title))
                    self.handle_clear_button(is_add=True)

        else:
            # Show error if any field blank
            self.show_popup_message('All fields are required')

    def load_movies(self):
        """Load movie to the GUI movie list"""
        # First clear the current movie on list
        self.root.ids.movie_list.clear_widgets()
        # Check the current order
        desc = self.current_order == 'Descending Order'
        # Add movies based on current sort_by and order to movie to show list
        self.movie_to_show = self.my_collection.sort_movies(self.sort_by, desc)
        # Add buttons based on movie list
        for index, movie in enumerate(self.movie_to_show):
            watch_mark = 'watched' if movie.is_watched else ''
            btn = Button(text='{} ({} from {}) {}'.format(
                movie.title, movie.category, movie.year, watch_mark),
                         size_hint_y=None,
                         height=30)
            # Save movie object to btn
            btn.movie = movie
            # If pressed, execute handle_watch_movie function
            btn.bind(on_press=self.handle_watch_movie)
            # If movie is watched, change background color
            if watch_mark:
                btn.background_color = (1, 0.5, 0.5, 1)
            # Add btn to movie_list id
            self.root.ids.movie_list.add_widget(btn)

    def handle_watch_movie(self, instance):
        """Handle watch movie if user click on movie"""
        # Movie object is saved to btn.movie >> instance.movie
        current_movie = instance.movie
        # Toggle between watch/unwatch
        current_movie.is_watched = not current_movie.is_watched
        # Load movie to the GUI list for immediate sorting
        self.load_movies()
        # Show message and reload the count watch
        watch_mark = 'watched' if current_movie.is_watched else 'unwatched'

        self.root.ids.message.text = 'You have {} {}'.format(
            watch_mark, current_movie.title)
        self.count_watch()

    def show_popup_message(self, text):
        """Handle show popup message"""
        self.root.ids.popup_message.text = text
        self.root.ids.popup.open()

    def handle_close_popup(self):
        """Close the popup"""
        self.root.ids.popup_message.text = ''
        self.root.ids.popup.dismiss()

    def handle_clear_button(self, is_add=False, is_search=False):
        """Clear input when pressed"""
        # If is_add, clear the add movie input
        if is_add:
            self.root.ids.title.text = ''
            self.root.ids.year.text = ''
            self.root.ids.category.text = ''
        # If is_search, clear the search input
        elif is_search:
            self.root.ids.title_search.text = ''
            self.root.ids.year_search.text = ''
            self.root.ids.category_search.text = ''
            self.root.ids.watch_search.text = ''
        # Else clear all
        else:
            self.root.ids.title.text = ''
            self.root.ids.year.text = ''
            self.root.ids.category.text = ''
            self.root.ids.title_search.text = ''
            self.root.ids.year_search.text = ''
            self.root.ids.category_search.text = ''
            self.root.ids.watch_search.text = ''

    def handle_search(self, title, year, category, watch):
        """Search for movie in list"""
        # Only search when at least on field is provided
        if title or category or watch or year:
            # Check for valid year, category and title
            title_check = self.handle_input(title, is_title=True, blank=True)
            year_check = self.handle_input(year, is_year=True, blank=True)
            category_check = self.handle_input(category,
                                               is_category=True,
                                               blank=True)
            watch_check = self.handle_input(watch, is_watch=True, blank=True)
            # If all are valid, then search
            if title_check and year_check and category_check and watch_check:
                # If is_watched is provided, change to bool, else None
                is_watched = None
                if watch:
                    is_watched = watch.lower() == 'y'
                # Search movie method
                self.my_collection.search_movies(title.strip(), year, category,
                                                 is_watched)
                # If found, show movie count and display in GUI movie list
                if self.my_collection.search_list:
                    self.movie_to_show = self.my_collection.search_list
                    self.show_popup_message('We have found: {} movies'.format(
                        len(self.movie_to_show)))
                    self.load_movies()
                    # Clear the input when completed
                    self.handle_clear_button(is_search=True)
                # If no movie found, show error message
                else:
                    self.show_popup_message('No movie found!')
        else:
            # If no field is fill in, show error message
            self.show_popup_message('Your must at least fill in one field')

    def handle_clear_search(self):
        """Clear the search and return the original list"""
        # Set search list to empty
        self.my_collection.search_list = []
        self.handle_clear_button(is_search=True)
        self.load_movies()

    def check_exist(self, title, year, category):
        """Check if movie is existed"""

        # Filter method based on title, year, category
        def find_duplicate(movie):
            filter_title = title.lower() == movie.title.lower()
            filter_year = int(movie.year) == int(year)
            filter_category = movie.category.lower() == category.lower()
            return filter_title and filter_year and filter_category

        return list(filter(find_duplicate, self.my_collection.movies))

    def handle_input(self,
                     input_data,
                     is_title=False,
                     is_year=False,
                     is_category=False,
                     is_watch=False,
                     blank=False):
        """Handle input data"""
        # Check if year > 0 and is a number
        if blank and not input_data:
            return True
        else:
            if is_year:
                try:
                    year = int(input_data)
                    if year < 0:
                        raise ValueError()
                    return input_data.strip()
                except ValueError:
                    self.show_popup_message(
                        'Your year must be a number and greater than 0')
            # Check if input in category list
            elif is_category:
                # Check if category is in the category list
                if input_data.lower().strip() not in [
                        'action', 'comedy', 'documentary', 'drama', 'fantasy',
                        'thriller'
                ]:
                    self.show_popup_message(
                        'Please enter a correct category '
                        '(Action, Comedy, Documentary, Drama, Fantasy, Thriller)'
                    )
                else:
                    return input_data.strip()
            # Check if valid watch
            elif is_watch:
                if input_data.lower() not in ['y', 'n']:
                    self.show_popup_message('Your watch field must be Y or N')
                else:
                    return True
            # Check if title is blank
            elif not input_data.strip() and is_title:
                self.show_popup_message('Your title must not be blank')
            else:
                return input_data.strip()
Example #15
0
class MoviesToWatchApp(App):
    """..."""
    status_text = StringProperty()
    head_right = StringProperty()
    state_codes = ListProperty()

    def __init__(self, **kwargs):
        """Construct main app."""
        super().__init__(**kwargs)
        # basic data example - dictionary of names: phone numbers
        # TODO: After running it, add another entry to the dictionary and see how the layout changes
        # self.name_to_phone = {"Bob Brown": "0414144411", "Cat Cyan": "0441411211", "Oren Ochre": "0432123456"}
        self.movie_collection = MovieCollection()
        self.movie_collection.load_movies('movies.csv')

    def build(self):
        """Build the Kivy GUI."""
        self.title = "Movies To Watch 2.0"
        self.head_left = "Sort by:"
        self.head_right = f"To watch: {self.movie_collection.get_num_unwatched()}. Watched: {self.movie_collection.get_num_watched()}"
        self.content_add = "Add new movie"
        self.content_title = "Title: "
        self.content_category = "Category: "
        self.content_year = "Year: "
        self.state_codes = KEYS
        self.key = self.state_codes[0]
        self.root = Builder.load_file('app.kv')
        self.create_widgets()
        return self.root

    def create_widgets(self):
        """Create buttons from dictionary entries and add them to the GUI."""
        self.movie_collection.sort(self.key)
        for movie in self.movie_collection.movies:
            # create a button for each data entry, specifying the text and id
            # (although text and id are the same in this case, you should see how this works)
            watched = "watched" if movie.is_watched else ""
            name = f"{movie.title} ({movie.category} from {movie.year}) {watched}"
            temp_button = Button(text=name)
            temp_button.background_color = (0, 1, 0,
                                            1) if watched == "watched" else (1,
                                                                             0,
                                                                             0,
                                                                             1)
            temp_button.bind(on_release=self.press_entry)
            temp_button.movie = movie
            # add the button to the "entries_box" layout widget
            self.root.ids.entries_box.add_widget(temp_button)
        self.head_right = f"To watch: {self.movie_collection.get_num_unwatched()}. Watched: {self.movie_collection.get_num_watched()}"

    def press_entry(self, instance):
        """
        Handle pressing entry buttons.
        :param instance: the Kivy button instance that was clicked
        """
        # get name (dictionary key) from the id of Button we clicked on
        movie = instance.movie
        # update status text
        watched = movie.is_watched
        if watched:
            movie.mark_unwatched()
            instance.text = f"{movie.title} ({movie.category} from {movie.year})"
            instance.background_color = (1, 0, 0, 1)
            self.status_text = f"You have not watched {movie.title}"
        else:
            movie.mark_watched()
            instance.text = f"{movie.title} ({movie.category} from {movie.year}) watched"
            instance.background_color = (0, 1, 0, 1)
            self.status_text = f"You have watched {movie.title}"
        self.head_right = f"To watch: {self.movie_collection.get_num_unwatched()}. Watched: {self.movie_collection.get_num_watched()}"

    def clear_all(self):
        """Clear all of the widgets that are children of the "entries_box" layout widget."""
        self.root.ids.entries_box.clear_widgets()

    def change_state(self, state_code):
        """ handle change of spinner selection, output result to label widget """
        self.key = state_code
        self.clear_all()
        self.create_widgets()

    def press_add(self, added_title, added_category, added_year):
        """
        Handler for pressing the add button
        :return: None
        """
        if added_title and added_category and added_year:
            if added_year.isdigit():
                if added_category in CATEGORIES:
                    self.movie_collection.add_movie(
                        Movie(added_title, int(added_year), added_category))
                    self.clear_all()
                    self.create_widgets()
                else:
                    self.status_text = "The category must be one of the following: Action, Comedy, Documentary, " \
                                       "Drama, Fantasy, Thriller "
            else:
                self.status_text = "Please enter a valid number"
        else:
            self.status_text = "All fields must be completed"

    def clear_fields(self):
        """
        Clear the text input fields from the add entry popup
        If we don't do this, the popup will still have text in it when opened again
        :return: None
        """
        self.root.ids.added_title.text = ""
        self.root.ids.added_category.text = ""
        self.root.ids.added_year.text = ""