Graphs and tables for your Spotify account.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
5.4 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. # imports {{{ #
  2. import math
  3. import random
  4. import requests
  5. import os
  6. import urllib
  7. import secrets
  8. import pprint
  9. import string
  10. from datetime import datetime
  11. from django.shortcuts import render, redirect
  12. from django.http import HttpResponseBadRequest
  13. # }}} imports #
  14. TIME_FORMAT = '%Y-%m-%d-%H-%M-%S'
  15. TRACKS_TO_QUERY = 200
  16. # generate_random_string {{{ #
  17. def generate_random_string(length):
  18. """Generates a random string of a certain length
  19. Args:
  20. length: the desired length of the randomized string
  21. Returns:
  22. A random string
  23. """
  24. all_chars = string.ascii_letters + string.digits
  25. rand_str = "".join(random.choice(all_chars) for _ in range(length))
  26. return rand_str
  27. # }}} generate_random_string #
  28. # token_expired {{{ #
  29. def token_expired(token_obtained_at, valid_for):
  30. """Returns True if token expired, False if otherwise
  31. Args:
  32. token_obtained_at: datetime object representing the date and time when the token was obtained
  33. valid_for: the time duration for which the token is valid, in seconds
  34. """
  35. time_elapsed = (datetime.today() - token_obtained_at).total_seconds()
  36. return time_elapsed >= valid_for
  37. # }}} token_expired #
  38. # index {{{ #
  39. # Create your views here.
  40. def index(request):
  41. return render(request, 'spotifyvis/index.html')
  42. # }}} index #
  43. # login {{{ #
  44. # uses Authorization Code flow
  45. def spotify_login(request):
  46. # use a randomly generated state string to prevent cross-site request forgery attacks
  47. state_str = generate_random_string(16)
  48. request.session['state_string'] = state_str
  49. payload = {
  50. 'client_id': os.environ['SPOTIFY_CLIENT_ID'],
  51. 'response_type': 'code',
  52. 'redirect_uri': 'http://localhost:8000/callback',
  53. 'state': state_str,
  54. 'scope': 'user-library-read',
  55. 'show_dialog': False
  56. }
  57. params = urllib.parse.urlencode(payload) # turn the payload dict into a query string
  58. authorize_url = "https://accounts.spotify.com/authorize/?{}".format(params)
  59. return redirect(authorize_url)
  60. # }}} login #
  61. # callback {{{ #
  62. def callback(request):
  63. # Attempt to retrieve the authorization code from the query string
  64. try:
  65. code = request.GET['code']
  66. except KeyError:
  67. return HttpResponseBadRequest("<h1>Problem with login</h1>")
  68. payload = {
  69. 'grant_type': 'authorization_code',
  70. 'code': code,
  71. 'redirect_uri': 'http://localhost:8000/callback',
  72. 'client_id': os.environ['SPOTIFY_CLIENT_ID'],
  73. 'client_secret': os.environ['SPOTIFY_CLIENT_SECRET'],
  74. }
  75. response = requests.post('https://accounts.spotify.com/api/token', data=payload).json()
  76. # despite its name, datetime.today() returns a datetime object, not a date object
  77. # use datetime.strptime() to get a datetime object from a string
  78. request.session['token_obtained_at'] = datetime.strftime(datetime.today(), TIME_FORMAT)
  79. request.session['access_token'] = response['access_token']
  80. request.session['refresh_token'] = response['refresh_token']
  81. request.session['valid_for'] = response['expires_in']
  82. # print(response)
  83. return redirect('user_data')
  84. # }}} callback #
  85. # user_data {{{ #
  86. def user_data(request):
  87. # get user token {{{ #
  88. token_obtained_at = datetime.strptime(request.session['token_obtained_at'], TIME_FORMAT)
  89. valid_for = int(request.session['valid_for'])
  90. if token_expired(token_obtained_at, valid_for):
  91. req_body = {
  92. 'grant_type': 'refresh_token',
  93. 'refresh_token': request.session['refresh_token'],
  94. 'client_id': os.environ['SPOTIFY_CLIENT_ID'],
  95. 'client_secret': os.environ['SPOTIFY_CLIENT_SECRET']
  96. }
  97. refresh_token_response = requests.post('https://accounts.spotify.com/api/token', data=req_body).json()
  98. request.session['access_token'] = refresh_token_response['access_token']
  99. request.session['valid_for'] = refresh_token_response['expires_in']
  100. # }}} get user token #
  101. auth_token_str = "Bearer " + request.session['access_token']
  102. headers = {
  103. 'Authorization': auth_token_str
  104. }
  105. user_data_response = requests.get('https://api.spotify.com/v1/me', headers = headers).json()
  106. # store the user_id so it may be used to create model
  107. request.session['user_id'] = user_data_response['id']
  108. # create user obj {{{ #
  109. try:
  110. user = User.objects.get(user_id=user_data_response['id'])
  111. except User.DoesNotExist:
  112. # Python docs recommends 32 bytes of randomness against brute force attacks
  113. user = User(user_id=user_data_response['id'], user_secret=secrets.token_urlsafe(32))
  114. request.session['user_secret'] = user.user_secret
  115. user.save()
  116. # }}} create user obj #
  117. context = {
  118. 'user_id': user.user_id,
  119. 'user_secret': user.user_secret,
  120. }
  121. # TODO: redirect to API app to parse library or loading page
  122. # parse_library(headers, TRACKS_TO_QUERY, user)
  123. return render(request, 'spotifyvis/logged_in.html', context)
  124. # }}} user_data #
  125. def admin_graphs(request):
  126. """TODO
  127. """
  128. user_id = "polarbier"
  129. # user_id = "chrisshyi13"
  130. user_obj = User.objects.get(user_id=user_id)
  131. context = {
  132. 'user_id': user_id,
  133. 'user_secret': user_obj.user_secret,
  134. }
  135. update_track_genres(user_obj)
  136. return render(request, 'spotifyvis/logged_in.html', context)