diff --git a/.idea/modules.xml b/.idea/modules.xml index 80d3e4c..c0e0ef5 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -5,6 +5,7 @@ + \ No newline at end of file diff --git a/Week3/Week3.iml b/Week3/Week3.iml new file mode 100644 index 0000000..5173e2f --- /dev/null +++ b/Week3/Week3.iml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Week3/src/AllFilters.java b/Week3/src/AllFilters.java new file mode 100644 index 0000000..3f5b388 --- /dev/null +++ b/Week3/src/AllFilters.java @@ -0,0 +1,24 @@ +import java.util.ArrayList; + +public class AllFilters implements Filter { + ArrayList filters; + + public AllFilters() { + filters = new ArrayList<>(); + } + + public void addFilter(Filter f) { + filters.add(f); + } + + @Override + public boolean satisfies(String id) { + for (Filter f : filters) { + if (!f.satisfies(id)) { + return false; + } + } + + return true; + } +} diff --git a/Week3/src/DirectorsFilter.java b/Week3/src/DirectorsFilter.java new file mode 100644 index 0000000..d0b7bf4 --- /dev/null +++ b/Week3/src/DirectorsFilter.java @@ -0,0 +1,25 @@ +/** + * A class for filter movies by directors + * + * @author Stanislav Rakitov + * @version 1.0 + */ +public class DirectorsFilter implements Filter { + String directors; + + public DirectorsFilter(String directors) { + this.directors = directors; + } + + @Override + public boolean satisfies(String id) { + String movieDirectors = MovieDatabase.getDirector(id); + String[] filterDirectors = directors.split(","); + for (String direcor : filterDirectors) { + if (movieDirectors.contains(direcor)) { + return true; + } + } + return false; + } +} diff --git a/Week3/src/EfficientRater.java b/Week3/src/EfficientRater.java new file mode 100644 index 0000000..2014403 --- /dev/null +++ b/Week3/src/EfficientRater.java @@ -0,0 +1,47 @@ +import java.util.ArrayList; +import java.util.HashMap; + +/** + * The class EfficientRater keeps track of one rater and all their ratings. + * + * @author Stanislav Rakitov + * @version 1.0 + */ +public class EfficientRater implements Rater { + private final String myID; + private final HashMap myRatings; + + public EfficientRater(String id) { + myID = id; + myRatings = new HashMap<>(); + } + + public void addRating(String movieID, Rating rating) { + myRatings.put(movieID, rating); + } + + public boolean hasRating(String movieID) { + return myRatings.containsKey(movieID); + } + + public String getID() { + return myID; + } + + public double getRating(String movieID) { + Rating rating = myRatings.get(movieID); + if (rating != null) { + return rating.getValue(); + } else { + return -1; + } + } + + public int numRatings() { + return myRatings.size(); + } + + public ArrayList getItemsRated() { + return new ArrayList<>(myRatings.keySet()); + } +} diff --git a/Week3/src/Filter.java b/Week3/src/Filter.java new file mode 100644 index 0000000..7e799b2 --- /dev/null +++ b/Week3/src/Filter.java @@ -0,0 +1,3 @@ +public interface Filter { + boolean satisfies(String id); +} diff --git a/Week3/src/FirstRatings.java b/Week3/src/FirstRatings.java new file mode 100644 index 0000000..4db2056 --- /dev/null +++ b/Week3/src/FirstRatings.java @@ -0,0 +1,384 @@ +import edu.duke.FileResource; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +/** + * A class to process the movie and ratings data and to answer questions about them. + * + * @author Stanislav Rakitov + * @version 1.0 + */ +public class FirstRatings { + private ArrayList movieArrayList; + private ArrayList raterArrayList; + private HashMap> directorsAndItsMovies; + private HashMap ratersWithIds; + private HashMap ratersAndCountOfRatings; + private HashMap> moviesAndRatersMap; + // The constrictor for only Movies + public FirstRatings(String fileName) { + String fullFileName = getFullFileName(fileName); + this.movieArrayList = loadMovies(fullFileName); + this.directorsAndItsMovies = getDirectorsAndMovies(); + } + // The constructor for Movies and Raters + public FirstRatings(String fileNameMovies, String fileNameRaters) { + this(fileNameMovies); + String fullFileNameRaters = getFullFileName(fileNameRaters); + this.raterArrayList = loadRaters(fullFileNameRaters); + this.ratersWithIds = loadRatersWithIDs(raterArrayList); + this.ratersAndCountOfRatings = getAllRatersIdAndItsRatingsCounterMap(); + this.moviesAndRatersMap = getAllMoviesAndRatesMap(this.movieArrayList); + } + + public FirstRatings() { + // Maybe later.. + } + + public ArrayList getMovieArrayList() { + return movieArrayList; + } + + public ArrayList getRaterArrayList() { + return raterArrayList; + } + + /** + * This method returns number of unique Raters + * + * @return int number of unique raters + */ + public int getRatersNumber() { + return ratersWithIds.size(); + } + + private HashMap> getAllMoviesAndRatesMap(ArrayList moviesList) { + HashMap> allMoviesAndRatesTempMap = new HashMap<>(); + // fulfill map with all MoviesID with null ArrayList + for (Movie movie : moviesList) { + allMoviesAndRatesTempMap.putIfAbsent(movie.getID(), new ArrayList<>()); + } + + for (Rater rater : raterArrayList) { + ArrayList ratedMovies = rater.getItemsRated(); + for (String ratedMovieID : ratedMovies) { + allMoviesAndRatesTempMap.get(ratedMovieID).add(rater.getID()); + } + } + + return allMoviesAndRatesTempMap; + } + + private HashMap loadRatersWithIDs(ArrayList ratersList) { + HashMap map = new HashMap<>(); + for (Rater rater : ratersList) { + map.put(rater.getID(), rater); + } + return map; + } + + // Proceed map of all directors and its movies + private HashMap> getDirectorsAndMovies() { + HashMap> map = new HashMap<>(); + for (Movie movie : movieArrayList) { + String movieName = movie.getTitle(); + String[] directors = movie.getDirector().split(","); + for (String director : directors) { + director = director.trim(); + HashSet set = new HashSet<>(); + if (map.containsKey(director)) { + set = map.get(director); + set.add(movieName); + } else { + set.add(movieName); + map.put(director, set); + } + } + } + return map; + } + + // Process every record from the CSV file. + ArrayList loadMovies(String filename) { + + // Create empty list of Movie's + ArrayList moviesList = new ArrayList<>(); + + // Get Duke's FileResource. + FileResource fileResource = new FileResource(filename); + + // Get Apache CSV + CSVParser csvRecords = fileResource.getCSVParser(); + + // Proceed every CSVRecord + for (CSVRecord record : csvRecords) { + moviesList.add(parseMovieCSVRecord(record)); + } + return moviesList; + } + + // Creates new Movie object + private Movie parseMovieCSVRecord(CSVRecord record) { + /* id(0),title(1),year(2),country(3),genre(4),director(5),minutes(6),poster(7) + * As it shows in CSV file + */ + String id = record.get(0); + String title = record.get(1); + String year = record.get(2); + String country = record.get(3); + String genre = record.get(4); + String director = record.get(5); + // The constructor takes minutes as int + int minutes = Integer.parseInt(record.get(6)); + String poster = record.get(7); + + // return new Movie object + return new Movie(id, title, year, genre, director, country, poster, minutes); + } + + /** + * A void method for tests cases. + * + *
    + *
  • determine how many movies in total. + *
  • determine how many movies include the Comedy genre. + *
  • determine how many movies are greater than 150 minutes in length. + *
  • determine the maximum number of movies by any director, and who the directors are that + * directed that many movies. + *
+ */ + public void testLoadMovies() { + int totalMoviesCount = movieArrayList.size(); + int totalComedyCount = totalComedy(); + int totalMoviesLonger150MinutesCount = total150(); + String directorWithMaxFilms = maxFilmsOneDirectorName()[0]; + String oneDirectorMaxFilmsCounter = maxFilmsOneDirectorName()[1]; + + System.out.printf("Note there are %d movies in this file.\n", totalMoviesCount); + System.out.println("How many of the movies include the Comedy genre? > " + totalComedyCount); + System.out.println( + "How many of these movies run longer than 150 minutes? > " + + totalMoviesLonger150MinutesCount); + System.out.println( + "What is the maximum number of films directed by one director? > " + + oneDirectorMaxFilmsCounter); + System.out.println( + "What is the name of the director who directed more films than any other " + + "director? " + + directorWithMaxFilms); + System.out.println("---"); + } + + // Determine the Director with max movies and its count + private String[] maxFilmsOneDirectorName() { + String director = ""; + int counter = 0; + for (Map.Entry> pair : directorsAndItsMovies.entrySet()) { + if (pair.getValue().size() > counter) { + counter = pair.getValue().size(); + director = pair.getKey(); + } + } + String stringCounter = String.valueOf(counter); + return new String[] {director, stringCounter}; + } + + // determine how many movies are greater than 150 minutes in length. + private int total150() { + int counter = 0; + for (Movie movie : movieArrayList) { + if (movie.getMinutes() > 150) { + counter++; + } + } + return counter; + } + + // determine how many movies include the Comedy genre + private int totalComedy() { + int counter = 0; + for (Movie movie : movieArrayList) { + if (movie.getGenres().toLowerCase().contains("comedy")) { + counter++; + } + } + return counter; + } + + // Make full filename for any OS types + private static String getFullFileName(String fileName) { + if (!(new File(fileName).exists())) { + fileName = + System.getProperty("user.dir") + File.separator + "data" + File.separator + fileName; + } + return fileName; + } + + public void testLoadRaters(String raterID, String movieID) { + + // Use for test purposes + testPrintAllRaterWithRatings(); + + // find the number of ratings for a particular rater + printNumberOfRatings(raterID); + + // Find the maximum number of ratings by any rater + printMaxNumOfRatings(); + + // Print the number of ratings a particular movie has + // String movieID = "1798709"; + System.out.printf( + "How many ratings does the movie %s have? > %d\n", + movieID, getNumberOfRatingsByMovie(movieID)); + // And how many different movies have been rated by all these raters + + // total number of unique movies that have been rated + printTotalNumberMoviesRated(); + } + + // Q10. What is the total number of unique movies that have been rated? + private void printTotalNumberMoviesRated() { + int total = 0; + for (Map.Entry> entry : moviesAndRatersMap.entrySet()) { + if (entry.getValue() != null) { + total++; + } + } + System.out.println( + "What is the total number of unique movies that have been rated? > " + total); + } + + // Get number of Ratings by MovieID + private int getNumberOfRatingsByMovie(String movieID) { + return moviesAndRatersMap.get(movieID).size(); + } + + // Determine how many raters have this maximum number of ratings and who those raters are. + private void printMaxNumOfRatings() { + + // Find max rating value + int maxRatings = getMaxRatings(); + System.out.println("What is the maximum number of ratings by any rater? > " + maxRatings); + + for (Map.Entry entry : ratersAndCountOfRatings.entrySet()) { + if (entry.getValue() == maxRatings) { + System.out.println("Which rater rated the most number of movies? > " + entry.getKey()); + } + } + } + + // find the maximum number of ratings by any rater + private int getMaxRatings() { + int maxRatings = 0; + for (int counter : ratersAndCountOfRatings.values()) { + if (counter > maxRatings) { + maxRatings = counter; + } + } + return maxRatings; + } + + // Get map with number of rates for every Rater + private HashMap getAllRatersIdAndItsRatingsCounterMap() { + HashMap map = new HashMap<>(); + for (String id : ratersWithIds.keySet()) { + int count = getNumberOfRatingsByRaterID(id); + map.put(id, count); + } // for + return map; + } + + // Find the number of ratings for a particular rater + private void printNumberOfRatings(String id) { + int ratingsCount = getNumberOfRatingsByRaterID(id); + System.out.printf("How many ratings does the rater number %s have? > %s\n", id, ratingsCount); + // System.out.printf("Rater id: %s has %d ratings\n", id, ratingsCount); + } + + // Get total number of rates for specific rater's id + private int getNumberOfRatingsByRaterID(String id) { + return ratersWithIds.get(id).numRatings(); + } + + /* + Print the total number of raters. Then for each rater, print the rater’s ID and the number of + ratings they did on one line, followed by each rating (both the movie ID and the rating given) + on a separate line. If you run your program on the file ratings_short.csv you will see there + are five raters. + */ + private void testPrintAllRaterWithRatings() { + // Print the total number of raters. + int totalRaterCount = raterArrayList.size(); + System.out.printf("Note there are %d raters in this file.\n", totalRaterCount); + + // Then for each rater, print the rater’s ID + // and the number of ratings they did on one line + // printAllRaterRatings(); + } + + // Print out number o rates for all raters + private void printAllRaterRatings() { + for (Map.Entry entry : ratersAndCountOfRatings.entrySet()) { + String raterId = entry.getKey(); + int ratingsCount = entry.getValue(); + System.out.printf("Rater id: %s has %d ratings\n", raterId, ratingsCount); + printAllRatingsByRater(raterId); + } + } + + // Print all ratings for specific Rater ID + private void printAllRatingsByRater(String raterID) { + Rater rater = ratersWithIds.get(raterID); + ArrayList allRatedMoviesIDs = rater.getItemsRated(); + for (String id : allRatedMoviesIDs) { + double rating = rater.getRating(id); + System.out.printf("Movie ID: %s, rating: %f\n", id, rating); + } + } + + /* + * This method should process every record from the CSV file whose name is + * filename, a file of raters and their ratings + */ + static ArrayList loadRaters(String fileName) { + ArrayList ratersList = new ArrayList<>(); + ArrayList idsList = new ArrayList<>(); + + String fullFileName = getFullFileName(fileName); + + // Get Duke's FileResource. + FileResource fileResource = new FileResource(fullFileName); + + // Get Apache CSV + CSVParser csvRecords = fileResource.getCSVParser(); + + // Proceed every CSVRecord + for (CSVRecord record : csvRecords) { + // rater_id,movie_id,rating,time + String rater_id = record.get(0); + String movie_id = record.get(1); + double rating = Double.parseDouble(record.get(2)); + String time = record.get(3); + if (!idsList.contains(rater_id)) { + Rater rater = new EfficientRater(rater_id); + ratersList.add(rater); + rater.addRating(movie_id, new Rating(movie_id, rating)); + idsList.add(rater_id); + } else { + for (Rater r : ratersList) { + if (r.getID().equals(rater_id)) { + r.addRating(movie_id, new Rating(movie_id, rating)); + } + } + } + } + + return ratersList; + } // loadRaters +} // class diff --git a/Week3/src/GenreFilter.java b/Week3/src/GenreFilter.java new file mode 100644 index 0000000..7913b96 --- /dev/null +++ b/Week3/src/GenreFilter.java @@ -0,0 +1,22 @@ +/** + * A class for filter movies by genre + * + * @author Stanislav Rakitov + * @version 1.0 + */ +public class GenreFilter implements Filter { + + private final String genre; + + // The constructor should have one parameter named genre representing one genre, + // and the satisfies method should return true if a movie has this genre. + // Note that movies may have several genres. + public GenreFilter(String genre) { + this.genre = genre.toLowerCase(); + } + + @Override + public boolean satisfies(String id) { + return MovieDatabase.getGenres(id).toLowerCase().contains(genre); + } +} diff --git a/Week3/src/MinutesFilter.java b/Week3/src/MinutesFilter.java new file mode 100644 index 0000000..18a51c0 --- /dev/null +++ b/Week3/src/MinutesFilter.java @@ -0,0 +1,26 @@ +/** + * A class for filter movies by time + * + * @author Stanislav Rakitov + * @version 1.0 + */ +public class MinutesFilter implements Filter { + private final int minMinutes; + private final int maxMinutes; + + public MinutesFilter(int minMinutes, int maxMinutes) { + this.minMinutes = minMinutes; + this.maxMinutes = maxMinutes; + } + + // No max minutes given + public MinutesFilter(int minMinutes) { + this.minMinutes = minMinutes; + this.maxMinutes = Integer.MAX_VALUE; + } + + @Override + public boolean satisfies(String id) { + return MovieDatabase.getMinutes(id) >= minMinutes && MovieDatabase.getMinutes(id) <= maxMinutes; + } +} diff --git a/Week3/src/Movie.java b/Week3/src/Movie.java new file mode 100644 index 0000000..ae38dc3 --- /dev/null +++ b/Week3/src/Movie.java @@ -0,0 +1,82 @@ +// An immutable passive data object (PDO) to represent item data +public class Movie { + private final String id; + private final String title; + private final int year; + private final String genres; + private String director; + private String country; + private String poster; + private int minutes; + + public Movie(String anID, String aTitle, String aYear, String theGenres) { + // just in case data file contains extra whitespace + id = anID.trim(); + title = aTitle.trim(); + year = Integer.parseInt(aYear.trim()); + genres = theGenres; + } + + public Movie( + String anID, + String aTitle, + String aYear, + String theGenres, + String aDirector, + String aCountry, + String aPoster, + int theMinutes) { + // just in case data file contains extra whitespace + id = anID.trim(); + title = aTitle.trim(); + year = Integer.parseInt(aYear.trim()); + genres = theGenres; + director = aDirector; + country = aCountry; + poster = aPoster; + minutes = theMinutes; + } + + // Returns ID associated with this item + public String getID() { + return id; + } + + // Returns title of this item + public String getTitle() { + return title; + } + + // Returns year in which this item was published + public int getYear() { + return year; + } + + // Returns genres associated with this item + public String getGenres() { + return genres; + } + + public String getCountry() { + return country; + } + + public String getDirector() { + return director; + } + + public String getPoster() { + return poster; + } + + public int getMinutes() { + return minutes; + } + + // Returns a string of the item's information + public String toString() { + String result = "Movie [id=" + id + ", title=" + title + ", year=" + year; + result += ", genres= " + genres + "]"; + return result; + } +} diff --git a/Week3/src/MovieDatabase.java b/Week3/src/MovieDatabase.java new file mode 100644 index 0000000..c86fa82 --- /dev/null +++ b/Week3/src/MovieDatabase.java @@ -0,0 +1,89 @@ +import java.util.ArrayList; +import java.util.HashMap; + +public class MovieDatabase { + private static HashMap ourMovies; + + public static void initialize(String moviefile) { + if (ourMovies == null) { + ourMovies = new HashMap<>(); + loadMovies("data/" + moviefile); + } + } + + private static void initialize() { + if (ourMovies == null) { + ourMovies = new HashMap<>(); + loadMovies("data/ratedmoviesfull.csv"); + } + } + + private static void loadMovies(String filename) { + FirstRatings fr = new FirstRatings(); + ArrayList list = fr.loadMovies(filename); + for (Movie m : list) { + ourMovies.put(m.getID(), m); + } + } + + public static boolean containsID(String id) { + initialize(); + return ourMovies.containsKey(id); + } + + public static int getYear(String id) { + initialize(); + return ourMovies.get(id).getYear(); + } + + public static String getGenres(String id) { + initialize(); + return ourMovies.get(id).getGenres(); + } + + public static String getTitle(String id) { + initialize(); + return ourMovies.get(id).getTitle(); + } + + public static Movie getMovie(String id) { + initialize(); + return ourMovies.get(id); + } + + public static String getPoster(String id) { + initialize(); + return ourMovies.get(id).getPoster(); + } + + public static int getMinutes(String id) { + initialize(); + return ourMovies.get(id).getMinutes(); + } + + public static String getCountry(String id) { + initialize(); + return ourMovies.get(id).getCountry(); + } + + public static String getDirector(String id) { + initialize(); + return ourMovies.get(id).getDirector(); + } + + public static int size() { + return ourMovies.size(); + } + + public static ArrayList filterBy(Filter f) { + initialize(); + ArrayList list = new ArrayList<>(); + for (String id : ourMovies.keySet()) { + if (f.satisfies(id)) { + list.add(id); + } + } + + return list; + } +} diff --git a/Week3/src/MovieRunnerAverage.java b/Week3/src/MovieRunnerAverage.java new file mode 100644 index 0000000..5695a38 --- /dev/null +++ b/Week3/src/MovieRunnerAverage.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.Collections; + +public class MovieRunnerAverage { + private final SecondRatings secondRatings; + + public MovieRunnerAverage() { + secondRatings = new SecondRatings("ratedmoviesfull.csv", "ratings.csv"); + } + + public void printAverageRatings(int minimalRatings) { + // SecondRatings secondRatings = new SecondRatings("ratedmovies_short.csv", + // "ratings_short.csv"); + // System.out.printf( + // "Total number of movies %d, total number of raters %d\n", + // secondRatings.getMovieSize(), secondRatings.getRaterSize()); + + /* + * In the MovieRunnerAverage class in the printAverageRatings method, + * add code to print a list of movies and their average ratings, + * for all those movies that have at least a specified number of ratings, + * sorted by averages. + * Specifically, this method should print the list of movies, + * one movie per line (print its rating first, followed by its title) + * in sorted order by ratings, lowest rating to highest rating. + * For example, if printAverageRatings is called on the files + * ratings_short.csv and ratedmovies_short.csv with the argument 3, + * then the output will display two movies: + * + * 8.25 Her + * 9.0 The Godfather + * */ + ArrayList ratedList = secondRatings.getAverageRatings(minimalRatings); + Collections.sort(ratedList); + System.out.printf("Total movies with %d ratings is %d\n", minimalRatings, ratedList.size()); + System.out.printf( + "The name of the movie that has the lowest rating is \"%s\"\n", + secondRatings.getTitle(ratedList.get(0).getItem())); + // for (Rating ratedObj : ratedList) { + // double rating = ratedObj.getValue(); + // String movieID = ratedObj.getItem(); + // String movieTitle = secondRatings.getTitle(movieID); + // System.out.println(rating + " " + movieTitle); + // } + } + + Double getAverageRatingOneMovie(String movieTitle) { + String movieID = secondRatings.getID(movieTitle); + return secondRatings.getAverageByID(movieID, 1); + } +} diff --git a/Week3/src/MovieRunnerWithFilters.java b/Week3/src/MovieRunnerWithFilters.java new file mode 100644 index 0000000..3d23af4 --- /dev/null +++ b/Week3/src/MovieRunnerWithFilters.java @@ -0,0 +1,151 @@ +import java.util.ArrayList; +import java.util.Collections; + +public class MovieRunnerWithFilters { + + private final ThirdRatings thirdRatings; + + public MovieRunnerWithFilters(String moviesFileName, String ratingsFileName) { + this.thirdRatings = new ThirdRatings(moviesFileName, ratingsFileName); + } + + public MovieRunnerWithFilters() { + this("ratedmovies_short.csv", "ratings_short.csv"); + } + + /** + * Print a list of movies and their average ratings sorted by averages + * + * @param minimalRatings int specified number of ratings + */ + public void printAverageRatings(int minimalRatings) { + ArrayList ratedList = thirdRatings.getAverageRatings(minimalRatings); + + Collections.sort(ratedList); + // Print the number of raters after creating a ThirdsRating object. + System.out.printf("Total movies with %d ratings is %d\n", minimalRatings, ratedList.size()); + + // Print the number of movies in the database. + System.out.println("The number of movies in the database is " + MovieDatabase.size()); + + // You will call getAverageRatings with a minimal number of raters to return an ArrayList of + // type Rating. + + ArrayList averageRatings = thirdRatings.getAverageRatings(minimalRatings); + + // Print out how many movies with ratings are returned, + // then sort them, and print out the rating and title of each movie + System.out.printf( + "How many movies with ratings %d are returned: %d%n", + minimalRatings, averageRatings.size()); + + printRatingsList(averageRatings); + } + + public int getAverageRatingsNumber(int minimalRatings) { + return thirdRatings.getAverageRatings(minimalRatings).size(); + } + + private void printRatingsList(ArrayList averageRatingList) { + System.out.printf("Found %d movie(s)%n", averageRatingList.size()); + averageRatingList.stream() + .sorted() + .forEach( + rating -> { + String movieID = rating.getItem(); + System.out.printf("%-4s %s%n", rating.getValue(), MovieDatabase.getTitle(movieID)); + System.out.println(" Year: " + MovieDatabase.getYear(movieID)); + System.out.println(" Time: " + MovieDatabase.getMinutes(movieID)); + System.out.println(" Genre(s): " + MovieDatabase.getGenres(movieID)); + System.out.println(" Director(s): " + MovieDatabase.getDirector(movieID)); + }); + System.out.println("-------"); + } + + /** + * Print a list of movies and their average ratings sorted by Year and minimal number of raters + * + * @param minimalRatings Minimal number of ratings + * @param year int Year of produce + */ + public void printAverageRatingsByYear(int minimalRatings, int year) { + System.out.println( + thirdRatings.getAverageRatingsByFilter(minimalRatings, new YearAfterFilter(year)).size()); + // printRatingsList( + // thirdRatings.getAverageRatingsByFilter(minimalRatings, new YearAfterFilter(year))); + } + + /** + * Print a list of movies and their average ratings sorted by Genre + * + * @param minimalRatings Minimal number of ratings + * @param genre Genre + */ + public void printAverageRatingsByGenre(int minimalRatings, String genre) { + System.out.println( + thirdRatings.getAverageRatingsByFilter(minimalRatings, new GenreFilter(genre)).size()); + + // printRatingsList( + // thirdRatings.getAverageRatingsByFilter(minimalRatings, new GenreFilter(genre))); + } + + /** + * Print a list of movies and their average ratings sorted by time + * + * @param minimalRatings Minimal number of ratings + * @param minMinutes Minimal length of movies in minutes + * @param maxMinutes Maximum length of movies in minutes + */ + public void printAverageRatingsByMinutes(int minimalRatings, int minMinutes, int maxMinutes) { + System.out.println( + thirdRatings + .getAverageRatingsByFilter(minimalRatings, new MinutesFilter(minMinutes, maxMinutes)) + .size()); + // printRatingsList( + // thirdRatings.getAverageRatingsByFilter( + // minimalRatings, new MinutesFilter(minMinutes, maxMinutes))); + } + + /** + * Print a list of movies and their average ratings sorted by Directors + * + * @param minimalRatings Minimal number of ratings + * @param directors directors of the movies + */ + public void printAverageRatingsByDirectors(int minimalRatings, String directors) { + System.out.println( + thirdRatings + .getAverageRatingsByFilter(minimalRatings, new DirectorsFilter(directors)) + .size()); + // System.out.println("Print movies directed by " + directors); + // printRatingsList( + // thirdRatings.getAverageRatingsByFilter(minimalRatings, new + // DirectorsFilter(directors))); + } + + public void printAverageRatingsByYearAfterAndGenre(int minimalRatings, int year, String genre) { + AllFilters filters = new AllFilters(); + filters.addFilter(new GenreFilter(genre)); + filters.addFilter(new YearAfterFilter(year)); + System.out.println(thirdRatings.getAverageRatingsByFilter(minimalRatings, filters).size()); + // System.out.printf( + // "Print movie(s) with at least %d rating in \"%s\" genre produced after year" + " of %d + // %n", + // minimalRatings, genre, year); + // printRatingsList(thirdRatings.getAverageRatingsByFilter(minimalRatings, filters)); + } + + public void printAverageRatingsByDirectorsAndMinutes( + int minimalRatings, int minMinutes, int maxMinutes, String director) { + AllFilters filters = new AllFilters(); + filters.addFilter(new MinutesFilter(minMinutes, maxMinutes)); + filters.addFilter(new DirectorsFilter(director)); + + System.out.println(thirdRatings.getAverageRatingsByFilter(minimalRatings, filters).size()); + + // System.out.printf( + // "Print movie(s) with at least %d rating at least %d minutes long directed by %s%n", + // minimalRatings, minMinutes, director); + // printRatingsList(thirdRatings.getAverageRatingsByFilter(minimalRatings, filters)); + } +} diff --git a/Week3/src/Rater.java b/Week3/src/Rater.java new file mode 100644 index 0000000..95e2281 --- /dev/null +++ b/Week3/src/Rater.java @@ -0,0 +1,16 @@ +import java.util.ArrayList; + +public interface Rater { + // void addRating(String item, double rating); + void addRating(String movieID, Rating rating); + + boolean hasRating(String item); + + String getID(); + + double getRating(String item); + + int numRatings(); + + ArrayList getItemsRated(); +} diff --git a/Week3/src/Rating.java b/Week3/src/Rating.java new file mode 100644 index 0000000..68a4502 --- /dev/null +++ b/Week3/src/Rating.java @@ -0,0 +1,29 @@ +// An immutable passive data object (PDO) to represent the rating data +public class Rating implements Comparable { + private final String item; + private final double value; + + public Rating(String anItem, double aValue) { + item = anItem; + value = aValue; + } + + // Returns item being rated + public String getItem() { + return item; + } + + // Returns the value of this rating (as a number so it can be used in calculations) + public double getValue() { + return value; + } + + // Returns a string of all the rating information + public String toString() { + return "[" + getItem() + ", " + getValue() + "]"; + } + + public int compareTo(Rating other) { + return Double.compare(value, other.value); + } +} diff --git a/Week3/src/SecondRatings.java b/Week3/src/SecondRatings.java new file mode 100644 index 0000000..6d52eb7 --- /dev/null +++ b/Week3/src/SecondRatings.java @@ -0,0 +1,151 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Week2. A new class to do many of the calculations focusing on computing averages on movie + * ratings. + * + * @author Stanislav Rakitov + * @version 1.0 + */ +public class SecondRatings { + private final ArrayList myMovies; + private final ArrayList myRaters; + private final FirstRatings firstRatings; + private final HashMap> moviesAndRatings; + private final HashMap moviesIdAndTitles; + + public SecondRatings() { + // default constructor + this("ratedmoviesfull.csv", "ratings.csv"); + } + + // The constructor should create a FirstRatings object and then call the loadMovies and + // loadRaters methods in FirstRatings to read in all the movie and ratings data and store them + // in the two private ArrayList variables of the SecondRatings class, myMovies and myRaters. + public SecondRatings(String moviesFileName, String ratingsFileName) { + firstRatings = new FirstRatings(moviesFileName, ratingsFileName); + myMovies = firstRatings.getMovieArrayList(); + myRaters = firstRatings.getRaterArrayList(); + moviesAndRatings = getMoviesAndRatingsMap(); + moviesIdAndTitles = getAllMoviesAndTitlesMap(); + } + + // Get all movies and titles map + private HashMap getAllMoviesAndTitlesMap() { + HashMap map = new HashMap<>(); + for (Movie movie : myMovies) { + String movieID = movie.getID(); + String movieTitle = movie.getTitle(); + map.put(movieID, movieTitle); + } + return map; + } + + // Returns a Map with movieID and all its ratings + private HashMap> getMoviesAndRatingsMap() { + HashMap> map = new HashMap<>(); + + for (Movie movie : myMovies) { + map.putIfAbsent(movie.getID(), new ArrayList<>()); + } + + for (Rater rater : myRaters) { + ArrayList ratings = rater.getItemsRated(); + for (String movieID : ratings) { + // String movieID, double rating + ArrayList list = map.get(movieID); + list.add(rater.getRating(movieID)); + } + } + + return map; + } + + /** + * A public method which returns the number of movies that were read in and stored in the + * ArrayList of type Movie + * + * @return int number of movies + */ + public int getMovieSize() { + return this.myMovies.size(); + } + + /** + * A public method which returns the number of raters that were read in and stored in the + * ArrayList of type Rater. + * + * @return int number of movies + */ + public int getRaterSize() { + return this.firstRatings.getRatersNumber(); + } + + /* + This method returns a double representing the average movie rating for this ID + if there are at least minimalRaters ratings. + If there are not minimalRaters ratings, then it returns 0.0. + */ + Double getAverageByID(String id, Integer minimalRaters) { + double result = 0.0; + ArrayList ratings = moviesAndRatings.get(id); + if (ratings != null && ratings.size() >= minimalRaters) { + result = ratings.stream().mapToDouble(d -> d).average().orElse(0.0); + } + + // If there are no ratings returns 0.0 + return result; + } + + /** + * This method should find the average rating for every movie that has been rated by at least + * minimalRaters raters. + * + * @param minimalRaters int the minimal number of raters supplying a rating + * @return an ArrayList of all the Rating objects for movies that have at least the minimal number + * of raters supplying a rating + */ + public ArrayList getAverageRatings(int minimalRaters) { + ArrayList list = new ArrayList<>(); + for (Movie movie : myMovies) { + String movieID = movie.getID(); + Double averageRating = getAverageByID(movieID, minimalRaters); + if (averageRating != 0.0) { + list.add(new Rating(movieID, averageRating)); + } + } + return list; + } + + /* + * This method returns the title of the movie with that ID. + * If the movie ID does not exist, then this method should return a String + * indicating the ID was not found. + * */ + String getTitle(String id) { + String title = moviesIdAndTitles.get(id); + if (title != null) { + return title; + } + // No title found + return "ID " + id + " NOT FOUND"; + } + /* + * This method returns the movie ID of this movie. + * If the title is not found, return “NO SUCH TITLE. + * */ + String getID(String title) { + String result = "NO SUCH TITLE"; + + for (Map.Entry entry : moviesIdAndTitles.entrySet()) { + if (Objects.equals(title, entry.getValue())) { + return entry.getKey(); + } + } + + return result; + } +} diff --git a/Week3/src/ThirdRatings.java b/Week3/src/ThirdRatings.java new file mode 100644 index 0000000..da666a2 --- /dev/null +++ b/Week3/src/ThirdRatings.java @@ -0,0 +1,64 @@ +import java.util.ArrayList; +import java.util.Collections; + +public class ThirdRatings { + + private final ArrayList myRaters; + private final ArrayList allMoviesIDs; + + public ThirdRatings(String moviesFileName, String ratingsFileName) { + this.myRaters = FirstRatings.loadRaters(ratingsFileName); + MovieDatabase.initialize(moviesFileName); + allMoviesIDs = MovieDatabase.filterBy(new TrueFilter()); + } + + public ArrayList getAverageRatings(int minimalRaters) { + ArrayList list = new ArrayList<>(); + for (String movieID : allMoviesIDs) { + Double averageRating = getAverageByID(movieID, minimalRaters); + if (averageRating != 0.0) { + list.add(new Rating(movieID, averageRating)); + } + } + return list; + } + + /* + This method returns a double representing the average movie rating for this ID + if there are at least minimalRaters ratings. + If there are not minimalRaters ratings, then it returns 0.0. + */ + Double getAverageByID(String movieID, Integer minimalRaters) { + long numOfRatings = myRaters.stream().filter(rater -> rater.hasRating(movieID)).count(); + + if (numOfRatings >= minimalRaters) { + return myRaters.stream() + .filter(rater -> rater.hasRating(movieID)) + .mapToDouble(rating -> rating.getRating(movieID)) + .average() + .orElse(0.0); + } + return 0.0; + } + + public int getRaterSize() { + return myRaters.size(); + } + + public ArrayList getAverageRatingsByFilter(Integer minimalRaters, Filter filterCriteria) { + ArrayList rating = new ArrayList<>(); + ArrayList movies = MovieDatabase.filterBy(filterCriteria); + // System.out.println(movies); + Rating rat; + for (String movie_id : movies) { + if (getAverageByID(movie_id, minimalRaters) != 0) { + rat = new Rating(movie_id, getAverageByID(movie_id, minimalRaters)); + rating.add(rat); + } + } + + Collections.sort(rating); + // System.out.println(rating); + return rating; + } +} // class diff --git a/Week3/src/TrueFilter.java b/Week3/src/TrueFilter.java new file mode 100644 index 0000000..9a5ae07 --- /dev/null +++ b/Week3/src/TrueFilter.java @@ -0,0 +1,6 @@ +public class TrueFilter implements Filter { + @Override + public boolean satisfies(String id) { + return true; + } +} diff --git a/Week3/src/Week1.java b/Week3/src/Week1.java new file mode 100644 index 0000000..f77bbc9 --- /dev/null +++ b/Week3/src/Week1.java @@ -0,0 +1,7 @@ +public class Week1 { + public static void main(String[] args) { + FirstRatings testLong = new FirstRatings("ratedmoviesfull.csv", "ratings.csv"); + testLong.testLoadMovies(); + testLong.testLoadRaters("193", "1798709"); + } +} diff --git a/Week3/src/Week2.java b/Week3/src/Week2.java new file mode 100644 index 0000000..d1f0eff --- /dev/null +++ b/Week3/src/Week2.java @@ -0,0 +1,32 @@ +public class Week2 { + public static void main(String[] args) { + MovieRunnerAverage average = new MovieRunnerAverage(); + + System.out.printf( + "Q5. What is the rating of the movie “The Maze Runner”? - %.4f\n", + average.getAverageRatingOneMovie("The Maze Runner")); + + System.out.printf( + "Q6. What is the rating of the movie “Moneyball”? - %.4f\n", + average.getAverageRatingOneMovie("Moneyball")); + + System.out.printf( + "Q7. what is the rating of the movie “Vacation”? - %.4f\n", + average.getAverageRatingOneMovie("Vacation")); + + // Q8. Using the files ratedmoviesfull.csv and ratings.csv, how many movies have 50 or more + // ratings? + average.printAverageRatings(50); + + // Q9. Using the files ratedmoviesfull.csv and ratings.csv, of those movies that have at least + // 20 ratings, + // what is the name of the movie that has the lowest rating? + + average.printAverageRatings(20); + + // Using the files ratedmoviesfull.csv and ratings.csv, + // of those movies that have at least 12 ratings, + // what is the name of the movie that has the lowest rating? + average.printAverageRatings(12); + } +} diff --git a/Week3/src/Week3.java b/Week3/src/Week3.java new file mode 100644 index 0000000..6b6655a --- /dev/null +++ b/Week3/src/Week3.java @@ -0,0 +1,58 @@ +public class Week3 { + public static void main(String[] args) { + // MovieRunnerWithFilters filters = new MovieRunnerWithFilters(); + // filters.printAverageRatingsByYear(1, 2000); + // filters.printAverageRatingsByGenre(1, "Crime"); + // filters.printAverageRatingsByMinutes(1, 110, 170); + // filters.printAverageRatingsByDirectors(1, "Charles Chaplin,Michael Mann,Spike Jonze"); + // filters.printAverageRatingsByYearAfterAndGenre(1, 1980, "Romance"); + // filters.printAverageRatingsByDirectorsAndMinutes( + // 1, 30, 170, "Spike Jonze,Michael Mann,Charles Chaplin,Francis Ford Coppola"); + + MovieRunnerWithFilters quiz3 = new MovieRunnerWithFilters("ratedmoviesfull.csv", "ratings.csv"); + System.out.print( + "Q.4 number of minimal raters set to 35. " + "How many rated movies are returned? "); + System.out.println(quiz3.getAverageRatingsNumber(35)); + + System.out.print( + "Q.5 printAverageRatingsByYearAfter. Minimal raters 20. Year 2000. How " + + "many rated movies are returned? "); + quiz3.printAverageRatingsByYear(20, 2000); + + System.out.print( + "Q.6 printAverageRatingsByGenre. minimal raters 20. genre Comedy. How many " + + "rated" + + " movies are returned? "); + quiz3.printAverageRatingsByGenre(20, "Comedy"); + + System.out.print( + "Q.7 printAverageRatingsByMinutes. Minimal raters 5. Movies that take at " + + "least 105 minutes and at most 135 minutes. How many rated movies are returned? "); + quiz3.printAverageRatingsByMinutes(5, 105, 135); + + System.out.println( + "Q.8 printAverageRatingsByDirectors. Minimal raters 4. And one of these directors:"); + System.out.println( + "\"Clint Eastwood,Joel Coen,Martin Scorsese,Roman Polanski,Nora Ephron,Ridley Scott,Sydney Pollack\""); + System.out.print("How many rated movies are returned? "); + quiz3.printAverageRatingsByDirectors( + 4, + "Clint Eastwood,Joel Coen,Martin Scorsese,Roman Polanski,Nora Ephron,Ridley Scott,Sydney Pollack"); + + System.out.println( + "Q.9 printAverageRatingsByYearAfterAndGenre. Minimal raters 8, the year " + + "set to 1990, and the genre set to \"Drama\"."); + System.out.print("How many rated movies are returned? "); + quiz3.printAverageRatingsByYearAfterAndGenre(8, 1990, "Drama"); + + System.out.print( + "Q.10 printAverageRatingsByDirectorsAndMinutes. \n" + + "Minimal raters 3 \n" + + "and the length of the film set to at least 90 minutes and no more than 180 minutes.\n" + + "Find all the movies that have at least one of these directors: \n" + + "\"Clint Eastwood,Joel Coen,Tim Burton,Ron Howard,Nora Ephron,Sydney Pollack\". \n" + + "How many rated movies are returned? "); + quiz3.printAverageRatingsByDirectorsAndMinutes( + 3, 90, 180, "Clint Eastwood,Joel Coen,Tim Burton,Ron Howard,Nora Ephron,Sydney Pollack"); + } +} diff --git a/Week3/src/YearAfterFilter.java b/Week3/src/YearAfterFilter.java new file mode 100644 index 0000000..ecb3e3a --- /dev/null +++ b/Week3/src/YearAfterFilter.java @@ -0,0 +1,12 @@ +public class YearAfterFilter implements Filter { + private final int myYear; + + public YearAfterFilter(int year) { + myYear = year; + } + + @Override + public boolean satisfies(String id) { + return MovieDatabase.getYear(id) >= myYear; + } +}