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
|
||||
Revises:
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""empty message
|
||||
"""create Activity & Sport tables
|
||||
|
||||
Revision ID: b7cfe0c17708
|
||||
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.users import users_blueprint # noqa
|
||||
from .activities.activities import activities_blueprint # noqa
|
||||
from .activities.records import records_blueprint # noqa
|
||||
from .activities.sports import sports_blueprint # noqa
|
||||
|
||||
app.register_blueprint(users_blueprint, url_prefix='/api')
|
||||
app.register_blueprint(auth_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')
|
||||
|
||||
if app.debug:
|
||||
|
@ -1,6 +1,14 @@
|
||||
import datetime
|
||||
|
||||
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):
|
||||
@ -10,6 +18,9 @@ class Sport(db.Model):
|
||||
activities = db.relationship('Activity',
|
||||
lazy=True,
|
||||
backref=db.backref('sports', lazy='joined'))
|
||||
records = db.relationship('Record',
|
||||
lazy=True,
|
||||
backref=db.backref('sports', lazy='joined'))
|
||||
|
||||
def __repr__(self):
|
||||
return self.label
|
||||
@ -55,6 +66,9 @@ class Activity(db.Model):
|
||||
ascent = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||
max_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):
|
||||
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,
|
||||
"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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -58,3 +58,14 @@ def add_activity(user_id, sport_id, activity_date, distance, duration):
|
||||
def get_gpx_filepath(activity_id):
|
||||
activity = Activity.query.filter_by(id=activity_id).first()
|
||||
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',
|
||||
lazy=True,
|
||||
backref=db.backref('users', lazy='joined'))
|
||||
records = db.relationship('Record',
|
||||
lazy=True,
|
||||
backref=db.backref('users', lazy='joined'))
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.username
|
||||
|
Loading…
Reference in New Issue
Block a user