API & Client: add weather to Activities - fix #8
This commit is contained in:
@ -6,7 +6,7 @@ from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy.event import listens_for
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.orm.session import object_session
|
||||
from sqlalchemy.types import Enum
|
||||
from sqlalchemy.types import JSON, Enum
|
||||
|
||||
from .utils_format import convert_in_duration, convert_value_to_integer
|
||||
|
||||
@ -121,6 +121,8 @@ class Activity(db.Model):
|
||||
bounds = db.Column(postgresql.ARRAY(db.Float), nullable=True)
|
||||
map = db.Column(db.String(255), nullable=True)
|
||||
map_id = db.Column(db.String(50), nullable=True)
|
||||
weather_start = db.Column(JSON, nullable=True)
|
||||
weather_end = db.Column(JSON, nullable=True)
|
||||
segments = db.relationship('ActivitySegment',
|
||||
lazy=True,
|
||||
cascade='all, delete',
|
||||
@ -234,7 +236,9 @@ class Activity(db.Model):
|
||||
"next_activity": next_activity.id if next_activity else None,
|
||||
"segments": [segment.serialize() for segment in self.segments],
|
||||
"records": [record.serialize() for record in self.records],
|
||||
"map": self.map_id if self.map else None
|
||||
"map": self.map_id if self.map else None,
|
||||
"weather_start": self.weather_start,
|
||||
"weather_end": self.weather_end
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
@ -14,6 +14,7 @@ from werkzeug.utils import secure_filename
|
||||
|
||||
from ..users.models import User
|
||||
from .models import Activity, ActivitySegment, Sport
|
||||
from .utils_weather import get_weather
|
||||
|
||||
|
||||
class ActivityException(Exception):
|
||||
@ -183,12 +184,17 @@ def get_gpx_info(gpx_file):
|
||||
max_speed = 0
|
||||
start = 0
|
||||
map_data = []
|
||||
weather_data = []
|
||||
|
||||
for segment_idx, segment in enumerate(gpx.tracks[0].segments):
|
||||
segment_start = 0
|
||||
for point_idx, point in enumerate(segment.points):
|
||||
if point_idx == 0 and start == 0:
|
||||
start = point.time
|
||||
weather_data.append(get_weather(point))
|
||||
if (point_idx == (len(segment.points) - 1) and
|
||||
segment_idx == (len(gpx.tracks[0].segments) - 1)):
|
||||
weather_data.append(get_weather(point))
|
||||
map_data.append([
|
||||
point.longitude, point.latitude
|
||||
])
|
||||
@ -215,7 +221,7 @@ def get_gpx_info(gpx_file):
|
||||
bounds.max_longitude
|
||||
]
|
||||
|
||||
return gpx_data, map_data
|
||||
return gpx_data, map_data, weather_data
|
||||
|
||||
|
||||
def get_chart_data(gpx_file):
|
||||
@ -305,7 +311,7 @@ def get_map_hash(map_filepath):
|
||||
|
||||
def process_one_gpx_file(params, filename):
|
||||
try:
|
||||
gpx_data, map_data = get_gpx_info(params['file_path'])
|
||||
gpx_data, map_data, weather_data = get_gpx_info(params['file_path'])
|
||||
auth_user_id = params['user'].id
|
||||
new_filepath = get_new_file_path(
|
||||
auth_user_id=auth_user_id,
|
||||
@ -333,6 +339,8 @@ def process_one_gpx_file(params, filename):
|
||||
params['user'], params['activity_data'], gpx_data)
|
||||
new_activity.map = map_filepath
|
||||
new_activity.map_id = get_map_hash(map_filepath)
|
||||
new_activity.weather_start = weather_data[0]
|
||||
new_activity.weather_end = weather_data[1]
|
||||
db.session.add(new_activity)
|
||||
db.session.flush()
|
||||
|
||||
|
32
fittrackee_api/fittrackee_api/activities/utils_weather.py
Normal file
32
fittrackee_api/fittrackee_api/activities/utils_weather.py
Normal file
@ -0,0 +1,32 @@
|
||||
import os
|
||||
|
||||
import forecastio
|
||||
import pytz
|
||||
from fittrackee_api import appLog
|
||||
|
||||
API_KEY = os.getenv('WEATHER_API')
|
||||
|
||||
|
||||
def get_weather(point):
|
||||
if not API_KEY or API_KEY == '':
|
||||
return None
|
||||
try:
|
||||
point_time = pytz.utc.localize(point.time)
|
||||
forecast = forecastio.load_forecast(
|
||||
API_KEY,
|
||||
point.latitude,
|
||||
point.longitude,
|
||||
time=point_time,
|
||||
units='si'
|
||||
)
|
||||
weather = forecast.currently()
|
||||
return {
|
||||
'summary': weather.summary,
|
||||
'icon': weather.icon,
|
||||
'temperature': weather.temperature,
|
||||
'humidity': weather.humidity,
|
||||
'wind': weather.windSpeed,
|
||||
}
|
||||
except Exception as e:
|
||||
appLog.error(e)
|
||||
return None
|
@ -14,6 +14,33 @@ def test_add_activity(
|
||||
assert 'Test' == activity_cycling_user_1.title
|
||||
assert '<Activity \'Cycling\' - 2018-01-01 00:00:00>' == str(activity_cycling_user_1) # noqa
|
||||
|
||||
serialized_activity = activity_cycling_user_1.serialize()
|
||||
assert 1 == serialized_activity['id']
|
||||
assert 1 == serialized_activity['user_id']
|
||||
assert 1 == serialized_activity['sport_id']
|
||||
assert serialized_activity['title'] == 'Test'
|
||||
assert 'creation_date' in serialized_activity
|
||||
assert serialized_activity['modification_date'] is not None
|
||||
assert str(serialized_activity['activity_date']) == '2018-01-01 00:00:00'
|
||||
assert serialized_activity['duration'] == '0:17:04'
|
||||
assert serialized_activity['pauses'] is None
|
||||
assert serialized_activity['moving'] == '0:17:04'
|
||||
assert serialized_activity['distance'] == 10.0
|
||||
assert serialized_activity['max_alt'] is None
|
||||
assert serialized_activity['descent'] is None
|
||||
assert serialized_activity['ascent'] is None
|
||||
assert serialized_activity['max_speed'] == 10.0
|
||||
assert serialized_activity['ave_speed'] == 10.0
|
||||
assert serialized_activity['with_gpx'] is False
|
||||
assert serialized_activity['bounds'] == []
|
||||
assert serialized_activity['previous_activity'] is None
|
||||
assert serialized_activity['next_activity'] is None
|
||||
assert serialized_activity['segments'] == []
|
||||
assert serialized_activity['records'] != []
|
||||
assert serialized_activity['map'] is None
|
||||
assert serialized_activity['weather_start'] is None
|
||||
assert serialized_activity['weather_end'] is None
|
||||
|
||||
|
||||
def test_add_segment(
|
||||
app, sport_1_cycling, user_1, activity_cycling_user_1,
|
||||
|
Reference in New Issue
Block a user