API: only one column to store record values
This commit is contained in:
		@@ -1,6 +1,7 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from mpwo_api import db
 | 
			
		||||
from sqlalchemy.ext.hybrid import hybrid_property
 | 
			
		||||
from sqlalchemy.types import Enum
 | 
			
		||||
 | 
			
		||||
record_types = [
 | 
			
		||||
@@ -64,8 +65,8 @@ class Activity(db.Model):
 | 
			
		||||
    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, 3), nullable=True)   # km/h
 | 
			
		||||
    ave_speed = db.Column(db.Numeric(5, 3), nullable=True)   # km/h
 | 
			
		||||
    max_speed = db.Column(db.Numeric(5, 2), nullable=True)   # km/h
 | 
			
		||||
    ave_speed = db.Column(db.Numeric(5, 2), nullable=True)   # km/h
 | 
			
		||||
    records = db.relationship('Record',
 | 
			
		||||
                              lazy=True,
 | 
			
		||||
                              backref=db.backref('activities', lazy='joined'))
 | 
			
		||||
@@ -125,8 +126,7 @@ class Record(db.Model):
 | 
			
		||||
        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)
 | 
			
		||||
    _value = db.Column("value", db.Integer, nullable=True)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return str(self.sports.label) + \
 | 
			
		||||
@@ -140,7 +140,36 @@ class Record(db.Model):
 | 
			
		||||
        self.record_type = record_type
 | 
			
		||||
        self.activity_date = activity.activity_date
 | 
			
		||||
 | 
			
		||||
    @hybrid_property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        if self._value is None:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        if self.record_type == 'LD':
 | 
			
		||||
            return datetime.timedelta(seconds=self._value)
 | 
			
		||||
        elif self.record_type in ['AS', 'MS']:
 | 
			
		||||
            return float(self._value / 100)
 | 
			
		||||
        else:  # 'FD'
 | 
			
		||||
            return float(self._value / 1000)
 | 
			
		||||
 | 
			
		||||
    @value.setter
 | 
			
		||||
    def value(self, val):
 | 
			
		||||
        if self.record_type == 'LD':
 | 
			
		||||
            hours, minutes, seconds = str(val).split(':')
 | 
			
		||||
            self._value = int(hours) * 3600 + int(minutes) * 60 + int(seconds)
 | 
			
		||||
        elif self.record_type in ['AS', 'MS']:
 | 
			
		||||
            self._value = int(val * 100)
 | 
			
		||||
        else:  # 'FD'
 | 
			
		||||
            self._value = int(val * 1000)
 | 
			
		||||
 | 
			
		||||
    def serialize(self):
 | 
			
		||||
        if self.value is None:
 | 
			
		||||
            value = None
 | 
			
		||||
        elif self.record_type in ['AS', 'FD', 'MS']:
 | 
			
		||||
            value = float(self.value)
 | 
			
		||||
        else:  # 'LD'
 | 
			
		||||
            value = str(self.value)
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            "id": self.id,
 | 
			
		||||
            "user_id": self.user_id,
 | 
			
		||||
@@ -148,8 +177,5 @@ class Record(db.Model):
 | 
			
		||||
            "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,
 | 
			
		||||
            "value": value,
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -42,11 +42,11 @@ def test_add_an_activity_gpx(app):
 | 
			
		||||
    assert 1 == data['data']['activities'][0]['sport_id']
 | 
			
		||||
    assert '0:04:10' == data['data']['activities'][0]['duration']
 | 
			
		||||
    assert data['data']['activities'][0]['ascent'] == 0.4
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 4.602
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 4.6
 | 
			
		||||
    assert data['data']['activities'][0]['descent'] == 23.4
 | 
			
		||||
    assert data['data']['activities'][0]['distance'] == 0.32
 | 
			
		||||
    assert data['data']['activities'][0]['max_alt'] == 998.0
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 5.086
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 5.09
 | 
			
		||||
    assert data['data']['activities'][0]['min_alt'] == 975.0
 | 
			
		||||
    assert data['data']['activities'][0]['moving'] == '0:04:10'
 | 
			
		||||
    assert data['data']['activities'][0]['pauses'] is None
 | 
			
		||||
@@ -99,11 +99,11 @@ def test_get_an_activity_with_gpx(app):
 | 
			
		||||
    assert 1 == data['data']['activities'][0]['sport_id']
 | 
			
		||||
    assert '0:04:10' == data['data']['activities'][0]['duration']
 | 
			
		||||
    assert data['data']['activities'][0]['ascent'] == 0.4
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 4.602
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 4.6
 | 
			
		||||
    assert data['data']['activities'][0]['descent'] == 23.4
 | 
			
		||||
    assert data['data']['activities'][0]['distance'] == 0.32
 | 
			
		||||
    assert data['data']['activities'][0]['max_alt'] == 998.0
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 5.086
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 5.09
 | 
			
		||||
    assert data['data']['activities'][0]['min_alt'] == 975.0
 | 
			
		||||
    assert data['data']['activities'][0]['moving'] == '0:04:10'
 | 
			
		||||
    assert data['data']['activities'][0]['pauses'] is None
 | 
			
		||||
 
 | 
			
		||||
