world_top_movies 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +123 -0
- data/LICENSE.txt +21 -0
- data/README.md +47 -0
- data/Rakefile +14 -0
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/config/environment.rb +24 -0
- data/db/development.db +0 -0
- data/db/migrate/20210915070733_create_users.rb +9 -0
- data/db/migrate/20210915070741_create_movies.rb +20 -0
- data/db/migrate/20210915070809_create_user_movies.rb +10 -0
- data/db/migrate/20210922215417_create_notes.rb +10 -0
- data/db/migrate/20210922215434_create_user_notes.rb +10 -0
- data/db/schema.rb +60 -0
- data/db/seeds.rb +5 -0
- data/lib/world_top_movies/cli.rb +334 -0
- data/lib/world_top_movies/models/movie.rb +63 -0
- data/lib/world_top_movies/models/note.rb +21 -0
- data/lib/world_top_movies/models/user.rb +67 -0
- data/lib/world_top_movies/models/user_movie.rb +9 -0
- data/lib/world_top_movies/models/user_note.rb +9 -0
- data/lib/world_top_movies/movie.rb +179 -0
- data/lib/world_top_movies/scraper.rb +51 -0
- data/lib/world_top_movies/version.rb +5 -0
- data/lib/world_top_movies.rb +10 -0
- data/world_top_movies.gemspec +36 -0
- metadata +190 -0
@@ -0,0 +1,334 @@
|
|
1
|
+
class WorldTopMovies::CLI
|
2
|
+
attr_accessor :movie_instance, :genre, :status
|
3
|
+
|
4
|
+
@@prompt = TTY::Prompt.new
|
5
|
+
@@user = nil
|
6
|
+
|
7
|
+
def self.prompt
|
8
|
+
@@prompt
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.user
|
12
|
+
@@user
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
introduce
|
17
|
+
select_movies_lookup_or_fav_movies
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def introduce
|
23
|
+
# Introduces the app and ask for user credentials
|
24
|
+
artii = Artii::Base.new({})
|
25
|
+
puts "-----------------------------------------------------------------------------------"
|
26
|
+
puts artii.asciify("World's Top Movies!")
|
27
|
+
puts "-----------------------------------------------------------------------------------"
|
28
|
+
puts " By Harold Torres Marino | p: +61 401 927 123 | e: haroldtm55@gmail.com".colorize(:mode => :italic)
|
29
|
+
puts "-----------------------------------------------------------------------------------"
|
30
|
+
sleep(0.5)
|
31
|
+
puts "\nWelcome to the World's Top Movies of all times, a place where you can look up for worldwide top rated movies!\n\n"
|
32
|
+
sleep(1.5)
|
33
|
+
username = self.class.prompt.ask("Please enter your username to log in or sign up: ") do |q|
|
34
|
+
q.required(true, "Oops, seems you haven't provided your username! Try again please.")
|
35
|
+
q.validate(/^[a-zA-Z0-9._-]+$/, "Oops, seems like that username is invalid. Only alphanumerical characters plus . - _ are allowed. Try again please.")
|
36
|
+
q.modify(:down)
|
37
|
+
end
|
38
|
+
@@user = WorldTopMovies::DB::User.find_or_create_by(username: username)
|
39
|
+
sleep(0.5)
|
40
|
+
puts "\nThanks #{username}. I'd like to ask you some questions, ok?"
|
41
|
+
end
|
42
|
+
|
43
|
+
def select_movies_lookup_or_fav_movies
|
44
|
+
options = [
|
45
|
+
"Top Movies Lookup",
|
46
|
+
"My Favourite Movies section",
|
47
|
+
"My Movie Notes",
|
48
|
+
"Exit app",
|
49
|
+
]
|
50
|
+
next_action = self.class.prompt.select(
|
51
|
+
"\nPlease choose where you want to go...", options
|
52
|
+
)
|
53
|
+
next_action == options[0] && scrape_and_print_movies
|
54
|
+
next_action == options[1] && select_action_favourite_movies
|
55
|
+
next_action == options[2] && select_action_movies_with_notes
|
56
|
+
next_action == options.last && close_app
|
57
|
+
select_next_action
|
58
|
+
end
|
59
|
+
|
60
|
+
def scrape_and_print_movies
|
61
|
+
# Asks the user which genre they want to see, then scrapres and generates the instances
|
62
|
+
sleep(1.5)
|
63
|
+
puts ""
|
64
|
+
type_of_scrape = self.class.prompt.select(
|
65
|
+
"Would you like to see the list of all movies in general or by genre?",
|
66
|
+
%w(General Genre)
|
67
|
+
)
|
68
|
+
puts "\nAlright! We're going to see the top #{type_of_scrape} movies..."
|
69
|
+
puts ""
|
70
|
+
sleep(1.5)
|
71
|
+
self.genre = nil if type_of_scrape == "General"
|
72
|
+
type_of_scrape == "Genre" && (self.genre = self.class.prompt.select(
|
73
|
+
"Choose a genre:\n", WorldTopMovies::Scraper.genres
|
74
|
+
))
|
75
|
+
WorldTopMovies::Movie.scrape_and_print_movies_compact(self.genre)
|
76
|
+
end
|
77
|
+
|
78
|
+
def select_next_action
|
79
|
+
# Ask user to select a new action and re run the app from the chosen action
|
80
|
+
puts ""
|
81
|
+
sleep(0.5)
|
82
|
+
options = [
|
83
|
+
"See more info of a movie from the last selected genre or general list",
|
84
|
+
"Add favourite movies from the last selected genre or general list",
|
85
|
+
"Start a new lookup",
|
86
|
+
"Go to My Favourite Movies section",
|
87
|
+
"Go to My Movie notes",
|
88
|
+
"Print all the movies displayed so far",
|
89
|
+
"Exit app",
|
90
|
+
]
|
91
|
+
|
92
|
+
next_action = self.class.prompt.select(
|
93
|
+
"What would you like to do now?", options
|
94
|
+
)
|
95
|
+
if next_action == options[0]
|
96
|
+
select_and_print_specific_movie
|
97
|
+
add_fav_or_leave_note
|
98
|
+
end
|
99
|
+
next_action == options[1] && add_favourite_movies
|
100
|
+
next_action == options[2] && scrape_and_print_movies
|
101
|
+
next_action == options[3] && select_action_favourite_movies
|
102
|
+
next_action == options[4] && select_action_movies_with_notes
|
103
|
+
next_action == options[5] && WorldTopMovies::Movie.scrape_and_print_movies_compact("all")
|
104
|
+
next_action == options.last && close_app
|
105
|
+
select_next_action
|
106
|
+
end
|
107
|
+
|
108
|
+
def select_and_print_specific_movie
|
109
|
+
# Asks user to select a movie from print_movies_compact
|
110
|
+
sleep(0.5)
|
111
|
+
puts ""
|
112
|
+
movie_url = self.class.prompt.select(
|
113
|
+
"Select a movie: ", WorldTopMovies::Movie.all_titles_and_links_hash_by_genre(self.genre), enum: ")"
|
114
|
+
)
|
115
|
+
self.movie_instance = WorldTopMovies::Movie.find_by_url(movie_url)
|
116
|
+
self.movie_instance.scrape_and_print_movie
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_favourite_movies
|
120
|
+
# Finds or creates a new Favourite movie instances and adds them to the database
|
121
|
+
sleep(0.5)
|
122
|
+
movie_urls = self.class.prompt.multi_select(
|
123
|
+
"\nSelect movies: ", WorldTopMovies::Movie.all_titles_and_links_hash_by_genre(self.genre), enum: ")",
|
124
|
+
)
|
125
|
+
WorldTopMovies::DB::Movie.add_movies(user: self.class.user, movie_urls: movie_urls)
|
126
|
+
movie_urls.size > 0 ? puts("\nThe movie(s) have been added to your favourites!") : puts("\nNo movies were selected.")
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_fav_or_leave_note
|
130
|
+
options = [
|
131
|
+
"Add it to your favourites",
|
132
|
+
"Leave a note",
|
133
|
+
"Nothing in particular",
|
134
|
+
]
|
135
|
+
puts ""
|
136
|
+
next_action = self.class.prompt.select(
|
137
|
+
"What would you like to do with this movie?", options
|
138
|
+
)
|
139
|
+
next_action == options[0] && add_favourite_movie
|
140
|
+
next_action == options[1] && leave_note
|
141
|
+
select_next_action
|
142
|
+
end
|
143
|
+
|
144
|
+
def leave_note
|
145
|
+
note_message = self.class.prompt.ask("Please leave your note\n\n") do |q|
|
146
|
+
q.required(true, "Oops, seems you haven't left any note! Try again please.")
|
147
|
+
end
|
148
|
+
WorldTopMovies::DB::Note.create_note(movie_url: self.movie_instance.url, note_message: note_message, user: self.class.user)
|
149
|
+
puts "\nYour note has been saved!"
|
150
|
+
sleep(1.5)
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_favourite_movie
|
154
|
+
# Finds or creates a new Favourite movie instance and adds it to the database
|
155
|
+
sleep(0.5)
|
156
|
+
# add_to_favourite = self.class.prompt.yes?("\nWould you like to add this movie to your favourites?")
|
157
|
+
if self.class.user.movies.none? { |m| m.url == self.movie_instance.url }
|
158
|
+
WorldTopMovies::DB::Movie.add_movies(user: self.class.user, movie_urls: self.movie_instance.url)
|
159
|
+
puts "\n#{self.movie_instance.title} has been added to your favourite movies!"
|
160
|
+
else
|
161
|
+
puts("\nOops! #{self.movie_instance.title} is already in your favourites!")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def delete_favourite_movies
|
166
|
+
sleep(0.5)
|
167
|
+
if self.class.user.movies.empty?
|
168
|
+
puts "\nOops, you haven't favourited any movies yet!!"
|
169
|
+
else
|
170
|
+
movie_urls = self.class.prompt.multi_select(
|
171
|
+
"\nSelect movies: ", self.class.user.favourite_movie_titles, enum: ")",
|
172
|
+
)
|
173
|
+
movie_urls.each do |movie_url|
|
174
|
+
WorldTopMovies::DB::UserMovie.delete_movie_record_from_user(user: self.class.user, movie_url: movie_url)
|
175
|
+
WorldTopMovies::Movie.delete_movie_instance_from_user(user: self.class.user, movie_url: movie_url)
|
176
|
+
end
|
177
|
+
movie_urls.size > 0 ? puts("\nThe movie(s) have been successfully deleted") : puts("\nNo movies were selected.")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def print_favourite_movies
|
182
|
+
sleep(1)
|
183
|
+
if !self.class.user.movies.empty?
|
184
|
+
self.class.user.print_all_favourite_movie_titles
|
185
|
+
else
|
186
|
+
puts "\nOops, you haven't favourited any movies yet!!"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def select_and_print_specific_favourite_movie
|
191
|
+
# Asks user to select a movie from print_movies_compact
|
192
|
+
sleep(0.5)
|
193
|
+
if self.class.user.movies.empty?
|
194
|
+
puts "\nOops, you haven't favourited any movies yet!!"
|
195
|
+
else
|
196
|
+
puts ""
|
197
|
+
|
198
|
+
movie_url = self.class.prompt.select(
|
199
|
+
"Select a movie: ", self.class.user.favourite_movie_titles, enum: ")"
|
200
|
+
)
|
201
|
+
WorldTopMovies::DB::Movie.all.find { |m| m.url == movie_url }.print_movie_details
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def select_action_favourite_movies
|
206
|
+
puts ""
|
207
|
+
if self.class.user.movies.empty?
|
208
|
+
puts "Oops, you haven't favourited any movies yet, let's change that!!"
|
209
|
+
scrape_and_print_movies
|
210
|
+
select_next_action
|
211
|
+
else
|
212
|
+
sleep(0.5)
|
213
|
+
options = [
|
214
|
+
"See all your favourite movies",
|
215
|
+
"See more info of any of your favourite movies",
|
216
|
+
"Delete any of your favourite movies",
|
217
|
+
"Take me to the top movies lookup",
|
218
|
+
"Exit app",
|
219
|
+
]
|
220
|
+
|
221
|
+
next_action = self.class.prompt.select(
|
222
|
+
"What would you like to do now?", options
|
223
|
+
)
|
224
|
+
if next_action == options[0]
|
225
|
+
print_favourite_movies
|
226
|
+
select_action_favourite_movies
|
227
|
+
elsif next_action == options[1]
|
228
|
+
select_and_print_specific_favourite_movie
|
229
|
+
select_action_favourite_movies
|
230
|
+
elsif next_action == options[2]
|
231
|
+
delete_favourite_movies
|
232
|
+
select_action_favourite_movies
|
233
|
+
elsif next_action == options[3]
|
234
|
+
scrape_and_print_movies
|
235
|
+
select_next_action
|
236
|
+
else
|
237
|
+
close_app
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def print_all_notes
|
243
|
+
if !self.class.user.notes.empty?
|
244
|
+
self.class.user.print_all_notes
|
245
|
+
else
|
246
|
+
puts "\nOops, you haven't left any notes yet!!"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def print_movies_with_notes
|
251
|
+
sleep(1)
|
252
|
+
if !self.class.user.notes.empty?
|
253
|
+
self.class.user.print_movies_with_notes
|
254
|
+
else
|
255
|
+
puts "\nOops, you haven't left any notes yet!!"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def select_and_print_specific_movie_with_notes
|
260
|
+
# Asks user to select a movie from print_movies_compact
|
261
|
+
sleep(0.5)
|
262
|
+
if self.class.user.notes.empty?
|
263
|
+
puts "\nOops, you haven't left any notes yet!!"
|
264
|
+
else
|
265
|
+
puts ""
|
266
|
+
movie_url = self.class.prompt.select(
|
267
|
+
"Select a movie: ", self.class.user.movies_with_notes_titles, enum: ")"
|
268
|
+
)
|
269
|
+
WorldTopMovies::DB::Movie.all.find { |m| m.url == movie_url }.print_movie_details
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def delete_notes
|
274
|
+
sleep(0.5)
|
275
|
+
if self.class.user.movies.empty?
|
276
|
+
puts "\nOops, you haven't left any notes yet!!"
|
277
|
+
else
|
278
|
+
note_ids = self.class.prompt.multi_select(
|
279
|
+
"\nSelect notes: ", self.class.user.notes_titles, enum: ")",
|
280
|
+
)
|
281
|
+
note_ids.each do |note_id|
|
282
|
+
WorldTopMovies::DB::UserNote.delete_note_record_from_user(user: self.class.user, note_id: note_id)
|
283
|
+
WorldTopMovies::DB::Note.delete_note_instance_from_user(user: self.class.user, note_id: note_id)
|
284
|
+
end
|
285
|
+
note_ids.size > 0 ? puts("\nThe note(s) have been successfully deleted") : puts("\nNo notes were selected.")
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def select_action_movies_with_notes
|
290
|
+
puts ""
|
291
|
+
if self.class.user.notes.empty?
|
292
|
+
puts "Oops, you haven't left any notes yet, let's change that!!"
|
293
|
+
scrape_and_print_movies
|
294
|
+
select_next_action
|
295
|
+
else
|
296
|
+
sleep(0.5)
|
297
|
+
options = [
|
298
|
+
"See all my notes",
|
299
|
+
"See all your movies with notes",
|
300
|
+
"Open specific movie with notes",
|
301
|
+
"Delete any of your notes",
|
302
|
+
"Take me to the top movies lookup",
|
303
|
+
"Exit app",
|
304
|
+
]
|
305
|
+
|
306
|
+
next_action = self.class.prompt.select(
|
307
|
+
"What would you like to do now?", options
|
308
|
+
)
|
309
|
+
if next_action == options[0]
|
310
|
+
print_all_notes
|
311
|
+
select_action_movies_with_notes
|
312
|
+
elsif next_action == options[1]
|
313
|
+
print_movies_with_notes
|
314
|
+
select_action_movies_with_notes
|
315
|
+
elsif next_action == options[2]
|
316
|
+
select_and_print_specific_movie_with_notes
|
317
|
+
select_action_movies_with_notes
|
318
|
+
elsif next_action == options[3]
|
319
|
+
delete_notes
|
320
|
+
select_action_movies_with_notes
|
321
|
+
elsif next_action == options[4]
|
322
|
+
scrape_and_print_movies
|
323
|
+
select_next_action
|
324
|
+
else
|
325
|
+
close_app
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def close_app
|
331
|
+
puts "\nOk #{self.class.user.username}, hope you enjoyed your time with me!"
|
332
|
+
exit!
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class WorldTopMovies::DB::Movie < ActiveRecord::Base
|
2
|
+
has_many :user_movies
|
3
|
+
has_many :users, through: :user_movies
|
4
|
+
has_many :notes
|
5
|
+
|
6
|
+
def self.generate_attributes_from_url(movie_url)
|
7
|
+
fav_movie = WorldTopMovies::Movie.find_by_url(movie_url)
|
8
|
+
attributes = {
|
9
|
+
title: fav_movie.title,
|
10
|
+
year: fav_movie.year,
|
11
|
+
duration: fav_movie.duration,
|
12
|
+
genres: fav_movie.genres.join(" - "),
|
13
|
+
user_rating: fav_movie.user_rating,
|
14
|
+
metascore: fav_movie.metascore,
|
15
|
+
description: fav_movie.description,
|
16
|
+
director: fav_movie.director,
|
17
|
+
stars: fav_movie.stars.join(" - "),
|
18
|
+
votes: fav_movie.votes,
|
19
|
+
gross_revenue: fav_movie.gross_revenue,
|
20
|
+
url: fav_movie.url,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.add_movies(user:, movie_urls:)
|
25
|
+
# Finds or creates new Favourite movie instances and adds them to the given user
|
26
|
+
movie_urls.class != Array && movie_urls = movie_urls.split()
|
27
|
+
movie_urls.each do |movie_url|
|
28
|
+
if user.movies.none? { |m| m.url == movie_url }
|
29
|
+
attributes = self.generate_attributes_from_url(movie_url)
|
30
|
+
user.movies << self.find_or_create_by(attributes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_movie_details
|
36
|
+
attributes = {
|
37
|
+
title: self.title,
|
38
|
+
year: self.year,
|
39
|
+
duration: self.duration,
|
40
|
+
genres: self.genres,
|
41
|
+
user_rating: self.user_rating,
|
42
|
+
metascore: self.metascore.to_s,
|
43
|
+
description: self.description,
|
44
|
+
director: self.director,
|
45
|
+
stars: self.stars,
|
46
|
+
votes: self.votes,
|
47
|
+
gross_revenue: self.gross_revenue,
|
48
|
+
url: self.url,
|
49
|
+
database: true,
|
50
|
+
}
|
51
|
+
movie = WorldTopMovies::Movie.new(attributes)
|
52
|
+
movie.scrape_and_print_movie
|
53
|
+
print_movie_notes
|
54
|
+
end
|
55
|
+
|
56
|
+
def print_movie_notes
|
57
|
+
logged_user = WorldTopMovies::CLI.user
|
58
|
+
notes = self.notes.select { |n| n.user && n.user.username == logged_user.username }
|
59
|
+
puts "\n----------------#{"My Own notes".bold}------------------"
|
60
|
+
notes.empty? ? puts("No notes left") : notes.each { |n| puts "\n-#{n.note_message}- on #{n.created_at.localtime}" }
|
61
|
+
puts "----------------------------------------------"
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class WorldTopMovies::DB::Note < ActiveRecord::Base
|
2
|
+
has_one :user_note, dependent: :destroy
|
3
|
+
has_one :user, through: :user_note
|
4
|
+
belongs_to :movie
|
5
|
+
|
6
|
+
def self.create_note(movie_url:, note_message:, user:)
|
7
|
+
#Creates a note to a movie that is created if it's not in the database, and appends it to the user
|
8
|
+
attributes = WorldTopMovies::DB::Movie.generate_attributes_from_url(movie_url)
|
9
|
+
if !WorldTopMovies::DB::Movie.find_by(url: movie_url)
|
10
|
+
movie_record = WorldTopMovies::DB::Movie.create(attributes)
|
11
|
+
else
|
12
|
+
movie_record = WorldTopMovies::DB::Movie.find_by(url: movie_url)
|
13
|
+
end
|
14
|
+
note_record = self.create(note_message: note_message, movie_id: movie_record.id)
|
15
|
+
user.notes << note_record
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.delete_note_instance_from_user(user:, note_id:)
|
19
|
+
user.notes.delete(user.find_note_from_id(note_id))
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class WorldTopMovies::DB::User < ActiveRecord::Base
|
2
|
+
has_many :user_movies, dependent: :destroy
|
3
|
+
has_many :movies, through: :user_movies
|
4
|
+
has_many :user_notes, dependent: :destroy
|
5
|
+
has_many :notes, through: :user_notes
|
6
|
+
|
7
|
+
def print_all_favourite_movie_titles
|
8
|
+
puts "\nOk! Your favourite movies are:\n\n"
|
9
|
+
sleep(1.5)
|
10
|
+
self.movies.sort_by { |m| m.title }.each_with_index do |m, i|
|
11
|
+
sleep(0.01)
|
12
|
+
WorldTopMovies::Movie.print_movie_compact(m, i)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def favourite_movie_titles
|
17
|
+
# returns a hash with key=title, value=url of all fav movies
|
18
|
+
result = {}
|
19
|
+
self.movies.sort_by { |m| m.title }.each { |m| result[m.title] = m.url }
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_movie_from_url(movie_url)
|
24
|
+
self.movies.find { |m| m.url == movie_url }
|
25
|
+
end
|
26
|
+
|
27
|
+
def print_movies_with_notes
|
28
|
+
puts "\nOk! Your movies with notes are:\n\n"
|
29
|
+
sleep(1.5)
|
30
|
+
self.movies_with_notes_arr.sort_by { |m| m.title }.each_with_index do |m, i|
|
31
|
+
sleep(0.01)
|
32
|
+
WorldTopMovies::Movie.print_movie_compact(m, i)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def movies_with_notes_titles
|
37
|
+
result = {}
|
38
|
+
self.movies_with_notes_arr.sort_by { |m| m.title }.each { |m| result[m.title] = m.url }
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
def movies_with_notes_arr
|
43
|
+
self.notes.map { |note| note.movie }.uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
def print_all_notes
|
47
|
+
puts "\nPrinting #{self.notes.count} note(s):\n\n"
|
48
|
+
sleep(1.5)
|
49
|
+
self.notes.each_with_index do |n, i|
|
50
|
+
sleep(0.01)
|
51
|
+
puts "--------------------------------------------------------------"
|
52
|
+
puts "\n#{i + 1}. #{n.note_message.colorize(:color => :green).italic}, \
|
53
|
+
Movie: #{n.movie.title.colorize(:color => :light_blue).bold}, \
|
54
|
+
Date: #{n.created_at.localtime.to_s.colorize(:color => :red).bold} \n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def notes_titles
|
59
|
+
result = {}
|
60
|
+
self.notes.each { |n| result["#{n.note_message}, #{n.movie.title}, #{n.created_at.localtime.to_s}"] = n.id }
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_note_from_id(note_id)
|
65
|
+
self.notes.find { |n| n.id == note_id }
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class WorldTopMovies::DB::UserMovie < ActiveRecord::Base
|
2
|
+
belongs_to :user
|
3
|
+
belongs_to :movie
|
4
|
+
|
5
|
+
def self.delete_movie_record_from_user(user:, movie_url:)
|
6
|
+
self.joins(:user, :movie)
|
7
|
+
.where("movies.url" => movie_url, "users.username" => user.username).destroy_all
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class WorldTopMovies::DB::UserNote < ActiveRecord::Base
|
2
|
+
belongs_to :user
|
3
|
+
belongs_to :note
|
4
|
+
|
5
|
+
def self.delete_note_record_from_user(user:, note_id:)
|
6
|
+
self.joins(:user, :note)
|
7
|
+
.where("notes.id" => note_id, "users.username" => user.username).destroy_all
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
class WorldTopMovies::Movie
|
2
|
+
attr_reader :award, :storyline, :languages, :official_site, :countries_of_orig
|
3
|
+
|
4
|
+
@@all = []
|
5
|
+
|
6
|
+
def initialize(attributes)
|
7
|
+
#Utilises metaprogramming to create new instances
|
8
|
+
attributes.each do |key, value|
|
9
|
+
self.class.attr_accessor(key)
|
10
|
+
self.send(("#{key}="), value)
|
11
|
+
end
|
12
|
+
# Add instance to @all only if it's not already there
|
13
|
+
self.class.all << self if self.class.all.none? { |m| m.url == self.url } && !self.database
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.new_from_page(m)
|
17
|
+
# Creates new instance with the attributes from general page
|
18
|
+
self.new({
|
19
|
+
title: m.css("h3 a").text,
|
20
|
+
year: m.css("h3 span.lister-item-year").text[1...-1].scan(/[0-9]/).join(),
|
21
|
+
duration: m.css("span.runtime").text,
|
22
|
+
genres: m.css("span.genre").text.strip.split(", "),
|
23
|
+
user_rating: m.css("div strong").text.to_f,
|
24
|
+
metascore: m.css("div span.metascore").text.strip.to_i,
|
25
|
+
description: m.css("p.text-muted")[1].text.strip,
|
26
|
+
director: m.css("div.lister-item-content p a")[0].text.strip,
|
27
|
+
stars: m.css("div.lister-item-content p a").slice(1..-1).map { |s| s.text },
|
28
|
+
votes: m.css("p.sort-num_votes-visible span")[1].text.gsub(",", "").to_i,
|
29
|
+
gross_revenue: m.css("p.sort-num_votes-visible span")[-1].text,
|
30
|
+
url: "https://imdb.com" + m.css("h3 a").attribute("href").value,
|
31
|
+
database: false,
|
32
|
+
})
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.all
|
36
|
+
@@all
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.all_top_general
|
40
|
+
# Filters out the movies with rating >= 8.4
|
41
|
+
self.all.select { |m| m.user_rating >= 8.4 }.sort_by { |m| m.user_rating }.reverse
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.all_by_genre(genre)
|
45
|
+
# Filters all movies from a given genre, if no genre, return general movies
|
46
|
+
return self.all_top_general if !genre
|
47
|
+
self.all.select { |m| m.genres.include?(genre) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.reset_all
|
51
|
+
self.all.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.all_titles_and_links_hash
|
55
|
+
# returns a hash with key=title, value=url of all movie instances
|
56
|
+
result = {}
|
57
|
+
self.all.each do |m|
|
58
|
+
result[m.title] = m.url
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.all_titles_and_links_hash_by_genre(genre)
|
64
|
+
# returns a hash with key=title, value=url of all movie instances from given genre
|
65
|
+
result = {}
|
66
|
+
counter = 1
|
67
|
+
self.all_by_genre(genre).each do |m|
|
68
|
+
if result.keys.none? { |key| key == m.title }
|
69
|
+
result[m.title] = m.url
|
70
|
+
else
|
71
|
+
result[m.title + "(#{counter + 1})"] = m.url
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.find_by_url(url)
|
79
|
+
self.all.find { |m| m.url == url }
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.delete_movie_instance_from_user(user:, movie_url:)
|
83
|
+
user.movies.delete(user.find_movie_from_url(movie_url))
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.scrape_and_print_movies_compact(genre = nil)
|
87
|
+
# Looks for the movies to print depending on the arg and prints title, rating, year
|
88
|
+
if genre == "all"
|
89
|
+
movies = self.all.sort_by { |m| m.user_rating }.reverse
|
90
|
+
else
|
91
|
+
WorldTopMovies::Scraper.make_movies(genre)
|
92
|
+
movies = genre == nil ? self.all_top_general : self.all_by_genre(genre)
|
93
|
+
end
|
94
|
+
puts "I'll give you #{movies.size} top movies!"
|
95
|
+
sleep(1.5)
|
96
|
+
movies.each_with_index do |movie, index|
|
97
|
+
sleep(0.01)
|
98
|
+
self.print_movie_compact(movie, index)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.print_movie_compact(movie, index)
|
103
|
+
puts "--------------------------------------------------------------"
|
104
|
+
puts "\n#{index + 1}. #{movie.title.colorize(:color => :green, :mode => :bold)}, \
|
105
|
+
Rating: #{movie.user_rating.to_s.colorize(:color => :light_blue, :mode => :bold)}, \
|
106
|
+
Year: #{movie.year.colorize(:color => :red)} \n"
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_awards_count
|
110
|
+
target = doc.css("li span.ipc-metadata-list-item__list-content-item")[0].text
|
111
|
+
@award || (@award = target if target.include?("nomination") || target.include?("win"))
|
112
|
+
end
|
113
|
+
|
114
|
+
def storyline
|
115
|
+
@storyline ||
|
116
|
+
@storyline = doc.css(
|
117
|
+
".Storyline__StorylineWrapper-sc-1b58ttw-0 div.ipc-html-content.ipc-html-content--base div"
|
118
|
+
)[0].text
|
119
|
+
end
|
120
|
+
|
121
|
+
def languages
|
122
|
+
@languages ||
|
123
|
+
@languages = doc.css(
|
124
|
+
"div[data-testid=title-details-section] li[data-testid=title-details-languages]"
|
125
|
+
)
|
126
|
+
.children[1].children[0].children.map { |l| l.text }.join(" - ")
|
127
|
+
end
|
128
|
+
|
129
|
+
def official_site
|
130
|
+
target = doc.css(
|
131
|
+
"div[data-testid=title-details-section] li[data-testid=title-details-officialsites]"
|
132
|
+
)
|
133
|
+
.children
|
134
|
+
if @official_site
|
135
|
+
@official_site
|
136
|
+
else
|
137
|
+
if target.children[0] && target.children[0].text.include?("site")
|
138
|
+
@official_site = target.children[1].children[0].children[0].attribute("href").value
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def countries_of_origin
|
144
|
+
@countries_of_origin ||
|
145
|
+
@countries_of_origin = doc.css("div[data-testid=title-details-section] li[data-testid=title-details-origin]")
|
146
|
+
.children[1].children[0].children.map { |c| c.text }.join(" - ")
|
147
|
+
end
|
148
|
+
|
149
|
+
def scrape_and_print_movie
|
150
|
+
# Prints detailed info of a selected movie from select_specific_movie, after scraping it.
|
151
|
+
description = self.description
|
152
|
+
storyline = self.storyline
|
153
|
+
puts "\n----------------------------------------------"
|
154
|
+
puts " #{self.title.upcase} - #{self.year} ".colorize(:background => :green, :color => :black).bold
|
155
|
+
puts "----------------------------------------------"
|
156
|
+
puts "\n#{"Genres:".bold} #{self.genres.class == Array && self.genres.join(" - ").green.italic || self.genres.green.italic}"
|
157
|
+
puts "#{"Duration:".bold} #{self.duration.green.italic}"
|
158
|
+
puts "#{"Stars:".bold} #{self.stars.class == Array && self.stars.join(" - ").green.italic || self.stars.green.italic}"
|
159
|
+
puts "#{"Rating:".bold} #{"#{self.user_rating} from #{self.votes} votes".green.italic}"
|
160
|
+
puts "#{"Metascore:".bold} #{self.metascore && self.metascore.to_s.green.italic || "N/A"}"
|
161
|
+
puts "#{"Directed by:".bold} #{self.director && self.director.green.italic}"
|
162
|
+
puts "#{"Total Awards:".bold} #{self.get_awards_count && self.get_awards_count.green.italic || "N/A"}"
|
163
|
+
puts "\n-----------------#{"Description".bold}-------------------"
|
164
|
+
puts "\n#{description.green.italic || "N/A"}\n"
|
165
|
+
puts "\n#{"Storyline:".bold}\n\n#{self.storyline && self.storyline.green.italic || "N/A"}\n"
|
166
|
+
puts "\n----------------#{"Other Details".bold}------------------"
|
167
|
+
puts "\n#{"Countries:".bold} #{self.countries_of_origin && self.countries_of_origin.green.italic || "N/A"}"
|
168
|
+
puts "#{"Languages:".bold} #{self.languages && self.languages.green.italic || "N/A"}"
|
169
|
+
puts "#{"IMDB URL:".bold} #{self.url.green.italic}"
|
170
|
+
puts "#{"Website:".bold} #{self.official_site && self.official_site.green.italic || "N/A"}"
|
171
|
+
puts "\nThis movie has a gross revenue of #{self.gross_revenue}".green.bold
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def doc
|
177
|
+
@doc || @doc = WorldTopMovies::Scraper.get_movie_details_page(self.url)
|
178
|
+
end
|
179
|
+
end
|