-
Notifications
You must be signed in to change notification settings - Fork 0
/
astroAPI.py
338 lines (266 loc) · 12.6 KB
/
astroAPI.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
from __init__ import app
from flask import request, jsonify
from models import db, Constellation, Stars, ConstellationSchema, SingleStarSchema, StarSchema
from models import AuthKeys
from messages import messages
from werkzeug import exceptions
from flask_cors import CORS
import functions
from wrappers import auth_key_required, admin_only
from env import secret_keys
# Schema init
constellation_schema = ConstellationSchema()
constellations_schema = ConstellationSchema(many=True)
single_star_schema = SingleStarSchema()
multiple_stars_schema = SingleStarSchema(many=True)
# CORS handling
CORS(app)
# API logic
@app.route('/astropy')
def home():
return jsonify({'Welcome': messages['welcome']})
@app.route('/astropy/hello', methods=['GET'])
def greetings():
"""Greeting message to the user, insert first and last name in the query
URL and you will receive a small greeting message"""
if not request.args:
return jsonify({'message': messages['hello']}), 204
try:
name_last = [request.args['name'], request.args['last']]
name = " ".join(name_last)
response = {'Hello, There': name}
return jsonify(response), 200
except exceptions.BadRequestKeyError as b:
return jsonify({'message': 'Bad Request'}), 400
# Main API routes for constellations, stars and TODO planets
@app.route('/astropy/api/v1/constellation', methods=['GET', 'POST'])
@auth_key_required
def get_constellation():
"""Main path to make get requests for constellations.
It takes the c parameter only."""
if not request.args:
return jsonify({'message': messages['no argument']}), 400
try:
c = request.args['c'].lower()
except exceptions.BadRequestKeyError:
return jsonify({'message': messages['bad request']}), 400
query_result = Constellation.query.filter_by(name=c).first_or_404()
output = constellation_schema.dump(query_result)
return jsonify({'constellation': output}), 200
@app.route('/astropy/api/v1/query', methods=['GET', 'POST'])
@auth_key_required
def get_constellations_via_query():
"""It allows flexibility upon constellation lookups. As a simple db search.
Parameters accepted: [quadrant as q, min latitude as min, max latitude as max]"""
if not request.args:
return jsonify({'message': messages['no argument']}), 400
obj_query = None
if 'q' in request.args and 'min' in request.args and 'max' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.quadrant == request.args['q'],
Constellation.min_latitude >= int(request.args['min']),
Constellation.max_latitude <= int(request.args['max']))
if 'q' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.quadrant == request.args['q'])
if 'min' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.quadrant == request.args['q'],
Constellation.min_latitude >= int(request.args['min']))
if 'max' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.quadrant == request.args['q'],
Constellation.max_latitude <= int(request.args['max']))
if 'min' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.min_latitude >= int(request.args['min']))
if 'q' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.quadrant == request.args['q'],
Constellation.min_latitude >= int(request.args['min']))
if 'max' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.max_latitude <= int(request.args['max']),
Constellation.min_latitude >= int(request.args['min']))
if 'max' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.max_latitude >= int(request.args['max']))
if 'min' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.max_latitude <= int(request.args['max']),
Constellation.min_latitude >= int(request.args['min']))
if 'q' in request.args:
obj_query = db.session.query(Constellation).filter(Constellation.quadrant == request.args['q'],
Constellation.max_latitude <= int(request.args['max']))
if obj_query is None or len(obj_query.all()) == 0:
return jsonify({'message': messages['not found']}), 404
else:
output = constellations_schema.dump(obj_query)
return jsonify({'constellations': output}), 200
@app.route('/astropy/api/v1/constellation/all', methods=['POST'])
@admin_only
# @auth_key_required
def get_all_constellations():
"""Route that yields all the constellations: FYI there are 88"""
_all = Constellation.query.all()
output = constellations_schema.dump(_all)
return jsonify({'constellations': output}), 200
@app.route('/astropy/api/v1/star', methods=['GET', 'POST'])
@auth_key_required
def get_star():
"""Route to get single stars from the query, only parameter as s accepted"""
if not request.args:
return jsonify({'message': messages['no argument']}), 400
try:
s = request.args['s'].lower()
query_result = Stars.query.filter_by(star=s).first_or_404()
output = single_star_schema.dump(query_result)
return jsonify({'star': output}), 200
except exceptions.BadRequestKeyError:
return jsonify({'message': 'Bad Request'}), 400
@app.route('/astropy/api/v1/star/all')
@admin_only
# @auth_key_required
def get_all_stars():
"""It returns all the stars of all the constellations. They are in the range
between 450-650, the real count would be way higher, but it considers
the main and most visible stars of the constellation"""
_all = Stars.query.all()
output = multiple_stars_schema.dump(_all)
return jsonify({'stars': output}), 200
# TODO get stars via query -- distance only
@app.route('/astropy/api/v1/where-to-look', methods=['GET', 'POST'])
@auth_key_required
def where_to_look():
""" Parameters: [latitude as lat and longitude as lon, and the star to observe as s]
Positive latitude indicates the Northern hemisphere, whereas negative points to
the Southern hemisphere. Otherwise, if you don't have coordinates at hand
the city is also an option, use city=... """
# where to look (declination - position)
# right ascension == number of hours behind the Sun on 21st March
if not request.args:
return jsonify({'message': messages['no argument']}), 400
response = {}
try:
s = request.args['s'].lower()
except exceptions.BadRequestKeyError:
return jsonify({'message': 'No star specified'}), 400
response['star'] = s
star = Stars.query.filter_by(star=s).first()
if not star:
return jsonify({'message': messages['not found']}), 404
if 'lat' in request.args and 'lon' in request.args:
response['lat'] = request.args['lat']
response['lon'] = request.args['lon']
try:
lat = int(request.args['lat'])
lon = int(request.args['lon'])
except ValueError:
return jsonify({'message': 'Format not valid'}), 400
elif 'city' in request.args:
city = request.args['city']
coordinates = functions.geocoding_api(city)
if coordinates is None:
return jsonify({'message': f'{city} not found'}), 404
response['lat'] = coordinates['lat']
response['lon'] = coordinates['lon']
lat = functions.check_if_north(coordinates['lat'])
lon = functions.check_if_east(coordinates['lon'])
else:
return jsonify({'message': messages['no coordinates']}), 400
declination = star.declination
RA = star.right_ascension
response['declination'] = declination
int_declination = int(declination[:3])
where = int_declination - lat
if abs(where) > 90:
response['where'] = f'{s} is not visible from your location'
return jsonify(response), 200
elif - 2 < where < + 2:
response['where'] = 'just look over your head'
elif where < 0:
response['where'] = f'{abs(where)}° towards south'
elif where > 0:
response['where'] = f'{where}° towards north'
# Get the time of sunrise and sunset from API call
sun_time = functions.sun_time_from_api(lat, lon)
response['sunrise at location'] = sun_time['sunrise']
response['sunset at location'] = sun_time['sunset']
star_params = functions.star_rising_time(int(RA[:2]), sun_time, RA)
response['current delay'] = star_params['current ra']
if star_params['message'] is not None:
response['message'] = star_params['message']
# Check for circumpolar stars
if lat + int_declination > 90 or lat + int_declination < - 90:
response['it rises'] = f'{s} is circumpolar star, hence it is always visible from this location'
else:
if 'star rise' in star_params:
degrees = functions.calculate_position(star_params['star rise'], sun_time['utc'])
response['current position'] = degrees
response['it rises'] = star_params['star rise']
response['it sets'] = star_params['star set']
# response['closest RA'] = star_time['closest delay']
else:
response['current position'] = star_params
return jsonify(response), 200
@app.route('/astropy/api/v1/star/closest/<int:limit>', methods=['GET', 'POST'])
@auth_key_required
def closest(limit):
"""Simple route to get the closest stars in the system, the integer is part
of the route and it indicates the limit of results the search will yield.
By default it is set to a max of 25 results, and a min of 5"""
if limit <= 0:
limit = 5
elif limit > 25:
limit = 25
_closest = Stars.query.order_by('distance').limit(limit)
output = multiple_stars_schema.dump(_closest)
return jsonify(output), 200
@app.route('/astropy/api/v1/star/brightest/<int:limit>', methods=['GET', 'POST'])
@auth_key_required
def brightest(limit):
"""Simple route to get the brightest stars in the system, the integer is part
of the route and it indicates the limit of results the search will yield.
By default it is set to a max of 25 results, and a min of 5"""
if limit <= 0:
limit = 5
elif limit > 25:
limit = 25
_brightest = Stars.query.order_by('app_magnitude').limit(limit)
output = multiple_stars_schema.dump(_brightest)
return jsonify(output), 200
# Admin-only route
@app.route('/astropy/api/put-auth-key', methods=['PUT'])
def put_auth_in_db():
admin_key = request.form['admin key']
if admin_key != secret_keys['ADMIN_KEY']:
return jsonify({'message': 'Invalid admin key'}), 403
user_id = request.form['user id']
api_key = request.form['api key']
exp = request.form['exp date']
new_auth_key = AuthKeys(user_id=user_id, key=api_key, expiration_date=exp)
db.session.add(new_auth_key)
db.session.commit()
return jsonify({'message': 'Added correctly'}), 202
@app.route('/astropy/api/delete-auth-key', methods=['DELETE'])
def delete_auth_key():
admin_key = request.form['admin key']
if admin_key != secret_keys['ADMIN_KEY']:
return jsonify({'message': 'Invalid admin key'}), 403
user_id = request.form['user id']
api_key = request.form['api key']
record = AuthKeys.query.filter_by(user_id=user_id, key=api_key).first()
try:
db.session.delete(record)
db.session.commit()
return jsonify({'message': 'Deleted correctly'}), 202
except exceptions:
return jsonify({'message': 'No record found'}), 404
@app.route('/astropy/api/update-auth-status', methods=['PUT'])
def update_auth_status():
admin_key = request.form['admin key']
if admin_key != secret_keys['ADMIN_KEY']:
return jsonify({'message': 'Invalid admin key'}), 403
user_id = request.form['user id']
api_key = request.form['api key']
status = request.form['status']
auth_key = AuthKeys.query.filter_by(user_id=user_id, key=api_key).first()
if status == 'True':
auth_key.active = True
elif status == 'False':
auth_key.active = False
db.session.commit()
return jsonify({'message': 'Status updated'}), 200
if __name__ == '__main__':
app.run()