-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconceptionsongs.py
executable file
·130 lines (103 loc) · 4.76 KB
/
conceptionsongs.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
#!/usr/bin/env python
import argparse
import re
import urllib
import json
import time
import datetime
# Parse the arguments.
parser = argparse.ArgumentParser(description='Given a birthdate, this program estimates a date of conception and returns the Billboard Hot 100 singles list for that date.')
parser.add_argument('date', help='a date in ISO 8601 format, "YYYY-MM-DD"')
parser.add_argument('apikey', help='your Billboard developer API key')
parser.add_argument('-n', '--number', type=int, help='number of songs to return')
parser.add_argument('-f', '--format', choices=['json', 'text'], default='text', help='output format')
group = parser.add_mutually_exclusive_group()
# The Billboard Hot 100 (singles)
group.add_argument('--singles', action='store_const', const=379, default=379, dest='chart')
# The Billboard 200 (albums)
group.add_argument('--albums', action='store_const', const=305, dest='chart')
args = parser.parse_args()
# Validate against ISO 8601.
if re.match(r'^\d{4}-\d{2}-\d{2}$', args.date) is None:
parser.error('Invalid date. Must be formatted "YYYY-MM-DD"')
# Set the year, month, and day.
year, month, day = map(int, args.date.split('-'))
# Set the birth date.
try:
birthDate = datetime.date(year, month, day)
except ValueError as e:
parser.error('Invalid date. {error}'.format(error=e))
# Validate against earliest and latest date available. Feburary 13, 1959 is the
# earliest date available.
if birthDate < datetime.date(1959, 2, 13) or birthDate > datetime.date.today():
parser.error('Invalid date. Must be between 1959-02-13 and today.')
# Set the conception date. Human pregnancy is on average 40 weeks (280 days).
conceptionDate = birthDate - datetime.timedelta(days=280)
# Set start and end dates, 3 days before and after conception date. The
# Billboard Hot 100 came out every week, so a 7 day search is sufficient.
startDate = conceptionDate - datetime.timedelta(days=3)
endDate = conceptionDate + datetime.timedelta(days=3)
# Set paramater defaults.
start = 1
count = 50
conceptionSongs = []
# Iterate the requests.
while True:
# Set the parameters.
params = urllib.urlencode({'format': 'json',
'id': args.chart,
'api_key': args.apikey,
'sdate': startDate,
'edate': endDate,
'start': start,
'count': count})
# Set the URL opener.
opener = urllib.URLopener()
# API does not return JSON unless Accept-Encoding header is set to gzip.
opener.addheader('Accept-Encoding', 'gzip')
# Make the Billboard API request.
try:
response = opener.open('http://api.billboard.com/apisvc/chart/v1/list?{params}'.format(params=params))
except IOError as e:
parser.error(e)
# Decode JSON.
results = json.loads(response.read())
# Iterate the results.
for chartItem in results['searchResults']['chartItem']:
# Some results have no distrubution.
try:
chartItem['distribution']
except KeyError:
chartItem['distribution'] = None
# Build a list containing songs.
conceptionSongs.append([chartItem['rank'],
chartItem['song'],
chartItem['artist'],
chartItem['distribution']])
# Set the new start paramater.
start = start + count
# Must sleep to comply with the 2 calls per second rate limit.
time.sleep(0.5)
# Break out of the request loop when there are no more results.
if (results['searchResults']['firstPosition'] + count) > results['searchResults']['totalRecords']:
break;
# Sort the song list according to rank.
conceptionSongs = sorted(conceptionSongs)
# Slice the list according to the -n argument.
if args.number is not None:
conceptionSongs = conceptionSongs[0:args.number]
# JSON output.
if args.format == 'json':
print json.dumps({'birthDate': birthDate.isoformat(),
'conceptionDate': conceptionDate.isoformat(),
'conceptionSongs': conceptionSongs})
exit()
# Default text output.
else:
print 'Conception date: {conceptionDate}'.format(conceptionDate=conceptionDate.isoformat())
print 'Conception songs:'
for conceptionSong in conceptionSongs:
print '{rank}. "{song}" by {artist} ({distribution})'.format(rank=conceptionSong[0],
song=conceptionSong[1],
artist=conceptionSong[2],
distribution=conceptionSong[3])