API & Client: filter activities - fix #3
This commit is contained in:
		@@ -10,9 +10,10 @@ from sqlalchemy import exc
 | 
				
			|||||||
from ..users.utils import authenticate, verify_extension
 | 
					from ..users.utils import authenticate, verify_extension
 | 
				
			||||||
from .models import Activity
 | 
					from .models import Activity
 | 
				
			||||||
from .utils import (
 | 
					from .utils import (
 | 
				
			||||||
    ActivityException, convert_in_duration, create_activity, edit_activity,
 | 
					    ActivityException, create_activity, edit_activity, get_chart_data,
 | 
				
			||||||
    get_chart_data, process_files
 | 
					    process_files
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					from .utils_format import convert_in_duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
activities_blueprint = Blueprint('activities', __name__)
 | 
					activities_blueprint = Blueprint('activities', __name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,7 +63,8 @@ def get_activities(auth_user_id):
 | 
				
			|||||||
        response_object = {
 | 
					        response_object = {
 | 
				
			||||||
            'status': 'success',
 | 
					            'status': 'success',
 | 
				
			||||||
            'data': {
 | 
					            'data': {
 | 
				
			||||||
                'activities': [activity.serialize() for activity in activities]
 | 
					                'activities': [activity.serialize(params)
 | 
				
			||||||
 | 
					                               for activity in activities]
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        code = 200
 | 
					        code = 200
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,8 @@ from sqlalchemy.ext.hybrid import hybrid_property
 | 
				
			|||||||
from sqlalchemy.orm.session import object_session
 | 
					from sqlalchemy.orm.session import object_session
 | 
				
			||||||
from sqlalchemy.types import Enum
 | 
					from sqlalchemy.types import Enum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .utils_format import convert_in_duration, convert_value_to_integer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
record_types = [
 | 
					record_types = [
 | 
				
			||||||
    'AS',  # 'Best Average Speed'
 | 
					    'AS',  # 'Best Average Speed'
 | 
				
			||||||
    'FD',  # 'Farthest Distance'
 | 
					    'FD',  # 'Farthest Distance'
 | 
				
			||||||
@@ -16,23 +18,6 @@ record_types = [
 | 
				
			|||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def convert_timedelta_to_integer(value):
 | 
					 | 
				
			||||||
    hours, minutes, seconds = str(value).split(':')
 | 
					 | 
				
			||||||
    return int(hours) * 3600 + int(minutes) * 60 + int(seconds)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def convert_value_to_integer(record_type, val):
 | 
					 | 
				
			||||||
    if val is None:
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if record_type == 'LD':
 | 
					 | 
				
			||||||
        return convert_timedelta_to_integer(val)
 | 
					 | 
				
			||||||
    elif record_type in ['AS', 'MS']:
 | 
					 | 
				
			||||||
        return int(val * 100)
 | 
					 | 
				
			||||||
    else:  # 'FD'
 | 
					 | 
				
			||||||
        return int(val * 1000)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def update_records(user_id, sport_id, connection, session):
 | 
					def update_records(user_id, sport_id, connection, session):
 | 
				
			||||||
    record_table = Record.__table__
 | 
					    record_table = Record.__table__
 | 
				
			||||||
    new_records = Activity.get_user_activity_records(
 | 
					    new_records = Activity.get_user_activity_records(
 | 
				
			||||||
@@ -161,18 +146,61 @@ class Activity(db.Model):
 | 
				
			|||||||
        self.distance = distance
 | 
					        self.distance = distance
 | 
				
			||||||
        self.duration = duration
 | 
					        self.duration = duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def serialize(self):
 | 
					    def serialize(self, params=None):
 | 
				
			||||||
 | 
					        date_from = params.get('from') if params else None
 | 
				
			||||||
 | 
					        date_to = params.get('to') if params else None
 | 
				
			||||||
 | 
					        distance_from = params.get('distance_from') if params else None
 | 
				
			||||||
 | 
					        distance_to = params.get('distance_to') if params else None
 | 
				
			||||||
 | 
					        duration_from = params.get('duration_from') if params else None
 | 
				
			||||||
 | 
					        duration_to = params.get('duration_to') if params else None
 | 
				
			||||||
 | 
					        ave_speed_from = params.get('ave_speed_from') if params else None
 | 
				
			||||||
 | 
					        ave_speed_to = params.get('ave_speed_to') if params else None
 | 
				
			||||||
 | 
					        sport_id = params.get('sport_id') if params else None
 | 
				
			||||||
        previous_activity = Activity.query.filter(
 | 
					        previous_activity = Activity.query.filter(
 | 
				
			||||||
            Activity.id != self.id,
 | 
					            Activity.id != self.id,
 | 
				
			||||||
            Activity.user_id == self.user_id,
 | 
					            Activity.user_id == self.user_id,
 | 
				
			||||||
            Activity.activity_date <= self.activity_date
 | 
					            Activity.sport_id == sport_id if sport_id else True,
 | 
				
			||||||
 | 
					            Activity.activity_date <= self.activity_date,
 | 
				
			||||||
 | 
					            Activity.activity_date >= datetime.datetime.strptime(
 | 
				
			||||||
 | 
					                date_from, '%Y-%m-%d'
 | 
				
			||||||
 | 
					            ) if date_from else True,
 | 
				
			||||||
 | 
					            Activity.activity_date <= datetime.datetime.strptime(
 | 
				
			||||||
 | 
					                date_to, '%Y-%m-%d'
 | 
				
			||||||
 | 
					            ) if date_to else True,
 | 
				
			||||||
 | 
					            Activity.distance >= int(distance_from) if distance_from else True,
 | 
				
			||||||
 | 
					            Activity.distance <= int(distance_to) if distance_to else True,
 | 
				
			||||||
 | 
					            Activity.duration >= convert_in_duration(duration_from)
 | 
				
			||||||
 | 
					            if duration_from else True,
 | 
				
			||||||
 | 
					            Activity.duration <= convert_in_duration(duration_to)
 | 
				
			||||||
 | 
					            if duration_to else True,
 | 
				
			||||||
 | 
					            Activity.ave_speed >= float(ave_speed_from)
 | 
				
			||||||
 | 
					            if ave_speed_from else True,
 | 
				
			||||||
 | 
					            Activity.ave_speed <= float(ave_speed_to)
 | 
				
			||||||
 | 
					            if ave_speed_to else True,
 | 
				
			||||||
        ).order_by(
 | 
					        ).order_by(
 | 
				
			||||||
            Activity.activity_date.desc()
 | 
					            Activity.activity_date.desc()
 | 
				
			||||||
        ).first()
 | 
					        ).first()
 | 
				
			||||||
        next_activity = Activity.query.filter(
 | 
					        next_activity = Activity.query.filter(
 | 
				
			||||||
            Activity.id != self.id,
 | 
					            Activity.id != self.id,
 | 
				
			||||||
            Activity.user_id == self.user_id,
 | 
					            Activity.user_id == self.user_id,
 | 
				
			||||||
            Activity.activity_date >= self.activity_date
 | 
					            Activity.sport_id == sport_id if sport_id else True,
 | 
				
			||||||
 | 
					            Activity.activity_date >= self.activity_date,
 | 
				
			||||||
 | 
					            Activity.activity_date >= datetime.datetime.strptime(
 | 
				
			||||||
 | 
					                date_from, '%Y-%m-%d'
 | 
				
			||||||
 | 
					            ) if date_from else True,
 | 
				
			||||||
 | 
					            Activity.activity_date <= datetime.datetime.strptime(
 | 
				
			||||||
 | 
					                date_to, '%Y-%m-%d'
 | 
				
			||||||
 | 
					            ) if date_to else True,
 | 
				
			||||||
 | 
					            Activity.distance >= int(distance_from) if distance_from else True,
 | 
				
			||||||
 | 
					            Activity.distance <= int(distance_to) if distance_to else True,
 | 
				
			||||||
 | 
					            Activity.duration >= convert_in_duration(duration_from)
 | 
				
			||||||
 | 
					            if duration_from else True,
 | 
				
			||||||
 | 
					            Activity.duration <= convert_in_duration(duration_to)
 | 
				
			||||||
 | 
					            if duration_to else True,
 | 
				
			||||||
 | 
					            Activity.ave_speed >= float(ave_speed_from)
 | 
				
			||||||
 | 
					            if ave_speed_from else True,
 | 
				
			||||||
 | 
					            Activity.ave_speed <= float(ave_speed_to)
 | 
				
			||||||
 | 
					            if ave_speed_to else True,
 | 
				
			||||||
        ).order_by(
 | 
					        ).order_by(
 | 
				
			||||||
            Activity.activity_date.asc()
 | 
					            Activity.activity_date.asc()
 | 
				
			||||||
        ).first()
 | 
					        ).first()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,8 @@ from flask import Blueprint, jsonify, request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from ..users.models import User
 | 
					from ..users.models import User
 | 
				
			||||||
from ..users.utils import authenticate
 | 
					from ..users.utils import authenticate
 | 
				
			||||||
from .models import Activity, Sport, convert_timedelta_to_integer
 | 
					from .models import Activity, Sport
 | 
				
			||||||
 | 
					from .utils_format import convert_timedelta_to_integer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
stats_blueprint = Blueprint('stats', __name__)
 | 
					stats_blueprint = Blueprint('stats', __name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -360,9 +360,3 @@ def process_files(auth_user_id, activity_data, activity_file, folders):
 | 
				
			|||||||
        return [process_one_gpx_file(common_params, filename)]
 | 
					        return [process_one_gpx_file(common_params, filename)]
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return process_zip_archive(common_params, folders['extract_dir'])
 | 
					        return process_zip_archive(common_params, folders['extract_dir'])
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def convert_in_duration(value):
 | 
					 | 
				
			||||||
    hours = int(value.split(':')[0])
 | 
					 | 
				
			||||||
    minutes = int(value.split(':')[1])
 | 
					 | 
				
			||||||
    return timedelta(seconds=(hours*3600 + minutes*60))
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								fittrackee_api/fittrackee_api/activities/utils_format.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								fittrackee_api/fittrackee_api/activities/utils_format.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					from datetime import timedelta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_in_duration(value):
 | 
				
			||||||
 | 
					    hours = int(value.split(':')[0])
 | 
				
			||||||
 | 
					    minutes = int(value.split(':')[1])
 | 
				
			||||||
 | 
					    return timedelta(seconds=(hours*3600 + minutes*60))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_timedelta_to_integer(value):
 | 
				
			||||||
 | 
					    hours, minutes, seconds = str(value).split(':')
 | 
				
			||||||
 | 
					    return int(hours) * 3600 + int(minutes) * 60 + int(seconds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_value_to_integer(record_type, val):
 | 
				
			||||||
 | 
					    if val is None:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if record_type == 'LD':
 | 
				
			||||||
 | 
					        return convert_timedelta_to_integer(val)
 | 
				
			||||||
 | 
					    elif record_type in ['AS', 'MS']:
 | 
				
			||||||
 | 
					        return int(val * 100)
 | 
				
			||||||
 | 
					    else:  # 'FD'
 | 
				
			||||||
 | 
					        return int(val * 1000)
 | 
				
			||||||
@@ -6,7 +6,7 @@ export default function ActivitiesList (props) {
 | 
				
			|||||||
  const { activities, sports } = props
 | 
					  const { activities, sports } = props
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="card">
 | 
					    <div className="card">
 | 
				
			||||||
      <div className="card-bord">
 | 
					      <div className="card-body">
 | 
				
			||||||
        <table className="table">
 | 
					        <table className="table">
 | 
				
			||||||
          <thead>
 | 
					          <thead>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import React from 'react'
 | 
					import React from 'react'
 | 
				
			||||||
import { Helmet } from 'react-helmet'
 | 
					import { Helmet } from 'react-helmet'
 | 
				
			||||||
import { connect } from 'react-redux'
 | 
					import { connect } from 'react-redux'
 | 
				
			||||||
 | 
					import { Link } from 'react-router-dom'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ActivitiesFilter from './ActivitiesFilter'
 | 
					import ActivitiesFilter from './ActivitiesFilter'
 | 
				
			||||||
import ActivitiesList from './ActivitiesList'
 | 
					import ActivitiesList from './ActivitiesList'
 | 
				
			||||||
@@ -75,6 +76,16 @@ class Activities extends React.Component {
 | 
				
			|||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                {activities.length === 0 && (
 | 
				
			||||||
 | 
					                  <div className="card text-center">
 | 
				
			||||||
 | 
					                    <div className="card-body">
 | 
				
			||||||
 | 
					                      No workouts. {' '}
 | 
				
			||||||
 | 
					                      <Link to={{ pathname: '/activities/add' }}>
 | 
				
			||||||
 | 
					                        Upload one !
 | 
				
			||||||
 | 
					                      </Link>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,7 @@ function NavBar(props) {
 | 
				
			|||||||
                      pathname: '/activities/add',
 | 
					                      pathname: '/activities/add',
 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    Add workout
 | 
					                    <strong>Add workout</strong>
 | 
				
			||||||
                  </Link>
 | 
					                  </Link>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user