9 Commits
Author SHA1 Message Date
Saba_frm 13266e501e development 2026-05-02 13:39:41 +03:30
Saba_frm f2be2ee51c develop 2026-05-02 13:29:35 +03:30
Saba_frm 6d9a1603a2 develop 2026-04-30 12:18:24 +03:30
MehrdadShirvani 6a01e43eb2 Merge remote-tracking branch 'origin/main' 2026-04-27 16:20:34 +03:30
MehrdadShirvani cafed6c3ab fix: images type in Movie class 2026-04-27 16:19:01 +03:30
MehrdadShirvani e459dbf684 fix: add more detailed comments in MovieService 2026-04-27 09:26:01 +03:30
MehrdadShirvani 6855df7fc3 Update README.md 2026-04-27 05:55:00 +00:00
MehrdadShirvani 8dd1f66e92 fix: add more detailed comments in MovieService 2026-04-27 09:20:50 +03:30
MehrdadShirvani 2c3f37c8d8 fix: Movie fields 2026-04-27 08:40:05 +03:30
7 changed files with 407 additions and 148 deletions
+1 -3
View File
@@ -8,7 +8,5 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" project-jdk-name="21" project-jdk-type="JavaSDK" />
<output url="file://$PROJECT_DIR$/out" />
</component>
</project> </project>
+28 -53
View File
@@ -121,64 +121,40 @@ Understanding: You must be able to explain your implementation
## Bonus Tasks ✒️ ## Bonus Tasks ✒️
1. Fetch and display movie information using movie ID 1. Fetch and display movie information using movie ID
In this section, you must implement the ability to search for a movie by its ID. The user enters an ID, and the program displays complete movie details. Implement movie search by ID. User enters an ID, program displays complete movie details.
Movie Class Fields (already defined): ### ⚠️ First: Add Missing Fields to Movie Class
The API returns more data than your current class supports. Add these fields:
- `actors`, `awards`, `director`, `imdb_id`, `imdb_votes`, `metascore`, `plot`, `rated`, `released`, `runtime`, `type`, `writer`
- id (Long) - Movie identifier ### Implementation Steps:
- title (String) - Movie title 1. **Complete `getMovieById(movieId)`**
- Build request to: `https://api.meshcomp.ir/api/v1/movies/{movieId}`
- If status 200 → return JSON body, else → return null
- year (String) - Release year 2. **Complete `displayMovieDetails(movieId)`**
- Call `getMovieById()` → check for null
- Use Gson to parse JSON to Movie object (after adding fields above)
- Print formatted output (see example below)
- genres (List<Genre>) - List of genres ### Example Output:
```
- images (String) - Poster image URL =======================================================================
THE SHAWSHANK REDEMPTION (1994)
- country (String) - Production country =======================================================================
Rating: 9.3/10 | Votes: 1,738,596
- imdbRating (String) - IMDB rating Director: Frank Darabont
Cast: Tim Robbins, Morgan Freeman, Bob Gunton, William Sadler
Implementation Steps: Genre: Crime, Drama
Runtime: 142 min | Rated: R
- Build and Send Request (getMovieById method): Country: USA | Released: 14 Oct 1994
Plot: Two imprisoned men bond over a number of years, finding
- Create HTTP request to: https://api.meshcomp.ir/api/v1/movies/{movieId} solace and eventual redemption through acts of common decency.
Awards: Nominated for 7 Oscars. Another 19 wins & 30 nominations.
- Replace {movieId} with the ID from user =======================================================================
- Send request and get response
- Handle Response (getMovieById method):
- If status code is 200, return JSON body
- Otherwise, print error and return null
- Parse JSON and Create Movie Object (displayMovieDetails method):
- Parse JSON response to JsonObject
- Extract all fields: id, title, year, poster, country, imdb_rating
- Parse "genres" array and convert each to Genre object
- Create a Movie object with all extracted data
- Display Results (displayMovieDetails method):
- Print all movie details in a clean format
2. Advanced search (title, genre, year, rating, etc.)
3. Sorting options for movies
4. Save/load favorite movies to/from a file
5. Improved console menu design
6. GUI version using JavaFX
```
--- ---
## References 📚 ## References 📚
@@ -202,4 +178,3 @@ Jackson Tutorial:
--- ---
## Submission ⌛ ## Submission ⌛
Deadline: May 1st (11 Ordibehesht) Deadline: May 1st (11 Ordibehesht)
View File
+29 -20
View File
@@ -53,43 +53,52 @@ public class Rational {
// TODO: Instance method - this - other // TODO: Instance method - this - other
// Formula: a/b - c/d = (a*d - c*b) / (b*d) // Formula: a/b - c/d = (a*d - c*b) / (b*d)
public Rational subtract(Rational other) { public Rational subtract(Rational other)
// YOUR CODE HERE {
return null; // Remove this line when implemented int newNumerator = this.numerator * other.denominator - other.numerator * this.denominator;
int newDenominator = this.denominator * other.denominator;
return new Rational(newNumerator, newDenominator);
} }
// TODO: Static method - r1 - r2 // TODO: Static method - r1 - r2
public static Rational subtract(Rational r1, Rational r2) { public static Rational subtract(Rational r1, Rational r2)
// YOUR CODE HERE {
return null; // Remove this line when implemented return r1.subtract(r2);
} }
// TODO: Instance method - this * other // TODO: Instance method - this * other
// Formula: (a/b) * (c/d) = (a*c) / (b*d) // Formula: (a/b) * (c/d) = (a*c) / (b*d)
public Rational multiply(Rational other) { public Rational multiply(Rational other)
// YOUR CODE HERE {
return null; // Remove this line when implemented int newNumerator = this.numerator * other.numerator;
int newDenominator = this.denominator * other.denominator;
return new Rational(newNumerator, newDenominator);
} }
// TODO: Static method - r1 * r2 // TODO: Static method - r1 * r2
public static Rational multiply(Rational r1, Rational r2) { public static Rational multiply(Rational r1, Rational r2)
// YOUR CODE HERE {
return null; // Remove this line when implemented return r1.multiply(r2);
} }
// TODO: Instance method - this / other // TODO: Instance method - this / other
// Formula: (a/b) ÷ (c/d) = (a*d) / (b*c) // Formula: (a/b) ÷ (c/d) = (a*d) / (b*c)
public Rational divide(Rational other) { public Rational divide(Rational other)
// YOUR CODE HERE {
// HINT: Division is multiplication by reciprocal if (other.numerator == 0)
// Don't forget to check if other.numerator is zero! {
return null; // Remove this line when implemented throw new ArithmeticException("Cannot divide by zero");
}
int newNumerator = this.numerator * other.denominator;
int newDenominator = this.denominator * other.numerator;
return new Rational(newNumerator, newDenominator);
} }
// TODO: Static method - r1 / r2 // TODO: Static method - r1 / r2
public static Rational divide(Rational r1, Rational r2) { public static Rational divide(Rational r1, Rational r2)
// YOUR CODE HERE {
return null; // Remove this line when implemented return r1.divide(r2);
} }
// PROVIDED - String representation // PROVIDED - String representation
+19 -8
View File
@@ -7,28 +7,35 @@ public class Main {
private static final MovieService movieService = new MovieService(); private static final MovieService movieService = new MovieService();
private static final Scanner scanner = new Scanner(System.in); private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) { public static void main(String[] args)
{
System.out.println("\n=== MOVIE API CLIENT ===\n"); System.out.println("\n=== MOVIE API CLIENT ===\n");
boolean running = true; boolean running = true;
while (running) { while (running)
{
displayMenu(); displayMenu();
int choice = getIntInput("Enter your choice: "); int choice = getIntInput("Enter your choice: ");
switch (choice) { switch (choice)
{
case 1: case 1:
movieService.displayGenres(); movieService.displayGenres();
break; break;
case 2: case 2:
// TODO: Get genre ID and page from user, then call movieService.displayMoviesByGenre() // TODO: Get genre ID and page from user, then call movieService.displayMoviesByGenre()
Long genreId = getLongInput("Enter genre ID: ");
Long page = getLongInput("Enter page number: ");
movieService.displayMoviesByGenre(genreId, page);
break; break;
case 3: case 3:
// TODO (BONUS): Get movie ID from user, then call movieService.displayMovieDetails() // TODO (BONUS): Get movie ID from user, then call movieService.displayMovieDetails()
Long movieId = getLongInput("Enter movie ID: ");
movieService.displayMovieDetails(movieId);
break; break;
@@ -42,7 +49,8 @@ public class Main {
System.out.println("Invalid choice! Please enter 0-4"); System.out.println("Invalid choice! Please enter 0-4");
} }
if (running) { if (running)
{
System.out.println("\nPress Enter to continue..."); System.out.println("\nPress Enter to continue...");
scanner.nextLine(); scanner.nextLine();
scanner.nextLine(); scanner.nextLine();
@@ -65,7 +73,8 @@ public class Main {
private static int getIntInput(String prompt) { private static int getIntInput(String prompt) {
System.out.print(prompt); System.out.print(prompt);
while (!scanner.hasNextInt()) { while (!scanner.hasNextInt())
{
System.out.print("Invalid input. " + prompt); System.out.print("Invalid input. " + prompt);
scanner.next(); scanner.next();
} }
@@ -74,9 +83,11 @@ public class Main {
return input; return input;
} }
private static Long getLongInput(String prompt) { private static Long getLongInput(String prompt)
{
System.out.print(prompt); System.out.print(prompt);
while (!scanner.hasNextLong()) { while (!scanner.hasNextLong())
{
System.out.print("Invalid input. " + prompt); System.out.print("Invalid input. " + prompt);
scanner.next(); scanner.next();
} }
+203 -6
View File
@@ -6,19 +6,216 @@ import java.util.List;
* Model class representing a movie * Model class representing a movie
* Fields: id, title, genres, images * Fields: id, title, genres, images
*/ */
public class Movie { public class Movie {
private Long id; private Long id;
private String title; private String title;
private List<Genre> genres;
private String images;
private String year;
private String imdbRating;
private String country;
private List<String> genres;
private List<String> images;
private String year;
private String imdb_id;
private String rated;
private String released;
private String runtime;
private String director;
private String writer;
private String actors;
private String plot;
private String language;
private String country;
private String awards;
private String poster;
private Double imdb_rating;
private String imdb_votes;
private String type;
private String metascore;
private String genre;
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public Movie(Long id, String title, List<String> genres, List<String> images,
String year, Double imdb_rating, String country)
{
this.id = id;
this.title = title;
this.genres = genres;
this.images = images;
this.year = year;
this.imdb_rating = imdb_rating;
this.country = country;
}
public String getDirector() { return director; }
public String getWriter() { return writer; }
public String getActors() { return actors; }
public String getPlot() { return plot; }
public String getLanguage() { return language; }
public String getAwards() { return awards; }
public String getPoster() { return poster; }
public String getImdb_votes() { return imdb_votes; }
public String getType() { return type; }
public String getMetascore() { return metascore; }
public String getRated() { return rated; }
public String getReleased() { return released; }
public String getRuntime() { return runtime; }
public String getImdb_id() { return imdb_id; }
public Movie(Long id, String title)
{
this.id = id;
this.title = title;
}
public Movie() {}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public List<String> getGenres()
{
return genres;
}
public void setGenres(List<String> genres)
{
this.genres = genres;
}
public List<String> getImages()
{
return images;
}
public void setImages(List<String> images)
{
this.images = images;
}
public String getYear()
{
return year;
}
public void setYear(String year)
{
this.year = year;
}
public Double getImdb_rating()
{
return imdb_rating;
}
public void setImdb_rating(Double imdb_rating)
{
this.imdb_rating = imdb_rating;
}
public String getCountry()
{
return country;
}
public void setCountry(String country)
{
this.country = country;
}
public void setDirector(String director) {
this.director = director;
}
public void setWriter(String writer) {
this.writer = writer;
}
public void setActors(String actors) {
this.actors = actors;
}
public void setPlot(String plot) {
this.plot = plot;
}
public void setLanguage(String language) {
this.language = language;
}
public void setAwards(String awards) {
this.awards = awards;
}
public void setPoster(String poster) {
this.poster = poster;
}
public void setImdb_votes(String imdb_votes) {
this.imdb_votes = imdb_votes;
}
public void setType(String type) {
this.type = type;
}
public void setMetascore(String metascore) {
this.metascore = metascore;
}
public void setRated(String rated) {
this.rated = rated;
}
public void setReleased(String released) {
this.released = released;
}
public void setRuntime(String runtime) {
this.runtime = runtime;
}
public void setImdb_id(String imdb_id) {
this.imdb_id = imdb_id;
}
@Override
public String toString()
{
return title + " (" + year + ") - IMDB: " + imdb_rating;
}
}
// TODO: Create a constructor with all fields // TODO: Create a constructor with all fields
// TODO: Create a simple constructor with id and title only // TODO: Create a simple constructor with id and title only
// TODO: Create getters and setters for all fields // TODO: Create getters and setters for all fields
}
+121 -52
View File
@@ -90,7 +90,8 @@ public class MovieService {
/** /**
* TODO 1: Display movies for a specific genre and page * TODO 1: Display movies for a specific genre and page
* *
* JSON format: { "movies": [ { "id": 1, "title": "Movie", "year": "1994" } ], "total": 177, "total_pages": 18 } * Open this link in your browser (and check Pretty-print checkbox) to view json format:
* https://api.meshcomp.ir/api/v1/genres/1/movies
* *
* Steps: * Steps:
* - Call getMoviesByGenreList(genreId, page) to get JSON * - Call getMoviesByGenreList(genreId, page) to get JSON
@@ -101,11 +102,39 @@ public class MovieService {
* *
* PUT ALL PARSING CODE INSIDE try-catch * PUT ALL PARSING CODE INSIDE try-catch
*/ */
public void displayMoviesByGenre(Long genreId, Long page) { public void displayMoviesByGenre(Long genreId, Long page)
// TODO: Write your code here {
// All parsing code must be inside try-catch try
try { {
// Your code here String json = getMoviesByGenreList(genreId, page);
if (json == null)
{
System.out.println("Failed to load movies");
return;
}
JsonObject root = JsonParser.parseString(json).getAsJsonObject();
long total = root.get("total").getAsLong();
long totalPages = root.get("total_pages").getAsLong();
JsonArray moviesArray = root.getAsJsonArray("movies");
System.out.println("\nMovies for genre " + genreId + " | Page " + page);
System.out.println("----------------------------------------------");
for (JsonElement element : moviesArray) {
JsonObject movieObj = element.getAsJsonObject();
Long id = movieObj.get("id").getAsLong();
String title = movieObj.get("title").getAsString();
String year = movieObj.get("year").getAsString();
System.out.println("ID: " + id + " | " + title + " (" + year + ")");
}
System.out.println("\nTotal Movies: " + total);
System.out.println("Total Pages: " + totalPages);
System.out.println();
} catch (Exception e) { } catch (Exception e) {
System.err.println("ERROR: " + e.getMessage()); System.err.println("ERROR: " + e.getMessage());
@@ -113,76 +142,116 @@ public class MovieService {
} }
/** /**
* BONUS: Fetch movie details from API using movie ID * BONUS: Fetch movie details from API using movie ID
* URL format: https://api.meshcomp.ir/api/v1/movies/{movieId} * URL format: https://api.meshcomp.ir/api/v1/movies/{movieId}
*/ */
private String getMovieById(Long movieId) { private String getMovieById(Long movieId) {
try { try {
// TODO: Complete this method String url = MOVIE_BY_ID_URL + movieId;
// String url = MOVIE_BY_ID_URL + movieId;
// HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build(); HttpRequest request = HttpRequest.newBuilder()
// HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); .uri(URI.create(url))
// if (response.statusCode() == 200) return response.body(); .build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return response.body();
} else {
System.out.println("HTTP error code: " + response.statusCode());
}
return null;
} catch (Exception e) { } catch (Exception e) {
System.out.println("!!Exception : " + e.getMessage()); System.out.println("!!Exception : " + e.getMessage());
} }
return null; return null;
} }
/**
* BONUS TODO: Display complete movie details for a given movie ID /*
* BONUS TODO: Display complete movie details for a given movie ID
* Expected JSON format from getMovieById():
* { Open this link in your browser (and check Pretty-print checkbox) to view json format:
* "id": 1, https://api.meshcomp.ir/api/v1/movies/1
* "title": "The Shawshank Redemption",
* "year": "1994", INSTRUCTIONS:
* "genres": ["Crime", "Drama"], 1. Call getMovieById(movieId) to get the JSON response string
* "poster": "https://moviesapi.ir/images/tt0111161_poster.jpg", 2. Check if response is null (if yes, print error and return)
* "country": "USA", 3. Parse the JSON string to JsonObject (inside try-catch)
* "imdb_rating": "9.3" 4. Use Gson to extract all fields from the JSON and map them to your Movie class:
* } - Add missing JSON fields to your Movie class first (actors, awards, director,
* imdb_id, imdb_votes, metascore, plot, rated, released, runtime, type, writer)
* INSTRUCTIONS: - Then use Gson to parse your json string
* 1. Call getMovieById(movieId) to get the JSON response 5. Print all movie details in a readable format
* 2. Check if response is null (if yes, print error and return)
* 3. Parse the JSON string to JsonObject (inside try-catch) (PUT ALL PARSING CODE INSIDE THE EXISTING try-catch)
* 4. Extract all fields: id, title, year, genres array, poster, country, imdb_rating
* 5. Convert genres array to comma-separated string
* 6. Print all movie details in a readable format
*
* PUT ALL PARSING CODE INSIDE THE EXISTING try-catch
*
* @param movieId The ID of the movie to display
*/ */
public void displayMovieDetails(Long movieId) {
// Step 1: Get JSON response from API /* EXAMPLE OUTPUT:
=======================================================================
THE SHAWSHANK REDEMPTION (1994)
=======================================================================
Rating: 9.3/10 | Votes: 1,738,596
Director: Frank Darabont
Cast: Tim Robbins, Morgan Freeman, Bob Gunton, William Sadler
Genre: Crime, Drama
Runtime: 142 min | Rated: R
Country: USA | Released: 14 Oct 1994
Plot: Two imprisoned men bond over a number of years, finding
solace and eventual redemption through acts of common decency.
Awards: Nominated for 7 Oscars. Another 19 wins & 30 nominations.
=======================================================================
*/
public void displayMovieDetails(Long movieId)
{
String jsonResponse = getMovieById(movieId); String jsonResponse = getMovieById(movieId);
// Step 2: Check if we got valid data if (jsonResponse == null)
if (jsonResponse == null) { {
System.out.println("Failed to get movie details for ID: " + movieId); System.out.println("Failed to get movie details for ID: " + movieId);
return; return;
} }
// Step 3-6: Parse JSON and display details (PUT YOUR CODE INSIDE try-catch) try
try { {
// TODO: Parse jsonResponse to JsonObject Gson gson = new Gson();
// TODO: Extract id, title, year, genres array, poster, country, imdb_rating Movie movie = gson.fromJson(jsonResponse, Movie.class);
// TODO: Convert genres array to a readable string
// TODO: Print all movie details
// HINT: Use JsonParser.parseString(jsonResponse).getAsJsonObject()
// HINT: Use getAsJsonArray("genres") to get genres
// HINT: For each genre, use getAsString()
// Your code here: System.out.println("\n=======================================================================");
System.out.println(" " + movie.getTitle() + " (" + movie.getYear() + ")");
System.out.println("=======================================================================\n");
System.out.println("Rating: " + movie.getImdb_rating() + "/10 | Votes: " + movie.getImdb_votes());
System.out.println("Director: " + movie.getDirector());
System.out.println("Cast: " + movie.getActors());
if (movie.getGenres() != null) {
System.out.println("Genre: " + String.join(", ", movie.getGenres()));
}
} catch (Exception e) { System.out.println("Runtime: " + movie.getRuntime() + " | Rated: " + movie.getRated());
System.out.println("Country: " + movie.getCountry() + " | Released: " + movie.getReleased());
System.out.println("Plot: " + movie.getPlot());
System.out.println("Awards: " + movie.getAwards());
System.out.println("=======================================================================\n");
}
catch (Exception e)
{
System.err.println("ERROR parsing movie details: " + e.getMessage()); System.err.println("ERROR parsing movie details: " + e.getMessage());
} }
} }
} }