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.

161 lines
5.1 KiB

  1. from django.shortcuts import render, redirect
  2. from django.http import HttpResponse, HttpResponseBadRequest
  3. import math
  4. import random
  5. import requests
  6. import os
  7. import urllib
  8. import json
  9. import pprint
  10. from datetime import datetime
  11. from .utils import parse_library, process_library_stats
  12. TIME_FORMAT = '%Y-%m-%d-%H-%M-%S'
  13. library_stats = {"audio_features":{}, "genres":{}, "year_released":{}, "artists":{}, "num_songs":0, "popularity":[], "total_runtime":0}
  14. # generate_random_string {{{ #
  15. def generate_random_string(length):
  16. """Generates a random string of a certain length
  17. Args:
  18. length: the desired length of the randomized string
  19. Returns:
  20. A random string
  21. """
  22. rand_str = ""
  23. possible_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  24. for _ in range(length):
  25. rand_str += possible_chars[random.randint(0, len(possible_chars) - 1)]
  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. def login(request):
  45. # use a randomly generated state string to prevent cross-site request forgery attacks
  46. state_str = generate_random_string(16)
  47. request.session['state_string'] = state_str
  48. payload = {
  49. 'client_id': os.environ['SPOTIFY_CLIENT_ID'],
  50. 'response_type': 'code',
  51. 'redirect_uri': 'http://localhost:8000/callback',
  52. 'state': state_str,
  53. 'scope': 'user-library-read',
  54. 'show_dialog': False
  55. }
  56. params = urllib.parse.urlencode(payload) # turn the payload dict into a query string
  57. authorize_url = "https://accounts.spotify.com/authorize/?{}".format(params)
  58. return redirect(authorize_url)
  59. # }}} login #
  60. # callback {{{ #
  61. def callback(request):
  62. # Attempt to retrieve the authorization code from the query string
  63. try:
  64. code = request.GET['code']
  65. except KeyError:
  66. return HttpResponseBadRequest("<h1>Problem with login</h1>")
  67. payload = {
  68. 'grant_type': 'authorization_code',
  69. 'code': code,
  70. 'redirect_uri': 'http://localhost:8000/callback',
  71. 'client_id': os.environ['SPOTIFY_CLIENT_ID'],
  72. 'client_secret': os.environ['SPOTIFY_CLIENT_SECRET'],
  73. }
  74. response = requests.post('https://accounts.spotify.com/api/token', data = payload).json()
  75. # despite its name, datetime.today() returns a datetime object, not a date object
  76. # use datetime.strptime() to get a datetime object from a string
  77. request.session['token_obtained_at'] = datetime.strftime(datetime.today(), TIME_FORMAT)
  78. request.session['access_token'] = response['access_token']
  79. request.session['refresh_token'] = response['refresh_token']
  80. request.session['valid_for'] = response['expires_in']
  81. # print(response)
  82. return redirect('user_data')
  83. # }}} callback #
  84. # user_data {{{ #
  85. def user_data(request):
  86. token_obtained_at = datetime.strptime(request.session['token_obtained_at'], TIME_FORMAT)
  87. valid_for = int(request.session['valid_for'])
  88. if token_expired(token_obtained_at, valid_for):
  89. req_body = {
  90. 'grant_type': 'refresh_token',
  91. 'refresh_token': request.session['refresh_token'],
  92. 'client_id': os.environ['SPOTIFY_CLIENT_ID'],
  93. 'client_secret': os.environ['SPOTIFY_CLIENT_SECRET']
  94. }
  95. refresh_token_response = requests.post('https://accounts.spotify.com/api/token', data = req_body).json()
  96. request.session['access_token'] = refresh_token_response['access_token']
  97. request.session['valid_for'] = refresh_token_response['expires_in']
  98. auth_token_str = "Bearer " + request.session['access_token']
  99. headers = {
  100. 'Authorization': auth_token_str
  101. }
  102. user_data_response = requests.get('https://api.spotify.com/v1/me', headers = headers).json()
  103. context = {
  104. 'user_name': user_data_response['display_name'],
  105. 'id': user_data_response['id'],
  106. }
  107. tracks_to_query = 5
  108. library_stats = {
  109. "audio_features":{},
  110. "genres":{},
  111. "year_released":{},
  112. "artists":{},
  113. "num_songs": 0,
  114. "popularity": {
  115. "average": 0,
  116. "std_dev": 0,
  117. },
  118. "total_runtime": 0
  119. }
  120. parse_library(headers, tracks_to_query, library_stats)
  121. processed_library_stats = process_library_stats(library_stats)
  122. print("================================================")
  123. print("Processed data follows\n")
  124. pprint.pprint(processed_library_stats)
  125. return render(request, 'spotifyvis/user_data.html', context)
  126. # }}} user_data #