API: Activity segments table init
This commit is contained in:
parent
6d4574332b
commit
e4a65f4c79
@ -23,10 +23,9 @@ def upgrade():
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('sport_id', sa.Integer(), nullable=False),
|
||||
sa.Column('activity_id', sa.Integer(), nullable=False),
|
||||
sa.Column('record_type', sa.Enum('FD', 'LD', 'MS', name='record_types'), nullable=True),
|
||||
sa.Column('record_type', sa.Enum('AS', 'FD', 'LD', 'MS', name='record_types'), nullable=True),
|
||||
sa.Column('activity_date', sa.DateTime(), nullable=False),
|
||||
sa.Column('value_interval', sa.Interval(), nullable=True),
|
||||
sa.Column('value_float', sa.Numeric(precision=5, scale=3), nullable=True),
|
||||
sa.Column('value', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['activity_id'], ['activities.id'], ),
|
||||
sa.ForeignKeyConstraint(['sport_id'], ['sports.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
|
43
mpwo_api/migrations/versions/dd73d23a7a3d_.py
Normal file
43
mpwo_api/migrations/versions/dd73d23a7a3d_.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""create Activities Segments table
|
||||
|
||||
Revision ID: dd73d23a7a3d
|
||||
Revises: caf0e0dc621a
|
||||
Create Date: 2018-05-14 12:12:57.299200
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'dd73d23a7a3d'
|
||||
down_revision = 'caf0e0dc621a'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('activity_segments',
|
||||
sa.Column('activity_id', sa.Integer(), nullable=False),
|
||||
sa.Column('segment_id', sa.Integer(), nullable=False),
|
||||
sa.Column('duration', sa.Interval(), nullable=False),
|
||||
sa.Column('pauses', sa.Interval(), nullable=True),
|
||||
sa.Column('moving', sa.Interval(), nullable=True),
|
||||
sa.Column('distance', sa.Numeric(precision=5, scale=3), nullable=True),
|
||||
sa.Column('min_alt', sa.Numeric(precision=5, scale=2), nullable=True),
|
||||
sa.Column('max_alt', sa.Numeric(precision=5, scale=2), nullable=True),
|
||||
sa.Column('descent', sa.Numeric(precision=5, scale=2), nullable=True),
|
||||
sa.Column('ascent', sa.Numeric(precision=5, scale=2), nullable=True),
|
||||
sa.Column('max_speed', sa.Numeric(precision=5, scale=2), nullable=True),
|
||||
sa.Column('ave_speed', sa.Numeric(precision=5, scale=2), nullable=True),
|
||||
sa.ForeignKeyConstraint(['activity_id'], ['activities.id'], ),
|
||||
sa.PrimaryKeyConstraint('activity_id', 'segment_id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('activity_segments')
|
||||
# ### end Alembic commands ###
|
@ -8,8 +8,8 @@ from sqlalchemy import exc
|
||||
from ..users.utils import authenticate, verify_extension
|
||||
from .models import Activity, Sport
|
||||
from .utils import (
|
||||
create_activity, edit_activity, get_file_path, get_gpx_info,
|
||||
get_new_file_path
|
||||
create_activity, create_segment, edit_activity, get_file_path,
|
||||
get_gpx_info, get_new_file_path
|
||||
)
|
||||
|
||||
activities_blueprint = Blueprint('activities', __name__)
|
||||
@ -170,6 +170,10 @@ def post_activity(auth_user_id):
|
||||
new_activity = create_activity(
|
||||
auth_user_id, activity_data, gpx_data)
|
||||
db.session.add(new_activity)
|
||||
db.session.flush()
|
||||
for segment_data in gpx_data['segments']:
|
||||
new_segment = create_segment(new_activity.id, segment_data)
|
||||
db.session.add(new_segment)
|
||||
db.session.commit()
|
||||
response_object = {
|
||||
'status': 'created',
|
||||
@ -178,7 +182,6 @@ def post_activity(auth_user_id):
|
||||
}
|
||||
}
|
||||
return jsonify(response_object), 201
|
||||
|
||||
except (exc.IntegrityError, ValueError) as e:
|
||||
db.session.rollback()
|
||||
appLog.error(e)
|
||||
|
@ -68,6 +68,13 @@ class Activity(db.Model):
|
||||
ascent = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||
max_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
||||
ave_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
||||
segments = db.relationship('ActivitySegment',
|
||||
lazy=True,
|
||||
cascade='all, delete',
|
||||
backref=db.backref(
|
||||
'activities',
|
||||
lazy='joined',
|
||||
single_parent=True))
|
||||
records = db.relationship('Record',
|
||||
lazy=True,
|
||||
backref=db.backref('activities', lazy='joined'))
|
||||
@ -102,7 +109,53 @@ class Activity(db.Model):
|
||||
"ascent": float(self.ascent) if self.ascent else None,
|
||||
"max_speed": float(self.max_speed) if self.max_speed else None,
|
||||
"ave_speed": float(self.ave_speed) if self.ave_speed else None,
|
||||
"with_gpx": self.gpx is not None
|
||||
"with_gpx": self.gpx is not None,
|
||||
"segments": [segment.serialize() for segment in self.segments]
|
||||
}
|
||||
|
||||
|
||||
class ActivitySegment(db.Model):
|
||||
__tablename__ = "activity_segments"
|
||||
activity_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey('activities.id'),
|
||||
primary_key=True)
|
||||
segment_id = db.Column(
|
||||
db.Integer,
|
||||
primary_key=True)
|
||||
duration = db.Column(db.Interval, nullable=False)
|
||||
pauses = db.Column(db.Interval, nullable=True)
|
||||
moving = db.Column(db.Interval, nullable=True)
|
||||
distance = db.Column(db.Numeric(5, 3), nullable=True) # kilometers
|
||||
min_alt = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||
max_alt = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||
descent = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||
ascent = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||
max_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
||||
ave_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
||||
|
||||
def __str__(self):
|
||||
return '<Segment \'{}\' for activity \'{}\'>'.format(
|
||||
self.segment_id, self.activity_id, )
|
||||
|
||||
def __init__(self, segment_id, activity_id):
|
||||
self.segment_id = segment_id
|
||||
self.activity_id = activity_id
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"activity_id": self.activity_id,
|
||||
"segment_id": self.segment_id,
|
||||
"duration": str(self.duration) if self.duration else None,
|
||||
"pauses": str(self.pauses) if self.pauses else None,
|
||||
"moving": str(self.moving) if self.moving else None,
|
||||
"distance": float(self.distance) if self.distance else None,
|
||||
"min_alt": float(self.min_alt) if self.min_alt else None,
|
||||
"max_alt": float(self.max_alt) if self.max_alt else None,
|
||||
"descent": float(self.descent) if self.descent else None,
|
||||
"ascent": float(self.ascent) if self.ascent else None,
|
||||
"max_speed": float(self.max_speed) if self.max_speed else None,
|
||||
"ave_speed": float(self.ave_speed) if self.ave_speed else None
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,7 +7,20 @@ from flask import current_app
|
||||
from mpwo_api import appLog
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from .models import Activity, Sport
|
||||
from .models import Activity, ActivitySegment, Sport
|
||||
|
||||
|
||||
def update_activity_data(activity, gpx_data):
|
||||
"""activity could be a complete activity or an activity segment"""
|
||||
activity.pauses = gpx_data['stop_time']
|
||||
activity.moving = gpx_data['moving_time']
|
||||
activity.min_alt = gpx_data['elevation_min']
|
||||
activity.max_alt = gpx_data['elevation_max']
|
||||
activity.descent = gpx_data['downhill']
|
||||
activity.ascent = gpx_data['uphill']
|
||||
activity.max_speed = gpx_data['max_speed']
|
||||
activity.ave_speed = gpx_data['average_speed']
|
||||
return activity
|
||||
|
||||
|
||||
def create_activity(
|
||||
@ -39,14 +52,7 @@ def create_activity(
|
||||
|
||||
if gpx_data:
|
||||
new_activity.gpx = gpx_data['filename']
|
||||
new_activity.pauses = gpx_data['stop_time']
|
||||
new_activity.moving = gpx_data['moving_time']
|
||||
new_activity.min_alt = gpx_data['elevation_min']
|
||||
new_activity.max_alt = gpx_data['elevation_max']
|
||||
new_activity.descent = gpx_data['downhill']
|
||||
new_activity.ascent = gpx_data['uphill']
|
||||
new_activity.max_speed = gpx_data['max_speed']
|
||||
new_activity.ave_speed = gpx_data['average_speed']
|
||||
update_activity_data(new_activity, gpx_data)
|
||||
else:
|
||||
new_activity.moving = duration
|
||||
new_activity.ave_speed = new_activity.distance / (
|
||||
@ -55,6 +61,17 @@ def create_activity(
|
||||
return new_activity
|
||||
|
||||
|
||||
def create_segment(activity_id, segment_data):
|
||||
new_segment = ActivitySegment(
|
||||
activity_id=activity_id,
|
||||
segment_id=segment_data['idx']
|
||||
)
|
||||
new_segment.duration = segment_data['duration']
|
||||
new_segment.distance = segment_data['distance']
|
||||
update_activity_data(new_segment, segment_data)
|
||||
return new_segment
|
||||
|
||||
|
||||
def edit_activity(activity, activity_data):
|
||||
activity.sport_id = activity_data.get('sport_id')
|
||||
if activity_data.get('title'):
|
||||
|
@ -3,7 +3,7 @@ import os
|
||||
|
||||
import pytest
|
||||
from mpwo_api import create_app, db
|
||||
from mpwo_api.activities.models import Activity, Record, Sport
|
||||
from mpwo_api.activities.models import Activity, ActivitySegment, Record, Sport
|
||||
from mpwo_api.users.models import User
|
||||
|
||||
os.environ["FLASK_ENV"] = 'testing'
|
||||
@ -93,6 +93,19 @@ def activity_cycling_user_1():
|
||||
return activity
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def activity_cycling_user_1_segment():
|
||||
activity_segment = ActivitySegment(
|
||||
activity_id=1,
|
||||
segment_id=0
|
||||
)
|
||||
activity_segment.duration = datetime.timedelta(seconds=6000)
|
||||
activity_segment.distance = 5
|
||||
db.session.add(activity_segment)
|
||||
db.session.commit()
|
||||
return activity_segment
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def activity_running_user_1():
|
||||
activity = Activity(
|
||||
|
@ -13,3 +13,10 @@ def test_add_activity(
|
||||
assert '0:17:04' == str(activity_cycling_user_1.duration)
|
||||
assert 'Test' == activity_cycling_user_1.title
|
||||
assert '<Activity \'Cycling\' - 2018-01-01 00:00:00>' == str(activity_cycling_user_1) # noqa
|
||||
|
||||
|
||||
def test_add_segment(
|
||||
app, sport_1_cycling, user_1, activity_cycling_user_1,
|
||||
activity_cycling_user_1_segment
|
||||
):
|
||||
assert '<Segment \'0\' for activity \'1\'>' == str(activity_cycling_user_1_segment) # noqa
|
||||
|
Loading…
Reference in New Issue
Block a user