• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gcivil-nyu-org / Wednesday-Fall2023-Team-1 / #614980386

13 Dec 2023 12:50AM UTC coverage: 86.973%. First build
#614980386

Pull #257

travis-ci

Pull Request #257: Final sprint

702 of 804 new or added lines in 23 files covered. (87.31%)

1382 of 1589 relevant lines covered (86.97%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.37
/dashboard/views.py
1
from django.shortcuts import render, redirect
1✔
2
import spotipy
1✔
3
import threading
1✔
4

5
# from dotenv import load_dotenv
6
from utils import get_spotify_token, vibe_calc_threads
1✔
7
from django.http import JsonResponse
1✔
8
from dashboard.models import TrackVibe, Track, Artist
1✔
9
from user_profile.models import Vibe, UserTop
1✔
10
from django.utils import timezone
1✔
11
from django.contrib import messages
1✔
12

13
from django.views.decorators.http import require_POST
1✔
14
from django.shortcuts import get_object_or_404
1✔
15

16
from .vibe_calc import calculate_vibe_async
1✔
17

18

19
def index(request):
1✔
20
    token_info = get_spotify_token(request)
1✔
21

22
    if token_info:
1✔
23
        # Initialize Spotipy with stored access token
24
        sp = spotipy.Spotify(auth=token_info["access_token"])
1✔
25

26
        # Pass username to navbar
27
        user_info = sp.current_user()
1✔
28
        username = user_info["display_name"]
1✔
29

30
        user_id = user_info["id"]
1✔
31
        current_time = timezone.now().astimezone(timezone.utc)
1✔
32
        midnight = current_time.replace(hour=0, minute=0, second=0, microsecond=0)
1✔
33
        recent_top = UserTop.objects.filter(user_id=user_id, time__gte=midnight).first()
1✔
34
        if not recent_top:
1✔
35
            # If no top info for this user today, get and save new row to UserTop database
36

37
            # Get top tracks and recommendation based on tracks
38
            top_tracks = sp.current_user_top_tracks(limit=5, time_range="short_term")
1✔
39
            top_track_ids = [track["id"] for track in top_tracks["items"]]
1✔
40

41
            recommendedtracks_ids = []
1✔
42
            if top_track_ids:
1✔
43
                save_track_info(top_tracks["items"], sp.audio_features(top_track_ids))
1✔
44

45
                recommendedtracks = sp.recommendations(
1✔
46
                    seed_tracks=top_track_ids, limit=5
47
                )
48
                recommendedtracks_ids = [
1✔
49
                    track["id"] for track in recommendedtracks["tracks"]
50
                ]
51
                save_track_info(
1✔
52
                    recommendedtracks["tracks"],
53
                    sp.audio_features(recommendedtracks_ids),
54
                )
55

56
            # Get top artists and top genres based on artist
57
            top_artists = sp.current_user_top_artists(limit=5, time_range="short_term")
1✔
58
            top_artists_ids = [artist["id"] for artist in top_artists["items"]]
1✔
59
            save_artist_info(top_artists["items"])
1✔
60
            top_genres = get_genres(top_artists["items"])
1✔
61

62
            top_data = UserTop(
1✔
63
                user_id=user_id,
64
                time=current_time,
65
                top_track=top_track_ids,
66
                top_artist=top_artists_ids,
67
                top_genre=top_genres,
68
                recommended_tracks=recommendedtracks_ids,
69
            )
70
            top_data.save()
1✔
71

72
            # Final data for context
73
            top_tracks = Track.objects.filter(id__in=top_track_ids)
1✔
74
            recommendedtracks = Track.objects.filter(id__in=recommendedtracks_ids)
1✔
75
            top_artists = Artist.objects.filter(id__in=top_artists_ids)
1✔
76

77
        else:
78
            # Already have top data today
NEW
79
            top_tracks = Track.objects.filter(id__in=recent_top.top_track)
×
NEW
80
            recommendedtracks = Track.objects.filter(
×
81
                id__in=recent_top.recommended_tracks
82
            )
NEW
83
            top_artists = Artist.objects.filter(id__in=recent_top.top_artist)
×
NEW
84
            top_genres = recent_top.top_genre
×
85

86
        # Check if user vibe already been calculated for today
87
        recent_vibe = Vibe.objects.filter(
1✔
88
            user_id=user_id, vibe_time__gte=midnight
89
        ).first()
90
        if recent_vibe:
1✔
91
            # Vibe already calculated within today
NEW
92
            vibe_or_not = "already_loaded"
×
93
        else:
94
            user_recent_tracks = sp.current_user_recently_played(limit=10)
1✔
95
            recent_tracks_ids = [
1✔
96
                item["track"]["id"] for item in user_recent_tracks["items"]
97
            ]
98
            recent_tracks_list = [item["track"] for item in user_recent_tracks["items"]]
1✔
99
            if recent_tracks_ids:
1✔
100
                recent_tracks_audio = sp.audio_features(recent_tracks_ids)
1✔
101
                save_track_info(recent_tracks_list, recent_tracks_audio)
1✔
102
                calculate_vibe(user_id, recent_tracks_list, recent_tracks_audio)
1✔
103
                vibe_or_not = "asyn_started"
1✔
104
            else:
105
                # Uf user has 0 recent songs to analyze
NEW
106
                vibe_or_not = "no_songs"
×
107

108
        current_year = current_time.year
1✔
109
        vibe_history = Vibe.objects.filter(
1✔
110
            user_id=user_id, vibe_time__year=current_year
111
        ).values("vibe_time", "user_audio_vibe", "user_lyrics_vibe")
112
        months = [
1✔
113
            {"number": 0, "short_name": "", "long_name": ""},
114
            {"number": 1, "short_name": "J", "long_name": "January"},
115
            {"number": 2, "short_name": "F", "long_name": "February"},
116
            {"number": 3, "short_name": "M", "long_name": "March"},
117
            {"number": 4, "short_name": "A", "long_name": "April"},
118
            {"number": 5, "short_name": "M", "long_name": "May"},
119
            {"number": 6, "short_name": "J", "long_name": "June"},
120
            {"number": 7, "short_name": "J", "long_name": "July"},
121
            {"number": 8, "short_name": "A", "long_name": "August"},
122
            {"number": 9, "short_name": "S", "long_name": "September"},
123
            {"number": 10, "short_name": "O", "long_name": "October"},
124
            {"number": 11, "short_name": "N", "long_name": "November"},
125
            {"number": 12, "short_name": "D", "long_name": "December"},
126
        ]
127

128
        context = {
1✔
129
            "username": username,
130
            "top_tracks": top_tracks,
131
            "top_artists": top_artists,
132
            "top_genres": top_genres,
133
            "recommendedtracks": recommendedtracks,
134
            "vibe_history": vibe_history,
135
            "iteratorMonth": months,
136
            "iteratorDay": range(0, 32),
137
            "currentYear": current_year,
138
            "midnight": midnight,
139
            "vibe_or_not": vibe_or_not,
140
        }
141

142
        return render(request, "dashboard/index.html", context)
1✔
143
    else:
144
        # No token, redirect to login again
145
        debug_info = f"Request: {request}"
×
146
        messages.error(
×
147
            request,
148
            f"Dashboard failed, please try again later. Debug info: {debug_info}",
149
        )
150
        return redirect("login:index")
×
151

152

153
def save_track_info(tracks_list, audio_features_list):
1✔
154
    data_list = []
1✔
155
    processed_ids = set()  # To track processed track IDs
1✔
156

157
    for track, audio_features in zip(tracks_list, audio_features_list):
1✔
158
        track_id = track["id"]
1✔
159

160
        if track_id in processed_ids:
1✔
NEW
161
            continue
×
162

163
        existing = Track.objects.filter(id=track_id).first()
1✔
164

165
        if not existing:
1✔
166
            track_data = Track(
1✔
167
                id=track_id,
168
                name=track["name"],
169
                popularity=track["popularity"],
170
                album_name=track["album"]["name"],
171
                album_release_date=track["album"]["release_date"],
172
                album_images_large=track["album"]["images"][0]["url"]
173
                if len(track["album"]["images"]) >= 1
174
                else None,
175
                album_images_small=track["album"]["images"][2]["url"]
176
                if len(track["album"]["images"]) >= 3
177
                else None,
178
                artist_names=[artist["name"] for artist in track["artists"]],
179
                artist_ids=[artist["id"] for artist in track["artists"]],
180
                acousticness=audio_features["acousticness"],
181
                danceability=audio_features["danceability"],
182
                energy=audio_features["energy"],
183
                instrumentalness=audio_features["instrumentalness"],
184
                loudness=audio_features["loudness"],
185
                speechiness=audio_features["speechiness"],
186
                valence=audio_features["valence"],
187
            )
188
            data_list.append(track_data)
1✔
189

190
        processed_ids.add(track_id)
1✔
191

192
    Track.objects.bulk_create(data_list)
1✔
193

194

195
def save_artist_info(artist_list):
1✔
196
    data_list = []
1✔
197
    processed_ids = set()
1✔
198

199
    for artist in artist_list:
1✔
NEW
200
        artist_id = artist["id"]
×
201

NEW
202
        if artist_id in processed_ids:
×
NEW
203
            continue
×
204

NEW
205
        existing = Artist.objects.filter(id=artist_id).first()
×
206

NEW
207
        if not existing:
×
NEW
208
            artist_info = Artist(
×
209
                id=artist_id,
210
                followers=artist["followers"]["total"],
211
                genres=artist["genres"],
212
                image=artist["images"][0]["url"] if artist["images"] else None,
213
                name=artist["name"],
214
                popularity=artist["popularity"],
215
            )
NEW
216
            data_list.append(artist_info)
×
217

NEW
218
        processed_ids.add(artist_id)
×
219

220
    Artist.objects.bulk_create(data_list)
1✔
221

222

223
def get_genres(top_artists):
1✔
224
    user_top_genres = set()  # Set to store unique genres
1✔
225

226
    for artist in top_artists:
1✔
NEW
227
        user_top_genres.update(artist["genres"])
×
228

229
    return list(user_top_genres)
1✔
230

231

232
def calculate_vibe(user_id, recent_tracks_list, recent_tracks_audio):
1✔
233
    track_names = []
1✔
234
    track_artists = []
1✔
235
    track_ids = []
1✔
236

237
    for track in recent_tracks_list:
1✔
238
        track_names.append(track["name"])
1✔
239
        track_artists.append(track["artists"][0]["name"])
1✔
240
        track_ids.append(track["id"])
1✔
241

242
    # Schedule asynchronous vibe calculation
243
    # But first check if a thread is already running and calculating!
244
    if user_id not in vibe_calc_threads:
1✔
245
        vibe_thread = threading.Thread(
1✔
246
            target=calculate_vibe_async,
247
            args=(
248
                track_names,
249
                track_artists,
250
                track_ids,
251
                recent_tracks_audio,
252
                user_id,
253
            ),
254
        )
255
        vibe_thread.start()
1✔
256
        vibe_calc_threads[user_id] = vibe_thread
1✔
257

258

259
def logout(request):
1✔
260
    # Clear Django session data
261
    request.session.clear()
×
262
    return redirect("login:index")
×
263

264

265
"""
1✔
266
# On huggingface spaces
267
def get_vector(word, model):
268
    # Get the word vector from the model.
269
    try:
270
        return model.wv[word]
271
    except KeyError:
272
        return np.zeros(model.vector_size)
273
"""
274

275

276
def get_task_status(request, midnight):
1✔
277
    if request.user.is_authenticated:
1✔
278
        user = request.user
1✔
279
        user_id = user.user_id
1✔
280

281
        # Check if there is a result in the database
282
        recent_vibe = Vibe.objects.filter(
1✔
283
            user_id=user_id, vibe_time__gte=midnight
284
        ).first()
285

286
        if recent_vibe and recent_vibe.user_audio_vibe:
1✔
287
            vibe_result = recent_vibe.user_audio_vibe
×
288
            if recent_vibe.user_lyrics_vibe:
×
289
                vibe_result += " " + recent_vibe.user_lyrics_vibe
×
290
            description = recent_vibe.description
×
NEW
291
            recent_tracks = get_recent_tracks(recent_vibe.recent_track)
×
292
            response_data = {
×
293
                "status": "SUCCESS",
294
                "result": vibe_result,
295
                "description": description,
296
                "recent_tracks": recent_tracks,
297
            }
298
            return JsonResponse(response_data)
×
299
        else:
300
            return JsonResponse({"status": "PENDING"})
1✔
301

302
    else:
303
        # No token, redirect to login again
NEW
304
        messages.error(
×
305
            request,
306
            "Get_task_status failed, please try again later.",
307
        )
308
        return redirect("login:index")
×
309

310

311
def get_recent_tracks(track_ids):
1✔
312
    # In theory, all tracks in track_ids should exist in database already!
313
    existing_tracks = TrackVibe.objects.filter(track_id__in=track_ids)
1✔
314

315
    final_tracks = []
1✔
316

317
    for track in existing_tracks:
1✔
318
        try:
1✔
319
            track_row = Track.objects.get(id=track.track_id)
1✔
320

NEW
321
        except Track.DoesNotExist:
×
NEW
322
            continue
×
323

324
        final_tracks.append(
1✔
325
            {
326
                "name": track_row.name,
327
                "id": track_row.id,
328
                "year": track_row.album_release_date[:4],
329
                "artists": ", ".join(track_row.artist_names),
330
                "album": track_row.album_name,
331
                "large_album_cover": track_row.album_images_large,
332
                "attributes": {
333
                    "Acousticness": track_row.acousticness * 100,
334
                    "Danceability": track_row.danceability * 100,
335
                    "Energy": track_row.energy * 100,
336
                    "Instrumentalness": track_row.instrumentalness * 100,
337
                    "Valence": track_row.valence * 100,
338
                    "Loudness": (min(60, track_row.loudness * -1) / 60) * 100,
339
                },
340
                "audio_vibe": track.track_audio_vibe.capitalize(),
341
                "lyrics_vibe": track.track_lyrics_vibe
342
                if track.track_lyrics_vibe
343
                else "",
344
            }
345
        )
346

347
    return final_tracks
1✔
348

349

350
@require_POST
1✔
351
def upvote_track(request, track_id):
1✔
352
    track = get_object_or_404(TrackVibe, pk=track_id)
1✔
353
    track.upvote_count += 1
1✔
354
    track.save()
1✔
355
    return JsonResponse({"upvote_count": track.upvote_count})
1✔
356

357

358
@require_POST
1✔
359
def cancel_upvote_track(request, track_id):
1✔
360
    track = get_object_or_404(TrackVibe, pk=track_id)
1✔
361
    if track.upvote_count > 0:
1✔
362
        track.upvote_count -= 1
1✔
363
        track.save()
1✔
364
    return JsonResponse({"upvote_count": track.upvote_count})
1✔
365

366

367
@require_POST
1✔
368
def downvote_track(request, track_id):
1✔
369
    track = get_object_or_404(TrackVibe, pk=track_id)
1✔
370
    track.downvote_count += 1
1✔
371
    track.save()
1✔
372
    return JsonResponse({"downvote_count": track.downvote_count})
1✔
373

374

375
@require_POST
1✔
376
def cancel_downvote_track(request, track_id):
1✔
377
    track = get_object_or_404(TrackVibe, pk=track_id)
1✔
378
    if track.downvote_count >= 0:
1✔
379
        track.downvote_count -= 1
1✔
380
        track.save()
1✔
381
    return JsonResponse({"downvote_count": track.downvote_count})
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc