API: Activities records init

+ minor db change for Activity
This commit is contained in:
Sam 2018-05-11 23:12:25 +02:00
parent 67099e5665
commit b24f5c2988
9 changed files with 228 additions and 3 deletions

View File

@ -1,4 +1,4 @@
"""empty message """create User table
Revision ID: 9741fc7834da Revision ID: 9741fc7834da
Revises: Revises:

View File

@ -1,4 +1,4 @@
"""empty message """create Activity & Sport tables
Revision ID: b7cfe0c17708 Revision ID: b7cfe0c17708
Revises: 9741fc7834da Revises: 9741fc7834da

View 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 ###

View File

@ -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:

View File

@ -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,
}

View 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

View 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]

View File

@ -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

View File

@ -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