@@ -58,11 +58,11 @@ def test_edit_an_activity_with_gpx(app):
 | 
			
		||||
    assert 2 == data['data']['activities'][0]['sport_id']
 | 
			
		||||
    assert '0:04:10' == data['data']['activities'][0]['duration']
 | 
			
		||||
    assert data['data']['activities'][0]['ascent'] == 0.4
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 4.602
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 4.6
 | 
			
		||||
    assert data['data']['activities'][0]['descent'] == 23.4
 | 
			
		||||
    assert data['data']['activities'][0]['distance'] == 0.32
 | 
			
		||||
    assert data['data']['activities'][0]['max_alt'] == 998.0
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 5.086
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 5.09
 | 
			
		||||
    assert data['data']['activities'][0]['min_alt'] == 975.0
 | 
			
		||||
    assert data['data']['activities'][0]['moving'] == '0:04:10'
 | 
			
		||||
    assert data['data']['activities'][0]['pauses'] is None
 | 
			
		||||
@@ -263,11 +263,11 @@ def test_edit_an_activity_wo_gpx_partial(app):
 | 
			
		||||
    assert data['data']['activities'][0]['sport_id'] == 1
 | 
			
		||||
    assert data['data']['activities'][0]['duration'] == '0:17:04'
 | 
			
		||||
    assert data['data']['activities'][0]['ascent'] is None
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 35.156
 | 
			
		||||
    assert data['data']['activities'][0]['ave_speed'] == 35.16
 | 
			
		||||
    assert data['data']['activities'][0]['descent'] is None
 | 
			
		||||
    assert data['data']['activities'][0]['distance'] == 10.0
 | 
			
		||||
    assert data['data']['activities'][0]['max_alt'] is None
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 35.156
 | 
			
		||||
    assert data['data']['activities'][0]['max_speed'] == 35.16
 | 
			
		||||
    assert data['data']['activities'][0]['min_alt'] is None
 | 
			
		||||
    assert data['data']['activities'][0]['moving'] is None  # no calculated
 | 
			
		||||
    assert data['data']['activities'][0]['pauses'] is None
 | 
			
		||||
 
 | 
			
		||||
@@ -65,13 +65,11 @@ def test_get_all_activities_for_authenticated_user(app):
 | 
			
		||||
    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 'value' 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]
 | 
			
		||||
    assert 'value' in data['data']['records'][1]
 | 
			
		||||
 
 | 
			
		||||
