spotify_rec 1.0 → 1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '00749e0a49528b3d680597ee48924685869ab8963027609a951f0d8e1bd6bfd6'
4
- data.tar.gz: ead9a411308f5234a5b4d1919201e0721f1191304a3e486e5f75e644db23fe1b
3
+ metadata.gz: 6e297d8af48b8d8d7ab6c0636518af40d7051e794ab637c725d544ecfa68679e
4
+ data.tar.gz: 2297ec4f7c3cfe5372eb30b6b5df7cd5fcf269e56857f9a014f9d9b378a6a8c6
5
5
  SHA512:
6
- metadata.gz: 88011906853f7d6e5e6e4fe650c5d474f0caa8534ee8ee590e7bd5178b48bf773a4b56c9d12f9fee97db0ce8affb8d7860a783330290ef4024c68e91a0aa43c7
7
- data.tar.gz: cd0e528637accbbd6c8a15b6a6de7d58e8b21e04cabe9c73787c9fb8885db2056b27075a6fefd199be3a40924f050f53aec97be5e2bbe1d58d32684700d7dd39
6
+ metadata.gz: c84dcb222e6107e57ac132796adfc3e05c6562063cfbb2f94cba521c669d095b359f49f0c1653b6395a89bd9c24bc132861b6b09fe6a454d2680a56b6fda0b92
7
+ data.tar.gz: 88a353cd4e84c0c465e4406bc899acbabe80cde04aee16ac123d019c258cb9508690ac981956b138f7f6bca2a31850c23b709bf67c80323d936cdccf8bc130a9
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # gems
4
5
  require 'rspotify'
@@ -15,40 +16,46 @@ require_relative '../lib/login'
15
16
  require_relative '../lib/rec'
16
17
  require_relative '../lib/menu'
17
18
  require_relative '../lib/my_list'
19
+ require_relative '../lib/tutorial'
20
+ require_relative '../lib/error'
18
21
 
19
- #modules
22
+ # modules
20
23
  include Login
24
+ include Tutorial
21
25
 
22
- RSpotify.authenticate("712ff89a218a4e6dbe1f169e06f949b9", "e9e0517f405b4a01a1be8823126459b7")
23
- $prompt = TTY::Prompt.new
26
+ RSpotify.authenticate('712ff89a218a4e6dbe1f169e06f949b9', 'e9e0517f405b4a01a1be8823126459b7')
24
27
 
25
- VERSION = 1.0
28
+ VERSION = 1.5
26
29
 
27
30
  ARGV << '--run' if ARGV.empty?
28
31
 
29
32
  options = {}
30
33
  parser = OptionParser.new do |opts|
31
- opts.banner = "Welcome to Spotify Recommendations! Usage: spotifyrec [options]".colorize(:light_green)
32
-
33
- opts.on("-v", "--version", "Display the version") do
34
- puts "Spotify Recommendations version #{VERSION}".colorize(:light_green)
35
- end
36
-
37
- opts.on("-h", "--help", "Display the help message") do
38
- puts opts
39
- exit
40
- end
41
-
42
- opts.on("-qGENRE", "--quick=GENRE", "Generate a quick recommendation with the chosen genre") do |genre|
43
- recommendations = RSpotify::Recommendations.generate(limit: 5, seed_genres: [genre])
44
- cleaned_recs = recommendations.tracks.map { |t| "#{t.name} by #{t.artists[0].name}" }
45
- puts "》 RECOMMENDATIONS 《".colorize(:light_green)
46
- cleaned_recs.each { |track| puts track }
47
- end
48
-
49
- opts.on("-r", "--run", "Run the application") do
50
- Login::login
51
- end
34
+ opts.banner = 'Welcome to Spotify Recommendations! Usage: spotify_rec [options]'.colorize(:light_green)
35
+
36
+ opts.on('-v', '--version', 'Display the version') do
37
+ puts "Spotify Recommendations version #{VERSION}".colorize(:light_green)
38
+ end
39
+
40
+ opts.on('-h', '--help', 'Display the help message') do
41
+ puts opts
42
+ exit
43
+ end
44
+
45
+ opts.on('-t', '--tutorial', 'View a brief tutorial') do
46
+ Tutorial.start
47
+ end
48
+
49
+ opts.on('-qGENRE', '--quick=GENRE', 'Generate a quick recommendation with the chosen genre') do |genre|
50
+ recommendations = RSpotify::Recommendations.generate(limit: 5, seed_genres: [genre])
51
+ cleaned_recs = recommendations.tracks.map { |t| "#{t.name} by #{t.artists[0].name}" }
52
+ puts '》 RECOMMENDATIONS 《'.colorize(:light_green)
53
+ cleaned_recs.each { |track| puts track }
54
+ end
55
+
56
+ opts.on('-r', '--run', 'Run the application') do
57
+ Login.initial_login
58
+ end
52
59
  end
