API: Activities records init
+ minor db change for Activity
This commit is contained in:
parent
67099e5665
commit
b24f5c2988
@ -1,4 +1,4 @@
|
|||||||
"""empty message
|
"""create User table
|
||||||
|
|
||||||
Revision ID: 9741fc7834da
|
Revision ID: 9741fc7834da
|
||||||
Revises:
|
Revises:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""empty message
|
"""create Activity & Sport tables
|
||||||
|
|
||||||
Revision ID: b7cfe0c17708
|
Revision ID: b7cfe0c17708
|
||||||
Revises: 9741fc7834da
|
Revises: 9741fc7834da
|
||||||
|
42
mpwo_api/migrations/versions/caf0e0dc621a_.py
Normal file
42
mpwo_api/migrations/versions/caf0e0dc621a_.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
"""create Record table
|
||||||
|
|
||||||
|
Revision ID: caf0e0dc621a
|
||||||
|
Revises: b7cfe0c17708
|
||||||
|
Create Date: 2018-05-11 22:33:08.267488
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'caf0e0dc621a'
|
||||||
|
down_revision = 'b7cfe0c17708'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('records',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
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('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.ForeignKeyConstraint(['activity_id'], ['activities.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['sport_id'], ['sports.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('user_id', 'sport_id', 'record_type', name='user_sports_records')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('records')
|
||||||
|
# ### end Alembic commands ###
|
@ -29,11 +29,13 @@ def create_app():
|
|||||||
from .users.auth import auth_blueprint # noqa
|
from .users.auth import auth_blueprint # noqa
|
||||||
from .users.users import users_blueprint # noqa
|
from .users.users import users_blueprint # noqa
|
||||||
from .activities.activities import activities_blueprint # noqa
|
from .activities.activities import activities_blueprint # noqa
|
||||||
|
from .activities.records import records_blueprint # noqa
|
||||||
from .activities.sports import sports_blueprint # noqa
|
from .activities.sports import sports_blueprint # noqa
|
||||||
|
|
||||||
app.register_blueprint(users_blueprint, url_prefix='/api')
|
app.register_blueprint(users_blueprint, url_prefix='/api')
|
||||||
app.register_blueprint(auth_blueprint, url_prefix='/api')
|
app.register_blueprint(auth_blueprint, url_prefix='/api')
|
||||||
app.register_blueprint(activities_blueprint, url_prefix='/api')
|
app.register_blueprint(activities_blueprint, url_prefix='/api')
|
||||||
|
app.register_blueprint(records_blueprint, url_prefix='/api')
|
||||||
app.register_blueprint(sports_blueprint, url_prefix='/api')
|
app.register_blueprint(sports_blueprint, url_prefix='/api')
|
||||||
|
|
||||||
if app.debug:
|
if app.debug:
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from mpwo_api import db
|
from mpwo_api import db
|
||||||
|
from sqlalchemy.types import Enum
|
||||||
|
|
||||||
|
record_types = [
|
||||||
|
'AS', # 'Best Average Speed'
|
||||||
|
'FD', # 'Farthest Distance'
|
||||||
|
'LD', # 'Longest Duration'
|
||||||
|
'MS', # 'Max speed'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Sport(db.Model):
|
class Sport(db.Model):
|
||||||
@ -10,6 +18,9 @@ class Sport(db.Model):
|
|||||||
activities = db.relationship('Activity',
|
activities = db.relationship('Activity',
|
||||||
lazy=True,
|
lazy=True,
|
||||||
backref=db.backref('sports', lazy='joined'))
|
backref=db.backref('sports', lazy='joined'))
|
||||||
|
records = db.relationship('Record',
|
||||||
|
lazy=True,
|
||||||
|
backref=db.backref('sports', lazy='joined'))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.label
|
return self.label
|
||||||
@ -55,6 +66,9 @@ class Activity(db.Model):
|
|||||||
ascent = 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, 3), nullable=True) # km/h
|
max_speed = db.Column(db.Numeric(5, 3), nullable=True) # km/h
|
||||||
ave_speed = db.Column(db.Numeric(5, 3), nullable=True) # km/h
|
ave_speed = db.Column(db.Numeric(5, 3), nullable=True) # km/h
|
||||||
|
records = db.relationship('Record',
|
||||||
|
lazy=True,
|
||||||
|
backref=db.backref('activities', lazy='joined'))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.sports.label) + \
|
return str(self.sports.label) + \
|
||||||
@ -87,3 +101,55 @@ class Activity(db.Model):
|
|||||||
"ave_speed": float(self.ave_speed) if self.ave_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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Record(db.Model):
|
||||||
|
__tablename__ = "records"
|
||||||
|
__table_args__ = (db.UniqueConstraint(
|
||||||
|
'user_id', 'sport_id', 'record_type', name='user_sports_records'),)
|
||||||
|
id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=True)
|
||||||
|
user_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey('users.id'),
|
||||||
|
nullable=False)
|
||||||
|
sport_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey('sports.id'),
|
||||||
|
nullable=False)
|
||||||
|
activity_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey('activities.id'),
|
||||||
|
nullable=False)
|
||||||
|
record_type = db.Column(Enum(*record_types, name="record_types"))
|
||||||
|
activity_date = db.Column(db.DateTime, nullable=False)
|
||||||
|
value_interval = db.Column(db.Interval, nullable=True)
|
||||||
|
value_float = db.Column(db.Numeric(5, 3), nullable=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.sports.label) + \
|
||||||
|
" - " + self.record_type + \
|
||||||
|
" - " + self.activity_date.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
def __init__(self, user_id, sport_id, activity, record_type):
|
||||||
|
self.user_id = user_id
|
||||||
|
self.sport_id = sport_id
|
||||||
|
self.activity_id = activity.id
|
||||||
|
self.record_type = record_type
|
||||||
|
self.activity_date = activity.activity_date
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"user_id": self.user_id,
|
||||||
|
"sport_id": self.sport_id,
|
||||||
|
"activity_id": self.activity_id,
|
||||||
|
"record_type": self.record_type,
|
||||||
|
"activity_date": self.activity_date,
|
||||||
|
"value_interval": str(
|
||||||
|
self.value_interval) if self.value_interval else None,
|
||||||
|
"value_float": str(
|
||||||
|
self.value_float) if self.value_float else None,
|
||||||
|
}
|
||||||
|
24
mpwo_api/mpwo_api/activities/records.py
Normal file
24
mpwo_api/mpwo_api/activities/records.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from flask import Blueprint, jsonify
|
||||||
|
|
||||||
|
from ..users.utils import authenticate
|
||||||
|
from .models import Record
|
||||||
|
|
||||||
|
records_blueprint = Blueprint('records', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@records_blueprint.route('/records', methods=['GET'])
|
||||||
|
@authenticate
|
||||||
|
def get_sports(auth_user_id):
|
||||||
|
"""Get all records for authenticated user"""
|
||||||
|
records = Record.query.filter_by(user_id=auth_user_id)\
|
||||||
|
.order_by(Record.record_type).all()
|
||||||
|
records_list = []
|
||||||
|
for record in records:
|
||||||
|
records_list.append(record.serialize())
|
||||||
|
response_object = {
|
||||||
|
'status': 'success',
|
||||||
|
'data': {
|
||||||
|
'records': records_list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonify(response_object), 200
|
77
mpwo_api/mpwo_api/tests/test_records.py
Normal file
77
mpwo_api/mpwo_api/tests/test_records.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
from mpwo_api.tests.utils import add_activity, add_record, add_sport, add_user
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_all_activities_for_authenticated_user(app):
|
||||||
|
add_user('test', 'test@test.com', '12345678')
|
||||||
|
add_user('toto', 'toto@toto.com', '12345678')
|
||||||
|
add_sport('cycling')
|
||||||
|
add_sport('running')
|
||||||
|
|
||||||
|
activity = add_activity(
|
||||||
|
user_id=1,
|
||||||
|
sport_id=2,
|
||||||
|
activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'),
|
||||||
|
distance=10,
|
||||||
|
duration=datetime.timedelta(seconds=1024)
|
||||||
|
)
|
||||||
|
add_record(1, 2, activity, 'LD')
|
||||||
|
|
||||||
|
activity = add_activity(
|
||||||
|
user_id=2,
|
||||||
|
sport_id=1,
|
||||||
|
activity_date=datetime.datetime.strptime('23/01/2018', '%d/%m/%Y'),
|
||||||
|
distance=15,
|
||||||
|
duration=datetime.timedelta(seconds=3600),
|
||||||
|
)
|
||||||
|
add_record(2, 1, activity, 'MS')
|
||||||
|
|
||||||
|
activity = add_activity(
|
||||||
|
user_id=1,
|
||||||
|
sport_id=1,
|
||||||
|
activity_date=datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'),
|
||||||
|
distance=12,
|
||||||
|
duration=datetime.timedelta(seconds=6000)
|
||||||
|
)
|
||||||
|
add_record(1, 1, activity, 'FD')
|
||||||
|
|
||||||
|
client = app.test_client()
|
||||||
|
resp_login = client.post(
|
||||||
|
'/api/auth/login',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
response = client.get(
|
||||||
|
'/api/records',
|
||||||
|
headers=dict(
|
||||||
|
Authorization='Bearer ' + json.loads(
|
||||||
|
resp_login.data.decode()
|
||||||
|
)['auth_token']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert 'success' in data['status']
|
||||||
|
assert len(data['data']['records']) == 2
|
||||||
|
|
||||||
|
assert 'Sun, 01 Apr 2018 00:00:00 GMT' == data['data']['records'][0]['activity_date'] # noqa
|
||||||
|
assert 1 == data['data']['records'][0]['user_id']
|
||||||
|
assert 1 == data['data']['records'][0]['sport_id']
|
||||||
|
assert 3 == data['data']['records'][0]['activity_id']
|
||||||
|
assert 'FD' == data['data']['records'][0]['record_type']
|
||||||
|
assert 'value_interval' in data['data']['records'][0]
|
||||||
|
assert 'value_float' in data['data']['records'][0]
|
||||||
|
|
||||||
|
assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['records'][1]['activity_date'] # noqa
|
||||||
|
assert 1 == data['data']['records'][1]['user_id']
|
||||||
|
assert 2 == data['data']['records'][1]['sport_id']
|
||||||
|
assert 1 == data['data']['records'][1]['activity_id']
|
||||||
|
assert 'LD' == data['data']['records'][1]['record_type']
|
||||||
|
assert 'value_interval' in data['data']['records'][1]
|
||||||
|
assert 'value_float' in data['data']['records'][1]
|
@ -1,7 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from mpwo_api import db
|
from mpwo_api import db
|
||||||
from mpwo_api.activities.models import Activity, Sport
|
from mpwo_api.activities.models import Activity, Record, Sport
|
||||||
from mpwo_api.users.models import User
|
from mpwo_api.users.models import User
|
||||||
|
|
||||||
|
|
||||||
@ -58,3 +58,14 @@ def add_activity(user_id, sport_id, activity_date, distance, duration):
|
|||||||
def get_gpx_filepath(activity_id):
|
def get_gpx_filepath(activity_id):
|
||||||
activity = Activity.query.filter_by(id=activity_id).first()
|
activity = Activity.query.filter_by(id=activity_id).first()
|
||||||
return activity.gpx
|
return activity.gpx
|
||||||
|
|
||||||
|
|
||||||
|
def add_record(user_id, sport_id, activity, record_type):
|
||||||
|
record = Record(
|
||||||
|
user_id=user_id,
|
||||||
|
sport_id=sport_id,
|
||||||
|
activity=activity,
|
||||||
|
record_type=record_type)
|
||||||
|
db.session.add(record)
|
||||||
|
db.session.commit()
|
||||||
|
return record
|
||||||
|
@ -22,6 +22,9 @@ class User(db.Model):
|
|||||||
activities = db.relationship('Activity',
|
activities = db.relationship('Activity',
|
||||||
lazy=True,
|
lazy=True,
|
||||||
backref=db.backref('users', lazy='joined'))
|
backref=db.backref('users', lazy='joined'))
|
||||||
|
records = db.relationship('Record',
|
||||||
|
lazy=True,
|
||||||
|
backref=db.backref('users', lazy='joined'))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User %r>' % self.username
|
return '<User %r>' % self.username
|
||||||
|
Loading…
Reference in New Issue
Block a user