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)
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)