Skip to content

Commit f6c56f8

Browse files
authored
Merge branch 'master' into private/aaronrobson/404-on-favicon-exception-raised
2 parents 1499f29 + 05a7d14 commit f6c56f8

13 files changed

+323
-83
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ lib/
1010
.DS_Store
1111
.idea/
1212
apod/__pycache__/
13+
venv/

Dockerfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM python:3-alpine
2+
3+
WORKDIR /usr/src/app
4+
COPY requirements.txt ./
5+
RUN pip install --no-cache-dir -r requirements.txt
6+
COPY . .
7+
EXPOSE 5000
8+
ENTRYPOINT ["python"]
9+
CMD ["application.py"]

README.md

Lines changed: 122 additions & 66 deletions
Large diffs are not rendered by default.
File renamed without changes.

utility.py renamed to apod/utility.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,21 @@ def _get_apod_chars(dt, thumbs):
5656
date_str = dt.strftime('%y%m%d')
5757
apod_url = '%sap%s.html' % (BASE, date_str)
5858
LOG.debug('OPENING URL:' + apod_url)
59-
soup = BeautifulSoup(requests.get(apod_url).text, 'html.parser')
59+
res = requests.get(apod_url)
60+
61+
if res.status_code == 404:
62+
return None
63+
# LOG.error(f'No APOD entry for URL: {apod_url}')
64+
# default_obj_path = 'static/default_apod_object.json'
65+
# LOG.debug(f'Loading default APOD response from {default_obj_path}')
66+
# with open(default_obj_path, 'r') as f:
67+
# default_obj_props = json.load(f)
68+
69+
# default_obj_props['date'] = dt.strftime('%Y-%m-%d')
70+
71+
# return default_obj_props
72+
73+
soup = BeautifulSoup(res.text, 'html.parser')
6074
LOG.debug('getting the data url')
6175
hd_data = None
6276
if soup.img:
@@ -88,7 +102,7 @@ def _get_apod_chars(dt, thumbs):
88102
props['media_type'] = media_type
89103
if data:
90104
props['url'] = _get_last_url(data)
91-
props['date'] = dt.isoformat()
105+
props['date'] = dt.strftime('%Y-%m-%d')
92106

93107
if hd_data:
94108
props['hdurl'] = _get_last_url(hd_data)

apod_parser/apod_object_parser.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import requests
2+
import json
3+
import os
4+
from PIL import Image
5+
6+
def get_data(api_key):
7+
raw_response = requests.get(f'https://api.nasa.gov/planetary/apod?api_key={api_key}').text
8+
response = json.loads(raw_response)
9+
return response
10+
11+
12+
def get_date(response):
13+
date = response['date']
14+
return date
15+
16+
17+
def get_explaination(response):
18+
explaination = response['explanation']
19+
return explaination
20+
21+
22+
def get_hdurl(response):
23+
hdurl = response['hdurl']
24+
return hdurl
25+
26+
27+
def get_media_type(response):
28+
media_type = response['media_type']
29+
return media_type
30+
31+
def get_service_version(response):
32+
service_version = response['service_version']
33+
return service_version
34+
35+
36+
def get_title(response):
37+
service_version = response['title']
38+
return service_version
39+
40+
def get_url(response):
41+
url = response['url']
42+
return url
43+
44+
def download_image(url, date):
45+
if os.path.isfile(f'{date}.png') == False:
46+
raw_image = requests.get(url).content
47+
with open(f'{date}.jpg', 'wb') as file:
48+
file.write(raw_image)
49+
50+
else:
51+
return FileExistsError
52+
53+
54+
def convert_image(image_path):
55+
path_to_image = os.path.normpath(image_path)
56+
57+
basename = os.path.basename(path_to_image)
58+
59+
filename_no_extension = basename.split(".")[0]
60+
61+
base_directory = os.path.dirname(path_to_image)
62+
63+
image = Image.open(path_to_image)
64+
image.save(f"{base_directory}/{filename_no_extension}.png")

apod_parser/apod_parser_readme.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# apod_object_parser
2+
3+
get a Nasa api key by clicking <a href="https://api.nasa.gov/#signUp">here</a>.
4+
5+
## How to use
6+
1. import the file
7+
```python
8+
import apod_object_parser
9+
```
10+
2. Now call the `get_data` function and pass the `nasa api key` as the argument. Note api_key is a string. The response returned will be a Dictionary. Now you can parse the dictionary too
11+
12+
```python
13+
response = apod_object_parser.get_data(##Pass In Your API key here)
14+
```
15+
### get_date
16+
17+
the `get_date` function takes the dictionary we got above and returns the date.
18+
19+
```python
20+
date = apod_object_parser.get_date(response)
21+
```
22+
### get_explaination
23+
the `get_explaination` function takes the dictionary we got above and returns the explaintion.
24+
25+
```python
26+
date = apod_object_parser.get_explaination(response)
27+
```
28+
### get_hdurl
29+
the `get_hdurl` function takes the dictionary we got above and returns the High Definition url of the image.
30+
31+
```python
32+
date = apod_object_parser.get_hdurl(response)
33+
```
34+
### get_title
35+
the `get_title` function takes the dictionary we got above and returns the title of the image.
36+
37+
```python
38+
date = apod_object_parser.get_title(response)
39+
```
40+
### get_url
41+
the `get_url` function takes the dictionary we got above and returns the Standard definition url of the image.
42+
43+
```python
44+
date = apod_object_parser.get_hdurl(response)
45+
```
46+
### get_media_type
47+
the `get_media_type` function takes the dictionary we got above and returns the media type the file (might be a video of a image).
48+
49+
```python
50+
date = apod_object_parser.get_hdurl(response)
51+
```
52+
53+
## Other functions
54+
there are also other functions that might help you in situations
55+
56+
### download_image
57+
the `download_image` finction takes the url (hdurl or url) and the date from the function `get_date` and downloads the image in the current directory and with the file name of the date. the image downloaded is in the .jpg format
58+
```python
59+
apod_object_parser.download_image(url, date)
60+
```
61+
62+
### convert_image
63+
sometimes the image we downloaded above might not be in the right format (.jpg) so you may call `convert_image` function to convert the image into .png. takes the `image_path` parameter which is the filepath.
64+
```python
65+
apod_object_parser.convert_image(image_path)
66+
```

