-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
419 lines (360 loc) · 24 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
import csv
from graph import Graph
from minheap import minheap
from scores import scores
from song import song
from time import perf_counter
with open("wakeup.txt", 'r') as file: ## Prints out wakeup ascii
print(file.read())
def keyChecker(dict, string): ## Checks if a given key is in a dictionary
if string in dict.keys(): ## If the string is a key
return True ## Return True
else:
return False ## Return False
def idToName(id, songlist): ## Given an id, return its song object
for song in songlist: ## Loop over songs in songlist
if (song.id == id): ## If song id matches given id
return song ## Return song
print("No song found with that ID") ## If no song found, print this
return
def nameSearch(name, songlist): ## Given a name, returns all songs with matching name
found = [] ## Initialize a list of found songs
for song in songlist: ## Loop over songs in songlist
if (song.name == name): ## If the song name matches the given name, append to the list
found.append(song)
return found ## Return list of found songs
def getSongScore(dictionary, songID): ## Given a song's ID, return the value in the dictionary
for key in dictionary.keys(): ## Loops over the number of keys in the dictionary
if key == songID: ## If the key matches the given song ID return it's value
return dictionary[key]
def getSongScore1(s): ## Getter for a song's score 1
return s.score1
def getSongScore2(s): ## Getter for a song's score 2
return s.score2
def getSongScore3(s): ## Getter for a song's score 3
return s.score3
songlist = [] ## Declares a list for songs
with open('data.csv', 'r', encoding = 'utf-8', errors = 'ignore') as csvfile: ## Opens CSV File
reader = csv.reader(csvfile, delimiter=',') ## Creates a reader for the CSV File
counter = 0 ## Counter to exclude the header
for row in reader: ## Loop over all rows in CSV File
if counter > 0: ## If not the first row, put needed column information into the song object and append to song list
songlist.append(song(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[8], row[9], row[10], row[11],
row[12], row[14], row[16], row[17], row[18]))
counter += 1 ## Increment the row counter
else: ## If the first row
counter +=1 ## Increment the row counter
scorelist = [] ## Declare a list for scores
score1List = [] ## Declare a list for score 1s
score2List = [] ## Declare a list for score 2s
score3List = [] ## Declare a list for score 3s
dictionary = {} ## Create a dictionary with songID, song object key-value pairs
x = 0
size = len(songlist)
for song in songlist: ## Loops over number of songs in songlist
song.score1 = s1 = (float(song.acousticness)+float(song.liveness))/2 ## Score 1 is the average of acousticness and liveness
song.score2 = s2 = (float(song.valence) + float(song.danceability))/2 ## Score 2 is the average of valence and danceability
song.score3 = s3 = (float(song.energy) + (float(song.tempo)/244))/2 ## Score 3 is the average of energy and adjusted tempo
scorelist.append(scores(s1, s2, s3)) ## Create a list of score objects containing all scores
score1List.append(song) ## Append score 1 to score 1 list
score2List.append(song) ## Append score 2 to score 2 list
score3List.append(song) ## Append score 3 to score 3 list
dictionary[song.id] = [song] ## Pairs song id to song object in dictionary
x = x + 1 ## Increments x
score1List.sort(key=getSongScore1) ## Sorts dictionary
score2List.sort(key=getSongScore2) ## Sorts dictionary
score3List.sort(key=getSongScore3) ## Sorts dictionary
menu = True ## Bool to keep menu running until exit
while menu: ## While not exiting, loop menu
print( ## Menu Print
"\n---------- MENU ----------\n"
"1.Generate Min-Heap Playlist\n"
"2.Generate Graph Playlist\n"
"3.Generate Combined Playlist\n"
"4.Search for Song ID\n"
"5.Search Songs by Artist\n"
"6.Exit\n"
)
menu = input("What would you like to do? ")
if menu =="1": ## If our input is 1, build a min-heap playlist
print("You have selected: Min-Heap Playlist")
choice = input("Please enter a song: ") ## Input for song selection
possible = [] ## List for all possible artists
possible2 = [] ## List for all possible song ids
possibleSong = [] ## List for all possible song objects
targetSong = choice ## Target song is our choice
valid = False ## Initializes validity check as false
for song in songlist: ## Loop over songs in songlist
if song.name == choice: ## If the song name matches our choice
possible.append(song.artists) ## Append that artist to the list of possible artist
possible2.append(song.id) ## Append that id to the list of possible ids
possibleSong.append(song) ## Append that song to the list of possible songs
if len(possible) == 0: ## If no possible songs found
print("Song not found, please try another song!")
valid = False ## Validity check is still false
elif len(possible) == 1: ## If only one possible song was found
targetSong = possibleSong[0] ## This must be our target song
print("\nNow generating your recommended playlist. . .")
valid = True ## Validity check is true
elif len(possible) > 1: ## If multiple possible songs were found
valid = True ## Validity check is true
print("Select the artist you are looking for: ")
x = 1 ## Initializes x as 1
for artists in possible: ## Loops over artists in list of potential artists
print(str(x) + ': ' + artists) ## Prints out option choice
x = x + 1 ## Increments x
pick = input("Enter choice as a numerical answer ex '1': ") ## Prompts input for choosing which artist
targetSong = possibleSong[int(pick)-1] ## targetSong is the song by selected artist
print("\nNow generating your recommended playlist. . .")
if valid: ## If validity check is true
tstart = perf_counter()
adjustedSongList = [] ## Make a playlist of song objects with adjusted scores
for song in songlist: ## Loop over songs in songlist
song2 = song ## Copy songs in songlist to a new song object
adjustedSongList.append(song2) ## Add the new song object to a cloned, adjusted list
targetScore1 = targetSong.score1 ## Uses our targetSong's score 1 as the ideal score 1
targetScore2 = targetSong.score2 ## Uses our targetSong's score 2 as the ideal score 2
targetScore3 = targetSong.score3 ## Uses our targetSong's score 3 as the ideal score 3
for song in adjustedSongList: ## Loops over songs in adjustedSongList
song.score1 = (1 - abs(targetScore1 - song.score1)) ## adjustedSong's score 1 is assigned
song.score2 = (1 - abs(targetScore2 - song.score2)) ## adjustedSong's score 2 is assigned
song.score3 = (1 - abs(targetScore3 - song.score3)) ## adjustedSong's score 3 is assigned
heap1 = minheap(len(adjustedSongList)) ## Create a minheap for score 1 with the size of the adjustedSongList
heap2 = minheap(len(adjustedSongList)) ## Create a minheap for score 2 with the size of the adjustedSongList
heap3 = minheap(len(adjustedSongList)) ## Create a minheap for score 3 with the size of the adjustedSongList
for song in adjustedSongList: ## For songs in adjustedSongList
heap1.insert(song.score1, song) ## Insert song score 1 and song object into heap
heap2.insert(song.score2, song) ## Insert song score 2 and song object into heap
heap3.insert(song.score3, song) ## Insert song score 3 and song object into heap
resulting1 = [] ## Create a list for the song similarity using score 1
resulting2 = [] ## Create a list for the song similarity using score 2
resulting3 = [] ## Create a list for the song similarity using score 3
counter = len(adjustedSongList) ## Counter is initially the size of the playlist
while counter > 0: ## Counter decrements until there are none left
resulting1.append(heap1.delete()) ## Delete top of min-heap and return the song object that was deleted to score 1 list
resulting2.append(heap2.delete()) ## Delete top of min-heap and return the song object that was deleted to score 2 list
resulting3.append(heap3.delete()) ## Delete top of min-heap and return the song object that was deleted to score 3 list
counter -= 1 ## Decrements counter
resulting1.reverse() ## Reverse list 1 to get in correct priority
resulting2.reverse() ## Reverse list 2 to get in correct priority
resulting3.reverse() ## Reverse list 3 to get in correct priority
joinedResulting = resulting1 + resulting2 + resulting3 ## Join all three lists together
occurences = {} ## Create a dictionary for occurences
finalPlaylist = [] ## Create a list for the final playlist
for song in joinedResulting: ## Loop over songs in the combined lists
if not song.id in occurences.keys(): ## If this song id isn't in a dictionary yet
occurences[song.id] = 1 ## Initialize it's occurence count as 1
else: ## If this song id has been added to dictionary already
occurences[song.id] += 1 ## Increment it's occurence
if len(finalPlaylist) < 15: ## If our total playlist is still less than 15 songs
if occurences[song.id] == 2: ## If this is the second time that we've found this song in our list
finalPlaylist.append(song) ## Add this song to the final playlist
tstop = perf_counter()
print("\nPrinting Final Playlist: ")
print("---------------------------")
for song in finalPlaylist: ## Loop over the songs in our final playlist
print(song.name + " by " + song.artists) ## Print out the songs in our final playlist
elapsed = tstop-tstart
print("\nPlaylist generated in: " + str(elapsed) + " seconds")
elif menu =="2": ## If our input is 2, build a graph playlist
print("You have selected: Graph Playlist")
choice = input("Please enter a song: ")## Input for song selection
possible = []## List for all possible artists
possible2 = []## List for all possible song ids
possibleSong = []## List for all possible song objects
targetSong = choice## Target song is our choice
valid = False## Initializes validity check as false
for song in songlist: ## Loop over songs in songlist
if song.name == choice: ## If the song name matches our choice
possible.append(song.artists) ## Append that artist to the list of possible artist
possible2.append(song.id) ## Append that id to the list of possible ids
possibleSong.append(song) ## Append that song to the list of possible songs
if len(possible) == 0: ## If no possible songs found
print("Song not found, please try another song!")
valid = False ## Validity check is still false
elif len(possible) == 1: ## If only one possible song was found
targetSong = possibleSong[0] ## This must be our target song
print("\nNow generating your recommended playlist. . .")
valid = True ## Validity check is true
elif len(possible) > 1: ## If multiple possible songs were found
valid = True ## Validity check is true
print("Select the artist you are looking for: ")
x = 1 ## Initializes x as 1
for artists in possible: ## Loops over artists in list of potential artists
print(str(x) + ': ' + artists) ## Prints out option choice
x = x + 1 ## Increments x
pick = input("Enter choice as a numerical answer ex '1': ") ## Prompts input for choosing which artist
targetSong = possibleSong[int(pick) - 1] ## targetSong is the song by selected artist
print("\nNow generating your recommended playlist. . .")
if valid:
tstart = perf_counter()
graph = Graph()
x = 0
firstScore1 = score1List[x]
firstScore2 = score2List[x]
firstScore3 = score3List[x]
#creates graph, each node with 6 connections, nodes are doubly connected between each other
while x < len(songlist) - 1:
secondScore1 = score1List[x + 1]
graph.insertEdgeType(secondScore1, firstScore1, 0)
graph.insertEdgeType(firstScore1, secondScore1, 1)
firstScore1 = secondScore1
secondScore2 = score2List[x + 1]
graph.insertEdgeType(secondScore2, firstScore2, 2)
graph.insertEdgeType(firstScore2, secondScore2, 3)
firstScore2 = secondScore2
secondScore3 = score3List[x + 1]
graph.insertEdgeType(secondScore3, firstScore3, 4)
graph.insertEdgeType(firstScore3, secondScore3, 5)
firstScore3 = secondScore3
x = x + 1
output = graph.bfs(targetSong, 15) #bfs the graph to retrieve output of 15 songs
tstop = perf_counter()
for resultingSong in output:
print(resultingSong.name + " by " + resultingSong.artists)
elapsed = tstop - tstart
print("\nPlaylist generated in: " + str(elapsed) + " seconds")
elif menu == "3": ## If our input is 3, generate a combined playlist
print("You have selected: Combined Playlist")
choice = input("Please enter a song: ") ## Input for song selection
possible = [] ## List for all possible artists
possible2 = [] ## List for all possible song ids
possibleSong = [] ## List for all possible song objects
targetSong = choice ## Target song is our choice
valid = False ## Initializes validity check as false
for song in songlist: ## Loop over songs in songlist
if song.name == choice: ## If the song name matches our choice
possible.append(song.artists) ## Append that artist to the list of possible artist
possible2.append(song.id) ## Append that id to the list of possible ids
possibleSong.append(song) ## Append that song to the list of possible songs
if len(possible) == 0: ## If no possible songs found
print("Song not found, please try another song!")
valid = False ## Validity check is still false
elif len(possible) == 1: ## If only one possible song was found
targetSong = possibleSong[0] ## This must be our target song
print("\nNow generating your recommended playlist. . .")
valid = True ## Validity check is true
elif len(possible) > 1: ## If multiple possible songs were found
valid = True ## Validity check is true
print("Select the artist you are looking for: ")
x = 1 ## Initializes x as 1
for artists in possible: ## Loops over artists in list of potential artists
print(str(x) + ': ' + artists) ## Prints out option choice
x = x + 1 ## Increments x
pick = input("Enter choice as a numerical answer ex '1': ") ## Prompts input for choosing which artist
targetSong = possibleSong[int(pick) - 1] ## targetSong is the song by selected artist
print("\nNow generating your recommended playlist. . .")
if valid:
tstart = perf_counter()
## Heap Implementation
adjustedSongList = [] ## Make a playlist of song objects with adjusted scores
for song in songlist: ## Loop over songs in songlist
song2 = song ## Copy songs in songlist to a new song object
adjustedSongList.append(song2) ## Add the new song object to a cloned, adjusted list
targetScore1 = targetSong.score1 ## Uses our targetSong's score 1 as the ideal score 1
targetScore2 = targetSong.score2 ## Uses our targetSong's score 2 as the ideal score 2
targetScore3 = targetSong.score3 ## Uses our targetSong's score 3 as the ideal score 3
for song in adjustedSongList: ## Loops over songs in adjustedSongList
song.score1 = (1 - abs(targetScore1 - song.score1)) ## adjustedSong's score 1 is assigned
song.score2 = (1 - abs(targetScore2 - song.score2)) ## adjustedSong's score 2 is assigned
song.score3 = (1 - abs(targetScore3 - song.score3)) ## adjustedSong's score 3 is assigned
heap1 = minheap(len(adjustedSongList)) ## Create a minheap for score 1 with the size of the adjustedSongList
heap2 = minheap(len(adjustedSongList)) ## Create a minheap for score 2 with the size of the adjustedSongList
heap3 = minheap(len(adjustedSongList)) ## Create a minheap for score 3 with the size of the adjustedSongList
for song in adjustedSongList: ## For songs in adjustedSongList
heap1.insert(song.score1, song) ## Insert song score 1 and song object into heap
heap2.insert(song.score2, song) ## Insert song score 2 and song object into heap
heap3.insert(song.score3, song) ## Insert song score 3 and song object into heap
resulting1 = [] ## Create a list for the song similarity using score 1
resulting2 = [] ## Create a list for the song similarity using score 2
resulting3 = [] ## Create a list for the song similarity using score 3
counter = len(adjustedSongList) ## Counter is initially the size of the playlist
while counter > 0: ## Counter decrements until there are none left
resulting1.append(heap1.delete()) ## Delete top of min-heap and return the song object that was deleted to score 1 list
resulting2.append(heap2.delete()) ## Delete top of min-heap and return the song object that was deleted to score 2 list
resulting3.append(heap3.delete()) ## Delete top of min-heap and return the song object that was deleted to score 3 list
counter -= 1 ## Decrements counter
resulting1.reverse() ## Reverse list 1 to get in correct priority
resulting2.reverse() ## Reverse list 2 to get in correct priority
resulting3.reverse() ## Reverse list 3 to get in correct priority
joinedResulting = resulting1 + resulting2 + resulting3 ## Join all three lists together
occurences = {} ## Create a dictionary for occurences
finalPlaylist = [] ## Create a list for the final playlist
for song in joinedResulting: ## Loop over songs in the combined lists
if not song.id in occurences.keys(): ## If this song id isn't in a dictionary yet
occurences[song.id] = 1 ## Initialize it's occurence count as 1
else: ## If this song id has been added to dictionary already
occurences[song.id] += 1 ## Increment it's occurence
if len(finalPlaylist) < 15: ## If our total playlist is still less than 15 songs
if occurences[song.id] == 2: ## If this is the second time that we've found this song in our list
finalPlaylist.append(song) ## Add this song to the final playlist
## Graph Implementation
graph = Graph()
x = 0
firstScore1 = score1List[x]
firstScore2 = score2List[x]
firstScore3 = score3List[x]
# creates graph, each node with 6 connections, nodes are doubly connected between each other
while x < len(songlist) - 1:
secondScore1 = score1List[x + 1]
graph.insertEdgeType(secondScore1, firstScore1, 0)
graph.insertEdgeType(firstScore1, secondScore1, 1)
firstScore1 = secondScore1
secondScore2 = score2List[x + 1]
graph.insertEdgeType(secondScore2, firstScore2, 2)
graph.insertEdgeType(firstScore2, secondScore2, 3)
firstScore2 = secondScore2
secondScore3 = score3List[x + 1]
graph.insertEdgeType(secondScore3, firstScore3, 4)
graph.insertEdgeType(firstScore3, secondScore3, 5)
firstScore3 = secondScore3
x = x + 1
output = graph.bfs(targetSong, 15)
tstop = perf_counter()
print("\nPrinting Final Playlist: ")
print("---------------------------")
for song in finalPlaylist: ## Loop over the songs in our final playlist
print(song.name + " by " + song.artists) ## Print out the songs in our final playlist
for resultingSong in output:
print(resultingSong.name + " by " + resultingSong.artists)
elapsed = tstop - tstart
print("\nPlaylist generated in: " + str(elapsed) + " seconds")
elif menu =="4": ## If our input is 4, search for a song ID
choice = input("Please enter a song title: ")
possible = [] ## Creates a list of possible song artists
possible2 = [] ## Creates a list of possible song ids
for song in songlist: ## Loops over songs in songlist
if song.name == choice: ## If the name of the song matches our input
possible.append(song.artists) ## Append the artist to our possible artists list
possible2.append(song.id) ## Append the id to our possible id list
if len(possible) == 0: ## If there were no possible songs found
print("Song not found, please try another song!")
elif len(possible) == 1: ## If only one possible song was found
print(choice + " by " + possible[0] + '\n' + "has a songID of: " + possible2[0])
elif len(possible) > 1: ## If multiple possible songs were found
print("Select the artist you are looking for: ")
x = 1 ## Initialize x as 1
for artists in possible: ## Loop over the artists in possible artists list
print(str(x) + ': ' + artists) ## Prints out option choice
x = x + 1 ## Increment x
pick = input("Enter choice as a numerical answer ex '1' : ") ## Prompts input for choosing which artist
print('\n' + choice + " by " + possible[int(pick)-1] + '\n' + "has a songID of: " + possible2[int(pick)-1]) ## Prints song by selected artist
elif menu =="5": ## If our input is 5, search for songs by artist
artlist = [] ## Create list for songs by artist
art = input("Input your favorite artist: ") ## Input for artist choice
art = "['" + art + "']" ## Formats search term for artist value
for song in songlist: ## Loop over songs in songlist
if song.artists == art: ## If the giver artist name matches song's artist
artlist.append(song.name) ## Add song to list by artist
if len(artlist) > 0: ## If songs by artist were found
print("\nShowing results for " + art + " : ")
print("---------------------------")
for songs in artlist: ## Loop over songs in artlist
print(songs) ## Print songs by artist
else: ## If no songs by artist were found, print message
print("\nNo artists found by that name!")
elif menu =="6": ## If our input is 6, exit the program
print("\nGoodbye!")
menu = None ## Quit menu
else: ## If our input is invalid, print error message
print("\nInvalid entry, please try again!")