53
60
 
54
- parser.parse!
61
+ parser.parse!
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RequirementError < StandardError
4
+ end
5
+
6
+ class MyListEmpty < StandardError
7
+ end
8
+
9
+ class UserFileError < StandardError
10
+ end
@@ -1,117 +1,154 @@
1
- require_relative 'menu'
2
-
3
- module Login
4
-
5
- def user
6
- return @user
7
- end
8
-
9
- def userdata
10
- path = File.join(File.dirname(File.dirname(File.absolute_path(__FILE__))))
11
- userfile = "#{path}/public/users.json"
12
- return userfile
13
- end
14
-
15
- def clear
16
- system("clear")
17
- end
18
-
19
- def initial_login
20
- clear
21
- puts " ;;;;;;;;;;;;;;;;;;;
22
- ;;;;;;;;;;;;;;;;;;;
23
- ; ;
24
- ; ;
25
- ; ;
26
- ; ;
27
- ; ;
28
- ; ;
29
- ,;;;;; ,;;;;;
30
- ;;;;;; ;;;;;;
31
- `;;;;' `;;;;'
32
- ".colorize(:light_green)
33
- puts "》 Welcome to the Spotify Recommendations App! 《".colorize(:light_green)
34
- puts
35
- return $prompt.select("Are you a new or returning user? \n", %w(New Returning))
36
- end
37
-
38
- def load_data
39
- file = File.read(userdata)
40
- return JSON.parse(file)
41
- end
42
-
43
- def gen_uid
44
- return load_data.length + 1
45
- end
46
-
47
- def store_user
48
- data = load_data
49
- user_details = {
50
- "id" => "#{@user.uid}",
51
- "username" => @user.username,
52
- "password" => @user.password,
53
- "playlist" => @user.playlist,
54
- "mylist" => @user.mylist
55
- }
56
- data << user_details
57
- File.open(userdata,"w") do |f|
58
- f.puts JSON.pretty_generate(data)
59
- end
60
- puts "You're now registered! Logging you in..".colorize(:light_green)
61
- sleep(1)
62
- @menu = Menu.new(@user)
63
- @menu.menu_router
64
- end
65
-
66
- def new_user
67
- system("clear")
68
- puts "Welcome! Please register for an account to continue.".colorize(:light_green)
69
- username = $prompt.ask("Username >") { |u| u.validate(/\A[0-9a-zA-Z'-]*\z/, "Username must only contain letters and numbers")}
70
- password = $prompt.mask("Password >")
71
- @user = User.new(username, password, gen_uid)
72
- @user_playlist = Playlist.new(@user)
73
- store_user
74
- end
75
-
76
- def returning_user
77
- system("clear")
78
- puts "Welcome back! Please login to continue.".colorize(:light_green)
79
- username = $prompt.ask("Username >")
80
- password= $prompt.mask("Password >")
81
- authenticate(username, password)
82
- end
83
-
84
- def authenticate(username, password)
85
- data = load_data
86
- auth = false
87
- data.each do |hash|
88
- if hash["username"].downcase == username.downcase
89
- if hash["password"] == password
90
- auth = true
91
- @user = User.new(username, password, hash["id"], hash["playlist"], hash["mylist"])
92
- @user_playlist = Playlist.new(@user)
93
- puts "Success! Logging you in..".colorize(:light_green)
94
- sleep(1)
95
- @menu = Menu.new(@user)
96
- @menu.menu_router
97
- end
98
- end
99
- end
100
- unless auth
101
- puts "Incorrect username or password!".colorize(:red)
102
- sleep(1)
103
- returning_user
104
- end
105
- end
106
-
107
- def login
108
- new_returning = initial_login.downcase
109
- if new_returning == "new"
110
- new_user
111
- end
112
- if new_returning == "returning"
113
- returning_user
114
- end
115
- end
116
-
117
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'menu'
4
+
5
+ module Login
6
+ @prompt = TTY::Prompt.new
7
+ @count = 0
8
+
9
+ # Login Logic Section
10
+ # Prompts the user to indicate whether they are a new or returning user
11
+ def initial_login
12
+ clear
13
+ ascii_art
14
+ puts '》 Welcome to the Spotify Recommendations App! 《'.colorize(:light_green)
15
+ puts
16
+ selection = @prompt.select("Are you a new or returning user? \n", %w[New Returning])
17
+ new_user if selection == 'New'
18
+ returning_user if selection == 'Returning'
19
+ end
20
+
21
+ # New user
22
+ # Prompts user to enter username for new account. Checks name for any special characters.
23
+ # Raises error if special character is included, and prompts the user again.
24
+ def new_user
25
+ system('clear')
26
+ File.open(userdata, 'w') { |f| f.write([].to_json) } unless File.exist?(userdata)
27
+ puts 'Welcome! Please register for an account to continue.'.colorize(:light_green)
28
+ username = @prompt.ask('Username >')
29
+ raise RequirementError.new, 'Requirements not met' if username.match?(/[!@#$%^&*(),.?":{}|<>]/)
30
+
31
+ new_user_password(username)
32
+ rescue RequirementError
33
+ puts 'Username cannot contain special characters. Please try again!'.colorize(:light_red)
34
+ new_user
35
+ end
36
+
37
+ # Prompts for new password. Validates password so no special characters are included.
38
+ def new_user_password(username)
39
+ password = @prompt.mask('Password >')
40
+ raise RequirementError.new, 'Requirements not met' if password.match?(/[!@#$%^&*(),.?":{}|<>]/)
41
+
42
+ @user = User.new(username, password, gen_uid)
43
+ @user_playlist = Playlist.new(@user)
44
+ store_user
45
+ rescue RequirementError
46
+ puts 'Password cannot contain special characters. Please try again!'.colorize(:light_red)
47
+ end
48
+
49
+ # Converts user details into a hash, ready to be stored in JSON file
50
+ def store_user
51
+ data = load_data
52
+ user_details = {
53
+ 'id' => @user.uid.to_s,
54
+ 'username' => @user.username,
55
+ 'password' => @user.password,
56
+ 'playlist' => @user.playlist,
57
+ 'mylist' => @user.mylist
58
+ }
59
+ data << user_details
60
+ write_user(data)
61
+ end
62
+
63
+ # Writes the hash of user data to the JSON file
64
+ def write_user(data)
65
+ File.open(userdata, 'w') do |f|
66
+ f.puts JSON.pretty_generate(data)
67
+ end
68
+ puts "You're now registered! Logging you in..".colorize(:light_green)
69
+ sleep(1)
70
+ go_to_menu
71
+ end
72
+
73
+ # Returning user
74
+ # Prompts user to enter username and password
75
+ def returning_user
76
+ system('clear')
77
+ File.open(userdata, 'w') { |f| f.write([].to_json) } unless File.exist?(userdata)
78
+ puts 'Welcome back! Please login to continue.'.colorize(:light_green)
79
+ username = @prompt.ask('Username >')
80
+ user_password = @prompt.mask('Password >')
81
+ authenticate(username, user_password)
82
+ end
83
+
84
+ # Loads data from JSON file
85
+ def authenticate(username, user_password)
86
+ data_arr = load_data
87
+ user_data(data_arr, username, user_password)
88
+ end
89
+
90
+ # Checks whether supplied username and password match any in the userfile
91
+ def user_data(data, username, user_password)
92
+ data.each do |hash|
93
+ next unless hash['username'].downcase == username.downcase
94
+
95
+ next unless hash['password'] == user_password
96
+
97
+ @user = User.new(username, user_password, hash['id'], hash['playlist'], hash['mylist'])
98
+ go_to_menu
99
+ end
100
+ @count += 1
101
+ no_auth
102
+ end
103
+
104
+ # Method exits application if user is unsuccessful 3 times or more. Otherwise returns to login
105
+ def no_auth
106
+ puts 'Incorrect username or password!'.colorize(:red)
107
+ sleep(1)
108
+ if @count >= 3
109
+ puts 'You have tried too many times! The application will now close..'.colorize(:light_red)
110
+ exit
111
+ end
112
+ returning_user
113
+ end
114
+
115
+ # Helper Module Methods
116
+ def user
117
+ @user
118
+ end
119
+
120
+ def gen_uid
121
+ load_data.length + 1
122
+ end
123
+
124
+ def userdata
125
+ path = File.join(File.dirname(File.dirname(File.absolute_path(__FILE__))))
126
+ "#{path}/public/users.json"
127
+ end
128
+
129
+ def load_data
130
+ file = File.read(userdata)
131
+ JSON.parse(file)
132
+ end
133
+
134
+ def clear
135
+ system('clear')
136
+ end
137
+
138
+ def go_to_menu
139
+ menu = Menu.new(@user)
140
+ menu.menu_router
141
+ end
142
+
143
+ def ascii_art
144
+ puts " ;;;;;;;;;;;;;;;;;;;
145
+ ; ;
146
+ ; ;
147
+ ; ;
148
+ ; ;
149
+ ; ;
150
+ ,;;;;; ,;;;;;
151
+ ;;;;;; ;;;;;;
152
+ `;;;;' `;;;;'\n".colorize(:light_green)
153
+ end
154
+ end
@@ -1,67 +1,116 @@
1
- class Menu
2
-
3
- def initialize(user)
4
- @user = user
5
- @prompt = prompt = TTY::Prompt.new
6
- @playlist = Playlist.new(@user)
7
- end
8
-
9
- def display_menu
10
- system("clear")
11
- return @prompt.select("》 MAIN MENU 《\n".colorize(:light_green), (["My List", "Recommendations", "Playlist", "Account Details", "Exit"]))
12
- end
13
-
14
- def my_list
15
- system("clear")
16
- selection = @prompt.select("》 MY LIST 《\n".colorize(:light_green), (["Display", "Add", "Remove", "Back"]))
17
- mylist = MyList.new(@user)
18
- case selection
19
- when "Display"
20
- mylist.list
21
- when "Add"
22
- mylist.add_to_list
23
- when "Remove"
24
- mylist.remove_from_list
25
- when "Back"
26
- menu_router
27
- end
28
- end
29
-
30
- def account_details
31
- system("clear")
32
- selection = @prompt.select("》 ACCOUNT MENU 《 \n".colorize(:light_green), (["View Details", "Change Username", "Change Password", "Delete Account", "Back"]))
33
- case selection
34
- when "View Details"
35
- @user.details
36
- when "Change Username"
37
- username = @prompt.ask("Please enter your new username >") { |u| u.validate(/\A[0-9a-zA-Z'-]*\z/, "Username must only contain letters and numbers")}
38
- @user.change_username(username)
39
- when "Change Password"
40
- password = @prompt.ask("Please enter your new password >")
41
- @user.change_password(password)
42
- when "Delete Account"
43
- @user.delete_account
44
- when "Back"
45
- menu_router
46
- end
47
- end
48
-
49
- def menu_router
50
- selection = display_menu
51
- case selection
52
- when "My List"
53
- system("clear")
54
- my_list
55
- when "Recommendations"
56
- recommendation = Rec.new(@user)
57
- recommendation.amount_of_suggestions
58
- when "Playlist"
59
- @playlist.menu
60
- when "Account Details"
61
- account_details
62
- when "Exit"
63
- exit
64
- end
65
- end
66
-
67
- end
1
+ # frozen_string_literal: true
2
+
3
+ class Menu
4
+ def initialize(user)
5
+ @user = user
6
+ @prompt = TTY::Prompt.new
7
+ @playlist = Playlist.new(@user)
8
+ @list_length = @user.mylist.length
9
+ end
10
+
11
+ # Prompts the user to select an item on the menu
12
+ def display_menu
13
+ system('clear')
14
+ arr = ['My List', 'Recommendations', 'Playlist', 'Account Details', 'Exit']
15
+ @prompt.select("》 MAIN MENU 《\n".colorize(:light_green), arr)
16
+ end
17
+
18
+ # Begins to case the user selection from the menu
19
+ def menu_router
20
+ selection = display_menu
21
+ case selection
22
+ when 'My List'
23
+ my_list
24
+ when 'Recommendations'
25
+ recommendations_menu
26
+ else
27
+ case_menu(selection)
28
+ end
29
+ end
30
+
31
+ # Opens the recommendations menu
32
+ def recommendations_menu
33
+ if @list_length.positive?
34
+ recommendation = Rec.new(@user)
35
+ recommendation.amount_of_suggestions
36
+ else
37
+ no_items
38
+ end
39
+ end
40
+
41
+ # Sends user back to menu if they don't have any items in 'MyList' yet
42
+ def no_items
43
+ puts "Uh oh! You don't have any items in your list yet, so we can't generate any".colorize(:light_red)
44
+ puts 'recommendations. Please add some before doing this!'.colorize(:light_red)
45
+ @prompt.keypress('Press any key to return to the previous menu..')
46
+ display_menu
47
+ end
48
+
49
+ # Continues to case the menu selection and route the user
50
+ def case_menu(selection)
51
+ case selection
52
+ when 'Playlist'
53
+ @playlist.menu
54
+ when 'Account Details'
55
+ account_details
56
+ when 'Exit'
57
+ exit
58
+ end
59
+ end
60
+
61
+ # Shows the MyList menu
62
+ def my_list
63
+ system('clear')
64
+ selection = @prompt.select("》 MY LIST 《\n".colorize(:light_green), %w[Display Add Remove Back])
65
+ mylist = MyList.new(@user)
66
+ case_my_list(selection, mylist)
67
+ end
68
+
69
+ # Cases the selection from the MyList menu and routes to the required method
70
+ def case_my_list(selection, mylist)
71
+ case selection
72
+ when 'Display'
73
+ mylist.list
74
+ when 'Add'
75
+ mylist.add_to_list
76
+ when 'Remove'
77
+ mylist.remove_from_list
78
+ when 'Back'
79
+ menu_router
80
+ end
81
+ end
82
+
83
+ # Prompts the user to select from the Account details menu
84
+ def account_details_select
85
+ system('clear')
86
+ arr = ['View Details', 'Change Username', 'Change Password', 'Delete Account', 'Back']
87
+ @prompt.select('》 ACCOUNT DETAILS 《\n'.colorize(:light_green), arr)
88
+ end
89
+
90
+ # Cases the account details menu and routes the user
91
+ def account_details
92
+ selection = account_details_select
93
+ case selection
94
+ when 'View Details'
95
+ @user.details
96
+ when 'Change Username'
97
+ username = @prompt.ask('Please enter your new username >')
98
+ @user.change_username(username)
99
+ else
100
+ case_account_details(selection)
101
+ end
102
+ end
103
+
104
+ # Continues to case the account details menu and route the user
105
+ def case_account_details(selection)
106
+ case selection
107
+ when 'Change Password'
108
+ password = @prompt.ask('Please enter your new password >')
109
+ @user.change_password(password)
110
+ when 'Delete Account'
111
+ @user.delete_account
112
+ when 'Back'
113
+ menu_router
114
+ end
115
+ end
116
+ end