application.py

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
A micro-service passing back enhanced information from Astronomy
33
Picture of the Day (APOD).
4-
4+
55
Adapted from code in https://github.com/nasa/planetary-api
66
Dec 1, 2015 (written by Dan Hammer)
77
@@ -18,10 +18,10 @@
1818
sys.path.insert(1, ".")
1919

2020
from datetime import datetime, date
21-
from random import sample
22-
from flask import request, jsonify, render_template, Flask
21+
from random import shuffle
22+
from flask import request, jsonify, render_template, Flask, current_app
2323
from flask_cors import CORS
24-
from utility import parse_apod, get_concepts
24+
from apod.utility import parse_apod, get_concepts
2525
import logging
2626

2727
#### added by justin for EB
@@ -31,9 +31,10 @@
3131
CORS(application)
3232

3333
LOG = logging.getLogger(__name__)
34+
# logging.basicConfig(level=logging.INFO)
3435
logging.basicConfig(level=logging.DEBUG)
3536

36-
# this should reflect both this service and the backing
37+
# this should reflect both this service and the backing
3738
# assorted libraries
3839
SERVICE_VERSION = 'v1'
3940
APOD_METHOD_NAME = 'apod'
@@ -91,7 +92,10 @@ def _apod_handler(dt, use_concept_tags=False, use_default_today_date=False, thum
9192
served through the API.
9293
"""
9394
try:
95+
9496
page_props = parse_apod(dt, use_default_today_date, thumbs)
97+
if not page_props:
98+
return None
9599
LOG.debug('managed to get apod page characteristics')
96100

97101
if use_concept_tags:
@@ -131,6 +135,11 @@ def _get_json_for_date(input_date, use_concept_tags, thumbs):
131135

132136
# get data
133137
data = _apod_handler(dt, use_concept_tags, use_default_today_date, thumbs)
138+
139+
# Handle case where no data is available
140+
if not data:
141+
return _abort(code=404, msg=f"No data available for date: {input_date}", usage=False)
142+
134143
data['service_version'] = SERVICE_VERSION
135144

136145
# return info as JSON
@@ -145,22 +154,27 @@ def _get_json_for_random_dates(count, use_concept_tags, thumbs):
145154
:param use_concept_tags:
146155
:return:
147156
"""
148-
149157
if count > 100 or count <= 0:
150158
raise ValueError('Count must be positive and cannot exceed 100')
151-
152159
begin_ordinal = datetime(1995, 6, 16).toordinal()
153160
today_ordinal = datetime.today().toordinal()
154161

155-
date_range = range(begin_ordinal, today_ordinal + 1)
156-
random_date_ordinals = sample(date_range, count)
162+
random_date_ordinals = list(range(begin_ordinal, today_ordinal + 1))
163+
shuffle(random_date_ordinals)
157164

158165
all_data = []
159166
for date_ordinal in random_date_ordinals:
160167
dt = date.fromordinal(date_ordinal)
161168
data = _apod_handler(dt, use_concept_tags, date_ordinal == today_ordinal, thumbs)
169+
170+
# Handle case where no data is available
171+
if not data:
172+
continue
173+
162174
data['service_version'] = SERVICE_VERSION
163175
all_data.append(data)
176+
if len(all_data) >= count:
177+
break
164178

165179
return jsonify(all_data)
166180

@@ -199,7 +213,14 @@ def _get_json_for_date_range(start_date, end_date, use_concept_tags, thumbs):
199213
while start_ordinal <= end_ordinal:
200214
# get data
201215
dt = date.fromordinal(start_ordinal)
216+
202217
data = _apod_handler(dt, use_concept_tags, start_ordinal == today_ordinal, thumbs)
218+
219+
# Handle case where no data is available
220+
if not data:
221+
start_ordinal += 1
222+
continue
223+
203224
data['service_version'] = SERVICE_VERSION
204225

205226
if data['date'] == dt.isoformat():
@@ -223,6 +244,10 @@ def home():
223244
methodname=APOD_METHOD_NAME,
224245
usage=_usage(joinstr='", "', prestr='"') + '"')
225246

247+
@application.route('/static/<asset_path>')
248+
def serve_static(asset_path):
249+
return current_app.send_static_file(asset_path)
250+
226251

227252
@application.route('/' + SERVICE_VERSION + '/' + APOD_METHOD_NAME + '/', methods=['GET'])
228253
def apod():
@@ -286,7 +311,4 @@ def application_error(e):
286311

287312

288313
if __name__ == '__main__':
289-
application.run()
290-
# httpd = make_server('', 8000, application)
291-
# print("Serving on port 8000...")
292-
# httpd.serve_forever()
314+
application.run('0.0.0.0', port=5000)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ nose==1.3.7
1616
setupext-janitor==1.0.0
1717
bs4==0.0.1
1818
mock>=3.0.0
19+
Pillow==7.1.2

run_coverage.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Need to sort out why this is the only way nosetests seem
22
# to work right..
3-
nosetests -v tests/apod/*
3+
nosetests -v tests/*

0 commit comments

Comments
 (0)