Skip to content

Commit 3a6f7d5

Browse files
committed
merge issues
2 parents f337c49 + 0d02048 commit 3a6f7d5

File tree

14 files changed

+389
-94
lines changed

14 files changed

+389
-94
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ lib/
1212
apod/__pycache__/
1313
.elasticbeanstalk/
1414
*.zip*
15-
Archive.zip
15+
Archive.zip
16+
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: 123 additions & 66 deletions
Large diffs are not rendered by default.
File renamed without changes.

utility.py renamed to apod/utility.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"""
88

99
from bs4 import BeautifulSoup
10-
from datetime import timedelta
10+
import datetime
1111
import requests
1212
import logging
1313
import json
@@ -53,10 +53,27 @@ def _get_last_url(data):
5353

5454
def _get_apod_chars(dt, thumbs):
5555
media_type = 'image'
56-
date_str = dt.strftime('%y%m%d')
57-
apod_url = '%sap%s.html' % (BASE, date_str)
56+
if dt:
57+
date_str = dt.strftime('%y%m%d')
58+
apod_url = '%sap%s.html' % (BASE, date_str)
59+
else:
60+
apod_url = '%sastropix.html' % BASE
5861
LOG.debug('OPENING URL:' + apod_url)
59-
soup = BeautifulSoup(requests.get(apod_url).text, 'html.parser')
62+
res = requests.get(apod_url)
63+
64+
if res.status_code == 404:
65+
return None
66+
# LOG.error(f'No APOD entry for URL: {apod_url}')
67+
# default_obj_path = 'static/default_apod_object.json'
68+
# LOG.debug(f'Loading default APOD response from {default_obj_path}')
69+
# with open(default_obj_path, 'r') as f:
70+
# default_obj_props = json.load(f)
71+
72+
# default_obj_props['date'] = dt.strftime('%Y-%m-%d')
73+
74+
# return default_obj_props
75+
76+
soup = BeautifulSoup(res.text, 'html.parser')
6077
LOG.debug('getting the data url')
6178
hd_data = None
6279
if soup.img:
@@ -88,7 +105,10 @@ def _get_apod_chars(dt, thumbs):
88105
props['media_type'] = media_type
89106
if data:
90107
props['url'] = _get_last_url(data)
91-
props['date'] = dt.isoformat()
108+
if dt:
109+
props['date'] = dt.strftime('%Y-%m-%d')
110+
else:
111+
props['date'] = _date(soup)
92112

93113
if hd_data:
94114
props['hdurl'] = _get_last_url(hd_data)
@@ -233,6 +253,36 @@ def _explanation(soup):
233253
return s
234254

235255

256+
def _date(soup):
257+
"""
258+
Accepts a BeautifulSoup object for the APOD HTML page and returns the
259+
date of the APOD image.
260+
"""
261+
LOG.debug('getting the date from soup data.')
262+
_today = datetime.date.today()
263+
for line in soup.text.split('\n'):
264+
today_year = str(_today.year)
265+
yesterday_year = str((_today-datetime.timedelta(days=1)).year)
266+
# Looks for the first line that starts with the current year.
267+
# This also checks yesterday's year so it doesn't break on January 1st at 00:00 UTC
268+
# before apod.nasa.gov uploads a new image.
269+
if line.startswith(today_year) or line.startswith(yesterday_year):
270+
LOG.debug('found possible date match: ' + line)
271+
# takes apart the date string and turns it into a datetime
272+
try:
273+
year, month, day = line.split()
274+
year = int(year)
275+
month = ['january', 'february', 'march', 'april',
276+
'may', 'june', 'july', 'august',
277+
'september', 'october', 'november', 'december'
278+
].index(month.lower()) + 1
279+
day = int(day)
280+
return datetime.date(year=year, month=month, day=day).strftime('%Y-%m-%d')
281+
except:
282+
LOG.debug('unable to retrieve date from line: ' + line)
283+
raise Exception('Date not found in soup data.')
284+
285+
236286
def parse_apod(dt, use_default_today_date=False, thumbs=False):
237287
"""
238288
Accepts a date in '%Y-%m-%d' format. Returns the URL of the APOD image
@@ -251,9 +301,9 @@ def parse_apod(dt, use_default_today_date=False, thumbs=False):
251301
# service (can happen because they are deployed in different
252302
# timezones). Use the fallback of prior day's date
253303