@@ -23,3 +23,148 @@ def test_add_record(app):
 | 
			
		||||
    assert 'LD' == record.record_type
 | 
			
		||||
    assert '2018-01-01 13:36:00' == str(record.activity_date)
 | 
			
		||||
    assert 'cycling - LD - 2018-01-01' == str(record)
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert 'id' in record_serialize
 | 
			
		||||
    assert 'user_id' in record_serialize
 | 
			
		||||
    assert 'sport_id' in record_serialize
 | 
			
		||||
    assert 'activity_id' in record_serialize
 | 
			
		||||
    assert 'record_type' in record_serialize
 | 
			
		||||
    assert 'activity_date' in record_serialize
 | 
			
		||||
    assert 'value' in record_serialize
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add_records_no_value(app):
 | 
			
		||||
    add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    add_sport('cycling')
 | 
			
		||||
 | 
			
		||||
    activity = add_activity(
 | 
			
		||||
        user_id=1,
 | 
			
		||||
        sport_id=1,
 | 
			
		||||
        activity_date=datetime.datetime.strptime('01/01/2018 13:36', '%d/%m/%Y %H:%M'),  # noqa
 | 
			
		||||
        distance=10,
 | 
			
		||||
        duration=datetime.timedelta(seconds=1024)
 | 
			
		||||
    )
 | 
			
		||||
    record = add_record(1, 1, activity, 'AS')
 | 
			
		||||
 | 
			
		||||
    assert record.value is None
 | 
			
		||||
    assert record._value is None
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert record_serialize.get('value') is None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add_as_records(app):
 | 
			
		||||
    add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    add_sport('cycling')
 | 
			
		||||
 | 
			
		||||
    activity = add_activity(
 | 
			
		||||
        user_id=1,
 | 
			
		||||
        sport_id=1,
 | 
			
		||||
        activity_date=datetime.datetime.strptime('01/01/2018 13:36', '%d/%m/%Y %H:%M'),  # noqa
 | 
			
		||||
        distance=10,
 | 
			
		||||
        duration=datetime.timedelta(seconds=1024)
 | 
			
		||||
    )
 | 
			
		||||
    record = add_record(1, 1, activity, 'AS')
 | 
			
		||||
    # record.value = 4.6
 | 
			
		||||
    record.value = 4.61
 | 
			
		||||
 | 
			
		||||
    assert isinstance(record.value, float)
 | 
			
		||||
    assert record.value == 4.61
 | 
			
		||||
    assert record._value == 461
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert record_serialize.get('value') == 4.61
 | 
			
		||||
    assert isinstance(record_serialize.get('value'), float)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add_fd_records(app):
 | 
			
		||||
    add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    add_sport('cycling')
 | 
			
		||||
 | 
			
		||||
    activity = add_activity(
 | 
			
		||||
        user_id=1,
 | 
			
		||||
        sport_id=1,
 | 
			
		||||
        activity_date=datetime.datetime.strptime('01/01/2018 13:36', '%d/%m/%Y %H:%M'),  # noqa
 | 
			
		||||
        distance=10,
 | 
			
		||||
        duration=datetime.timedelta(seconds=1024)
 | 
			
		||||
    )
 | 
			
		||||
    record = add_record(1, 1, activity, 'FD')
 | 
			
		||||
    record.value = 0.322
 | 
			
		||||
 | 
			
		||||
    assert isinstance(record.value, float)
 | 
			
		||||
    assert record.value == 0.322
 | 
			
		||||
    assert record._value == 322
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert record_serialize.get('value') == 0.322
 | 
			
		||||
    assert isinstance(record_serialize.get('value'), float)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add_ld_records(app):
 | 
			
		||||
    add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    add_sport('cycling')
 | 
			
		||||
 | 
			
		||||
    activity = add_activity(
 | 
			
		||||
        user_id=1,
 | 
			
		||||
        sport_id=1,
 | 
			
		||||
        activity_date=datetime.datetime.strptime('01/01/2018 13:36', '%d/%m/%Y %H:%M'),  # noqa
 | 
			
		||||
        distance=10,
 | 
			
		||||
        duration=datetime.timedelta(seconds=1024)
 | 
			
		||||
    )
 | 
			
		||||
    record = add_record(1, 1, activity, 'LD')
 | 
			
		||||
    record.value = activity.duration
 | 
			
		||||
 | 
			
		||||
    assert isinstance(record.value, datetime.timedelta)
 | 
			
		||||
    assert str(record.value) == '0:17:04'
 | 
			
		||||
    assert record._value == 1024
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert record_serialize.get('value') == '0:17:04'
 | 
			
		||||
    assert isinstance(record_serialize.get('value'), str)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add_ld_records_zero(app):
 | 
			
		||||
    add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    add_sport('cycling')
 | 
			
		||||
 | 
			
		||||
    activity = add_activity(
 | 
			
		||||
        user_id=1,
 | 
			
		||||
        sport_id=1,
 | 
			
		||||
        activity_date=datetime.datetime.strptime('01/01/2018 13:36', '%d/%m/%Y %H:%M'),  # noqa
 | 
			
		||||
        distance=10,
 | 
			
		||||
        duration=datetime.timedelta(seconds=0)
 | 
			
		||||
    )
 | 
			
		||||
    record = add_record(1, 1, activity, 'LD')
 | 
			
		||||
    record.value = activity.duration
 | 
			
		||||
 | 
			
		||||
    assert isinstance(record.value, datetime.timedelta)
 | 
			
		||||
    assert str(record.value) == '0:00:00'
 | 
			
		||||
    assert record._value == 0
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert record_serialize.get('value') == '0:00:00'
 | 
			
		||||
    assert isinstance(record_serialize.get('value'), str)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add_ms_records_no_value(app):
 | 
			
		||||
    add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    add_sport('cycling')
 | 
			
		||||
 | 
			
		||||
    activity = add_activity(
 | 
			
		||||
        user_id=1,
 | 
			
		||||
        sport_id=1,
 | 
			
		||||
        activity_date=datetime.datetime.strptime('01/01/2018 13:36', '%d/%m/%Y %H:%M'),  # noqa
 | 
			
		||||
        distance=10,
 | 
			
		||||
        duration=datetime.timedelta(seconds=1024)
 | 
			
		||||
    )
 | 
			
		||||
    record = add_record(1, 1, activity, 'MS')
 | 
			
		||||
    record.value = 23.5
 | 
			
		||||
 | 
			
		||||
    assert isinstance(record.value, float)
 | 
			
		||||
    assert record.value == 23.5
 | 
			
		||||
    assert record._value == 2350
 | 
			
		||||
 | 
			
		||||
    record_serialize = record.serialize()
 | 
			
		||||
    assert record_serialize.get('value') == 23.5
 | 
			
		||||
    assert isinstance(record_serialize.get('value'), float)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,4 +4,3 @@ from mpwo_api.tests.utils import add_user
 | 
			
		||||
def test_add_user(app):
 | 
			
		||||
    user = add_user('test', 'test@test.com', '12345678')
 | 
			
		||||
    assert '<User \'test\'>' == str(user)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user