254-
if use_default_today_date:
304+
if use_default_today_date and dt:
255305
# try to get the day before
256-
dt = dt - timedelta(days=1)
306+
dt = dt - datetime.timedelta(days=1)
257307
return _get_apod_chars(dt, thumbs)
258308
else:
259309
# pass exception up the call stack

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: 40 additions & 17 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:
@@ -122,15 +126,21 @@ def _get_json_for_date(input_date, use_concept_tags, thumbs):
122126
use_default_today_date = False
123127
if not input_date:
124128
# fall back to using today's date IF they didn't specify a date
125-
input_date = datetime.strftime(datetime.today(), '%Y-%m-%d')
126129
use_default_today_date = True
130+
dt = input_date # None
127131

128132
# validate input date
129-
dt = datetime.strptime(input_date, '%Y-%m-%d').date()
130-
_validate_date(dt)
133+
else:
134+
dt = datetime.strptime(input_date, '%Y-%m-%d').date()
135+
_validate_date(dt)
131136

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

136146
# return info as JSON
@@ -145,22 +155,27 @@ def _get_json_for_random_dates(count, use_concept_tags, thumbs):
145155
:param use_concept_tags:
146156
:return:
147157
"""
148-
149158
if count > 100 or count <= 0:
150159
raise ValueError('Count must be positive and cannot exceed 100')
151-
152160
begin_ordinal = datetime(1995, 6, 16).toordinal()
153161
today_ordinal = datetime.today().toordinal()
154162

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

158166
all_data = []
159167
for date_ordinal in random_date_ordinals:
160168
dt = date.fromordinal(date_ordinal)
161169
data = _apod_handler(dt, use_concept_tags, date_ordinal == today_ordinal, thumbs)
170+
171+
# Handle case where no data is available
172+
if not data:
173+
continue
174+
162175
data['service_version'] = SERVICE_VERSION
163176
all_data.append(data)
177+
if len(all_data) >= count:
178+
break
164179

165180
return jsonify(all_data)
166181

@@ -199,7 +214,14 @@ def _get_json_for_date_range(start_date, end_date, use_concept_tags, thumbs):
199214
while start_ordinal <= end_ordinal:
200215
# get data
201216
dt = date.fromordinal(start_ordinal)
217+
202218
data = _apod_handler(dt, use_concept_tags, start_ordinal == today_ordinal, thumbs)
219+
220+
# Handle case where no data is available
221+
if not data:
222+
start_ordinal += 1
223+
continue
224+
203225
data['service_version'] = SERVICE_VERSION
204226

205227
if data['date'] == dt.isoformat():
@@ -223,6 +245,10 @@ def home():
223245
methodname=APOD_METHOD_NAME,
224246
usage=_usage(joinstr='", "', prestr='"') + '"')
225247

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

227253
@application.route('/' + SERVICE_VERSION + '/' + APOD_METHOD_NAME + '/', methods=['GET'])
228254
def apod():
@@ -273,7 +299,7 @@ def page_not_found(e):
273299
"""
274300
Return a custom 404 error.
275301
"""
276-
LOG.info('Invalid page request: ' + e)
302+
LOG.info('Invalid page request: ' + str(e))
277303
return _abort(404, 'Sorry, Nothing at this URL.', usage=True)
278304

279305

@@ -286,7 +312,4 @@ def application_error(e):
286312

287313

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

0 commit comments

Comments
 (0)