Exemplo n.º 1
0
def login_user():
    # get post data
    post_data = request.get_json()
    if not post_data:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400
    email = post_data.get('email')
    password = post_data.get('password')
    try:
        # check for existing user
        user = User.query.filter(User.email == email).first()
        if user and bcrypt.check_password_hash(user.password, password):
            # generate auth token
            auth_token = user.encode_auth_token(user.id)
            response_object = {
                'status': 'success',
                'message': 'Successfully logged in.',
                'auth_token': auth_token.decode()
            }
            return jsonify(response_object), 200
        else:
            response_object = {
                'status': 'error',
                'message': 'Invalid credentials.'
            }
            return jsonify(response_object), 404
    # handler errors
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        return jsonify(response_object), 500
Exemplo n.º 2
0
def post_activity_no_gpx(auth_user_id):
    """Post an activity without gpx file"""
    activity_data = request.get_json()
    if not activity_data or activity_data.get('sport_id') is None \
            or activity_data.get('duration') is None \
            or activity_data.get('distance') is None \
            or activity_data.get('activity_date') is None:
        response_object = {
            'status': 'error',
            'message': 'Invalid payload.'
        }
        return jsonify(response_object), 400

    try:
        user = User.query.filter_by(id=auth_user_id).first()
        new_activity = create_activity(user, activity_data)
        db.session.add(new_activity)
        db.session.commit()

        response_object = {
            'status': 'created',
            'data': {
                'activities': [new_activity.serialize()]
            }
        }
        return jsonify(response_object), 201

    except (exc.IntegrityError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'fail',
            'message': 'Error during activity save.'
        }
        return jsonify(response_object), 500
Exemplo n.º 3
0
def delete_activity(auth_user_id, activity_id):
    """Delete an activity"""
    try:
        activity = Activity.query.filter_by(id=activity_id).first()
        if activity:
            response_object, code = can_view_activity(
                auth_user_id, activity.user_id)
            if response_object:
                return jsonify(response_object), code

            db.session.delete(activity)
            db.session.commit()
            response_object = {
                'status': 'no content'
            }
            code = 204
        else:
            response_object = {
                'status': 'not found',
                'data': {
                    'activities': []
                }
            }
            code = 404
    except (exc.IntegrityError, exc.OperationalError, ValueError, OSError) \
            as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 4
0
def post_sport(auth_user_id):
    """Post a sport"""
    sport_data = request.get_json()
    if not sport_data or sport_data.get('label') is None:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400

    try:
        new_sport = Sport(label=sport_data.get('label'))
        db.session.add(new_sport)
        db.session.commit()
        response_object = {
            'status': 'created',
            'data': {'sports': [new_sport.serialize()]},
        }
        code = 201
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 5
0
def update_sport(auth_user_id, sport_id):
    """Update a sport"""
    sport_data = request.get_json()
    if not sport_data or sport_data.get('label') is None:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400

    sports_list = []
    try:
        sport = Sport.query.filter_by(id=sport_id).first()
        if sport:
            sport.label = sport_data.get('label')
            db.session.commit()
            sports_list.append({'id': sport.id, 'label': sport.label})
            response_object = {
                'status': 'success',
                'data': {'sports': sports_list},
            }
            code = 200
        else:
            response_object = {
                'status': 'not found',
                'data': {'sports': sports_list},
            }
            code = 404
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 6
0
def process_zip_archive(common_params, extract_dir):
    with zipfile.ZipFile(common_params['file_path'], "r") as zip_ref:
        zip_ref.extractall(extract_dir)

    new_activities = []
    gpx_files_limit = os.getenv('REACT_APP_GPX_LIMIT_IMPORT', '10')
    if gpx_files_limit and gpx_files_limit.isdigit():
        gpx_files_limit = int(gpx_files_limit)
    else:
        gpx_files_limit = 10
        appLog.error('GPX limit not configured, set to 10.')
    gpx_files_ok = 0

    for gpx_file in os.listdir(extract_dir):
        if '.' in gpx_file and gpx_file.rsplit('.', 1)[1].lower(
        ) in current_app.config.get('ACTIVITY_ALLOWED_EXTENSIONS'):
            gpx_files_ok += 1
            if gpx_files_ok > gpx_files_limit:
                break
            file_path = os.path.join(extract_dir, gpx_file)
            params = common_params
            params['file_path'] = file_path
            new_activity = process_one_gpx_file(params, gpx_file)
            new_activities.append(new_activity)

    return new_activities
Exemplo n.º 7
0
def delete_sport(auth_user_id, sport_id):
    """Delete a sport"""
    try:
        sport = Sport.query.filter_by(id=sport_id).first()
        if sport:
            db.session.delete(sport)
            db.session.commit()
            response_object = {'status': 'no content'}
            code = 204
        else:
            response_object = {'status': 'not found', 'data': {'sports': []}}
            code = 404
    except exc.IntegrityError as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Associated activities exist.',
        }
        code = 500
    except (exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 8
0
def post_activity(auth_user_id):
    """Post an activity (with gpx file)"""
    response_object = verify_extension('activity', request)
    if response_object['status'] != 'success':
        return jsonify(response_object), 400

    activity_data = json.loads(request.form["data"])
    if not activity_data or activity_data.get('sport_id') is None:
        response_object = {
            'status': 'error',
            'message': 'Invalid payload.'
        }
        return jsonify(response_object), 400

    activity_file = request.files['file']
    upload_dir = os.path.join(
        current_app.config['UPLOAD_FOLDER'],
        'activities',
        str(auth_user_id))
    folders = {
        'extract_dir': os.path.join(upload_dir, 'extract'),
        'tmp_dir': os.path.join(upload_dir, 'tmp'),
    }

    try:
        new_activities = process_files(
            auth_user_id, activity_data, activity_file, folders
        )
        if len(new_activities) > 0:
            response_object = {
                'status': 'created',
                'data': {
                    'activities': [new_activity.serialize()
                                   for new_activity in new_activities]
                }
            }
            code = 201
        else:
            response_object = {
                'status': 'fail',
                'data': {
                    'activities': []
                }
            }
            code = 400
    except ActivityException as e:
        db.session.rollback()
        if e.e:
            appLog.error(e.e)
        response_object = {
            'status': e.status,
            'message': e.message,
        }
        code = 500 if e.status == 'error' else 400

    shutil.rmtree(folders['extract_dir'], ignore_errors=True)
    shutil.rmtree(folders['tmp_dir'], ignore_errors=True)
    return jsonify(response_object), code
Exemplo n.º 9
0
def edit_user(user_id):
    # get post data
    post_data = request.get_json()
    if not post_data:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400
    first_name = post_data.get('first_name')
    last_name = post_data.get('last_name')
    bio = post_data.get('bio')
    birth_date = post_data.get('birth_date')
    location = post_data.get('location')
    password = post_data.get('password')
    password_conf = post_data.get('password_conf')
    timezone = post_data.get('timezone')

    if password is not None and password != '':
        if password_conf != password:
            response_object = {
                'status': 'error',
                'message': 'Password and password confirmation don\'t match.\n'
            }
            return jsonify(response_object), 400
        else:
            password = bcrypt.generate_password_hash(
                password,
                current_app.config.get('BCRYPT_LOG_ROUNDS')).decode()

    try:
        user = User.query.filter_by(id=user_id).first()
        user.first_name = first_name
        user.last_name = last_name
        user.bio = bio
        user.location = location
        user.birth_date = (datetime.datetime.strptime(birth_date, '%Y-%m-%d')
                           if birth_date else None)
        if password is not None and password != '':
            user.password = password
        user.timezone = timezone
        db.session.commit()

        response_object = {
            'status': 'success',
            'message': 'User profile updated.'
        }
        return jsonify(response_object), 200

    # handler errors
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        return jsonify(response_object), 500
Exemplo n.º 10
0
def get_activity_data(auth_user_id, activity_id, data_type, segment_id=None):
    """Get data from an activity gpx file"""
    activity = Activity.query.filter_by(id=activity_id).first()
    content = ''
    if activity:
        response_object, code = can_view_activity(auth_user_id,
                                                  activity.user_id)
        if response_object:
            return jsonify(response_object), code
        if not activity.gpx or activity.gpx == '':
            message = f'No gpx file for this activity (id: {activity_id})'
            response_object = {'status': 'fail', 'message': message}
            return jsonify(response_object), 400

        try:
            absolute_gpx_filepath = get_absolute_file_path(activity.gpx)
            if data_type == 'chart':
                content = get_chart_data(absolute_gpx_filepath, segment_id)
            else:  # data_type == 'gpx'
                with open(absolute_gpx_filepath, encoding='utf-8') as f:
                    content = f.read()
                    if segment_id is not None:
                        content = extract_segment_from_gpx_file(
                            content, segment_id)
        except ActivityGPXException as e:
            appLog.error(e.message)
            response_object = {'status': e.status, 'message': e.message}
            code = 404 if e.status == 'not found' else 500
            return jsonify(response_object), code
        except Exception as e:
            appLog.error(e)
            response_object = {'status': 'error', 'message': 'internal error'}
            return jsonify(response_object), 500

        status = 'success'
        message = ''
        code = 200
    else:
        status = 'not found'
        message = f'Activity not found (id: {activity_id})'
        code = 404

    response_object = {
        'status':
        status,
        'message':
        message,
        'data': ({
            'chart_data': content
        } if data_type == 'chart' else {
            'gpx': content
        }),
    }
    return jsonify(response_object), code
Exemplo n.º 11
0
def del_picture(user_id):
    """
    delete authenticated user picture

    **Example request**:

    .. sourcecode:: http

      DELETE /api/auth/picture HTTP/1.1
      Content-Type: application/json

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 204 NO CONTENT
      Content-Type: application/json

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 204: picture deleted
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 500: Error during picture deletion.

    """
    try:
        user = User.query.filter_by(id=user_id).first()
        picture_path = get_absolute_file_path(user.picture)
        if os.path.isfile(picture_path):
            os.remove(picture_path)
        user.picture = None
        db.session.commit()

        response_object = {'status': 'no content'}
        return jsonify(response_object), 204

    except (exc.IntegrityError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'fail',
            'message': 'Error during picture deletion.',
        }
        return jsonify(response_object), 500
Exemplo n.º 12
0
def get_map(map_id):
    """
    Get map image for activities with gpx

    **Example request**:

    .. sourcecode:: http

      GET /api/activities/map/fa33f4d996844a5c73ecd1ae24456ab8?1563529507772
        HTTP/1.1
      Content-Type: application/json

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: image/png

    :param string map_id: activity map id

    :statuscode 200: success
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 404: map does not exist
    :statuscode 500:

    """
    try:
        activity = Activity.query.filter_by(map_id=map_id).first()
        if not activity:
            response_object = {
                'status': 'fail',
                'message': 'Map does not exist',
            }
            return jsonify(response_object), 404
        else:
            absolute_map_filepath = get_absolute_file_path(activity.map)
            return send_file(absolute_map_filepath)
    except Exception as e:
        appLog.error(e)
        response_object = {'status': 'error', 'message': 'internal error.'}
        return jsonify(response_object), 500
Exemplo n.º 13
0
def update_activity(auth_user_id, activity_id):
    """Update an activity"""
    activity_data = request.get_json()
    if not activity_data:
        response_object = {
            'status': 'error',
            'message': 'Invalid payload.'
        }
        return jsonify(response_object), 400

    try:
        activity = Activity.query.filter_by(id=activity_id).first()
        if activity:
            response_object, code = can_view_activity(
                auth_user_id, activity.user_id)
            if response_object:
                return jsonify(response_object), code

            activity = edit_activity(activity, activity_data, auth_user_id)
            db.session.commit()
            response_object = {
                'status': 'success',
                'data': {
                    'activities': [activity.serialize()]
                }
            }
            code = 200
        else:
            response_object = {
                'status': 'not found',
                'data': {
                    'activities': []
                }
            }
            code = 404
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 14
0
def get_map(map_id):
    try:
        activity = Activity.query.filter_by(map_id=map_id).first()
        if not activity:
            response_object = {
                'status': 'fail',
                'message': 'Map does not exist'
            }
            return jsonify(response_object), 404
        else:
            absolute_map_filepath = get_absolute_file_path(activity.map)
            return send_file(absolute_map_filepath)
    except Exception as e:
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'internal error.'
        }
        return jsonify(response_object), 500
Exemplo n.º 15
0
def del_picture(user_id):
    try:
        user = User.query.filter_by(id=user_id).first()
        picture_path = get_absolute_file_path(user.picture)
        if os.path.isfile(picture_path):
            os.remove(picture_path)
        user.picture = None
        db.session.commit()

        response_object = {'status': 'no content'}
        return jsonify(response_object), 204

    except (exc.IntegrityError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'fail',
            'message': 'Error during picture deletion.'
        }
        return jsonify(response_object), 500
Exemplo n.º 16
0
def get_weather(point):
    if not API_KEY or API_KEY == '':
        return None
    try:
        point_time = pytz.utc.localize(point.time)
        forecast = forecastio.load_forecast(API_KEY,
                                            point.latitude,
                                            point.longitude,
                                            time=point_time,
                                            units='si')
        weather = forecast.currently()
        return {
            'summary': weather.summary,
            'icon': weather.icon,
            'temperature': weather.temperature,
            'humidity': weather.humidity,
            'wind': weather.windSpeed,
        }
    except Exception as e:
        appLog.error(e)
        return None
Exemplo n.º 17
0
def edit_picture(user_id):
    code = 400
    response_object = verify_extension('picture', request)
    if response_object['status'] != 'success':
        return jsonify(response_object), code

    file = request.files['file']
    filename = secure_filename(file.filename)
    dirpath = os.path.join(current_app.config['UPLOAD_FOLDER'], 'pictures',
                           str(user_id))
    if not os.path.exists(dirpath):
        os.makedirs(dirpath)
    absolute_picture_path = os.path.join(dirpath, filename)
    relative_picture_path = os.path.join('pictures', str(user_id), filename)

    try:
        user = User.query.filter_by(id=user_id).first()
        if user.picture is not None:
            old_picture_path = get_absolute_file_path(user.picture)
            if os.path.isfile(get_absolute_file_path(old_picture_path)):
                os.remove(old_picture_path)
        file.save(absolute_picture_path)
        user.picture = relative_picture_path
        db.session.commit()

        response_object = {
            'status': 'success',
            'message': 'User picture updated.'
        }
        return jsonify(response_object), 200

    except (exc.IntegrityError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'fail',
            'message': 'Error during picture update.'
        }
        return jsonify(response_object), 500
Exemplo n.º 18
0
def post_activity(auth_user_id):
    """
    Post an activity with a gpx file

    **Example request**:

    .. sourcecode:: http

      POST /api/activities/ HTTP/1.1
      Content-Type: multipart/form-data

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 201 CREATED
      Content-Type: application/json

       {
          "data": {
            "activities": [
              {
                "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                "ascent": null,
                "ave_speed": 10.0,
                "bounds": [],
                "creation_date": "Sun, 14 Jul 2019 13:51:01 GMT",
                "descent": null,
                "distance": 10.0,
                "duration": "0:17:04",
                "id": 1,
                "map": null,
                "max_alt": null,
                "max_speed": 10.0,
                "min_alt": null,
                "modification_date": null,
                "moving": "0:17:04",
                "next_activity": 3,
                "notes": null,
                "pauses": null,
                "previous_activity": null,
                "records": [
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 4,
                    "record_type": "MS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 3,
                    "record_type": "LD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": "0:17:04"
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 2,
                    "record_type": "FD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 1,
                    "record_type": "AS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  }
                ],
                "segments": [],
                "sport_id": 1,
                "title": null,
                "user_id": 1,
                "weather_end": null,
                "weather_start": null,
                "with_gpx": false
              }
            ]
          },
          "status": "success"
        }

    :param integer auth_user_id: authenticate user id (from JSON Web Token)

    :form file: gpx file (allowed extensions: .gpx, .zip)
    :form data: sport id and notes (example: ``{"sport_id": 1, "notes": ""}``)

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 201: activity created
    :statuscode 400:
        - Invalid payload.
        - No file part.
        - No selected file.
        - File extension not allowed.
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 413: Error during picture update: file size exceeds 1.0MB.
    :statuscode 500:

    """
    response_object, response_code = verify_extension_and_size(
        'activity', request)
    if response_object['status'] != 'success':
        return jsonify(response_object), response_code

    activity_data = json.loads(request.form["data"])
    if not activity_data or activity_data.get('sport_id') is None:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400

    activity_file = request.files['file']
    upload_dir = os.path.join(current_app.config['UPLOAD_FOLDER'],
                              'activities', str(auth_user_id))
    folders = {
        'extract_dir': os.path.join(upload_dir, 'extract'),
        'tmp_dir': os.path.join(upload_dir, 'tmp'),
    }

    try:
        new_activities = process_files(auth_user_id, activity_data,
                                       activity_file, folders)
        if len(new_activities) > 0:
            response_object = {
                'status': 'created',
                'data': {
                    'activities': [
                        new_activity.serialize()
                        for new_activity in new_activities
                    ]
                },
            }
            code = 201
        else:
            response_object = {'status': 'fail', 'data': {'activities': []}}
            code = 400
    except ActivityException as e:
        db.session.rollback()
        if e.e:
            appLog.error(e.e)
        response_object = {'status': e.status, 'message': e.message}
        code = 500 if e.status == 'error' else 400

    shutil.rmtree(folders['extract_dir'], ignore_errors=True)
    shutil.rmtree(folders['tmp_dir'], ignore_errors=True)
    return jsonify(response_object), code
Exemplo n.º 19
0
def get_activities(auth_user_id):
    """Get all activities for authenticated user"""
    try:
        user = User.query.filter_by(id=auth_user_id).first()
        params = request.args.copy()
        page = 1 if 'page' not in params.keys() else int(params.get('page'))
        date_from = params.get('from')
        if date_from:
            date_from = datetime.strptime(date_from, '%Y-%m-%d')
            _, date_from = get_datetime_with_tz(user.timezone, date_from)
        date_to = params.get('to')
        if date_to:
            date_to = datetime.strptime(f'{date_to} 23:59:59',
                                        '%Y-%m-%d %H:%M:%S')
            _, date_to = get_datetime_with_tz(user.timezone, date_to)
        distance_from = params.get('distance_from')
        distance_to = params.get('distance_to')
        duration_from = params.get('duration_from')
        duration_to = params.get('duration_to')
        ave_speed_from = params.get('ave_speed_from')
        ave_speed_to = params.get('ave_speed_to')
        max_speed_from = params.get('max_speed_from')
        max_speed_to = params.get('max_speed_to')
        order = params.get('order')
        sport_id = params.get('sport_id')
        per_page = int(params.get('per_page')) if params.get('per_page') else 5
        activities = Activity.query.filter(
            Activity.user_id == auth_user_id,
            Activity.sport_id == sport_id if sport_id else True,
            Activity.activity_date >= date_from if date_from else True,
            Activity.activity_date < date_to + timedelta(seconds=1)
            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.moving >= convert_in_duration(duration_from)
            if duration_from else True,
            Activity.moving <= 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,
            Activity.max_speed >= float(max_speed_from)
            if max_speed_from else True,
            Activity.max_speed <= float(max_speed_to)
            if max_speed_to else True,
        ).order_by(
            Activity.activity_date.asc()
            if order == 'asc'
            else Activity.activity_date.desc()
        ).paginate(
            page, per_page, False
        ).items
        response_object = {
            'status': 'success',
            'data': {
                'activities': [activity.serialize(params)
                               for activity in activities]
            }
        }
        code = 200
    except Exception as e:
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 20
0
def post_activity_no_gpx(auth_user_id):
    """
    Post an activity without gpx file

    **Example request**:

    .. sourcecode:: http

      POST /api/activities/no_gpx HTTP/1.1
      Content-Type: application/json

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 201 CREATED
      Content-Type: application/json

       {
          "data": {
            "activities": [
              {
                "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                "ascent": null,
                "ave_speed": 10.0,
                "bounds": [],
                "creation_date": "Sun, 14 Jul 2019 13:51:01 GMT",
                "descent": null,
                "distance": 10.0,
                "duration": "0:17:04",
                "id": 1,
                "map": null,
                "max_alt": null,
                "max_speed": 10.0,
                "min_alt": null,
                "modification_date": null,
                "moving": "0:17:04",
                "next_activity": 3,
                "notes": null,
                "pauses": null,
                "previous_activity": null,
                "records": [
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 4,
                    "record_type": "MS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 3,
                    "record_type": "LD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": "0:17:04"
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 2,
                    "record_type": "FD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 1,
                    "record_type": "AS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  }
                ],
                "segments": [],
                "sport_id": 1,
                "title": null,
                "user_id": 1,
                "weather_end": null,
                "weather_start": null,
                "with_gpx": false
              }
            ]
          },
          "status": "success"
        }

    :param integer auth_user_id: authenticate user id (from JSON Web Token)

    :<json string activity_date: activity date  (format: ``%Y-%m-%d %H:%M``)
    :<json float distance: activity distance in km
    :<json integer duration: activity duration in seconds
    :<json string notes: notes (not mandatory)
    :<json integer sport_id: activity sport id
    :<json string title: activity title

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 201: activity created
    :statuscode 400: invalid payload
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 500:

    """
    activity_data = request.get_json()
    if (not activity_data or activity_data.get('sport_id') is None
            or activity_data.get('duration') is None
            or activity_data.get('distance') is None
            or activity_data.get('activity_date') is None):
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400

    try:
        user = User.query.filter_by(id=auth_user_id).first()
        new_activity = create_activity(user, activity_data)
        db.session.add(new_activity)
        db.session.commit()

        response_object = {
            'status': 'created',
            'data': {
                'activities': [new_activity.serialize()]
            },
        }
        return jsonify(response_object), 201

    except (exc.IntegrityError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'fail',
            'message': 'Error during activity save.',
        }
        return jsonify(response_object), 500
Exemplo n.º 21
0
def register_user():
    """
    register a user

    **Example request**:

    .. sourcecode:: http

      POST /api/auth/register HTTP/1.1
      Content-Type: application/json

    **Example responses**:

    - successful registration

    .. sourcecode:: http

      HTTP/1.1 201 CREATED
      Content-Type: application/json

      {
        "auth_token": "JSON Web Token",
        "message": "Successfully registered.",
        "status": "success"
      }

    - error on registration

    .. sourcecode:: http

      HTTP/1.1 400 BAD REQUEST
      Content-Type: application/json

      {
        "message": "Errors: Valid email must be provided.\\n",
        "status": "error"
      }

    :<json string username: user name (3 to 12 characters required)
    :<json string email: user email
    :<json string password: password (8 characters required)
    :<json string password_conf: password confirmation

    :statuscode 201: Successfully registered.
    :statuscode 400:
        - Invalid payload.
        - Sorry. That user already exists.
        - Errors:
            - 3 to 12 characters required for usernanme.
            - Valid email must be provided.
            - Password and password confirmation don't match.
            - 8 characters required for password.
    :statuscode 403:
        Error. Registration is disabled.
    :statuscode 500:
        Error. Please try again or contact the administrator.

    """
    if not current_app.config.get('REGISTRATION_ALLOWED'):
        response_object = {
            'status': 'error',
            'message': 'Error. Registration is disabled.',
        }
        return jsonify(response_object), 403
    # get post data
    post_data = request.get_json()
    if (not post_data or post_data.get('username') is None
            or post_data.get('email') is None
            or post_data.get('password') is None
            or post_data.get('password_conf') is None):
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400
    username = post_data.get('username')
    email = post_data.get('email')
    password = post_data.get('password')
    password_conf = post_data.get('password_conf')

    try:
        ret = register_controls(username, email, password, password_conf)
    except TypeError as e:
        db.session.rollback()
        appLog.error(e)

        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        return jsonify(response_object), 500
    if ret != '':
        response_object = {'status': 'error', 'message': ret}
        return jsonify(response_object), 400

    try:
        # check for existing user
        user = User.query.filter(
            or_(User.username == username, User.email == email)).first()
        if not user:
            # add new user to db
            new_user = User(username=username, email=email, password=password)
            new_user.timezone = 'Europe/Paris'
            db.session.add(new_user)
            db.session.commit()
            # generate auth token
            auth_token = new_user.encode_auth_token(new_user.id)
            response_object = {
                'status': 'success',
                'message': 'Successfully registered.',
                'auth_token': auth_token.decode(),
            }
            return jsonify(response_object), 201
        else:
            response_object = {
                'status': 'error',
                'message': 'Sorry. That user already exists.',
            }
            return jsonify(response_object), 400
    # handler errors
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)

        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        return jsonify(response_object), 500
Exemplo n.º 22
0
def register_user():
    # get post data
    post_data = request.get_json()
    if not post_data or post_data.get('username') is None \
            or post_data.get('email') is None \
            or post_data.get('password') is None \
            or post_data.get('password_conf') is None:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400
    username = post_data.get('username')
    email = post_data.get('email')
    password = post_data.get('password')
    password_conf = post_data.get('password_conf')

    try:
        ret = register_controls(username, email, password, password_conf)
    except TypeError as e:
        db.session.rollback()
        appLog.error(e)

        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        return jsonify(response_object), 500
    if ret != '':
        response_object = {'status': 'error', 'message': 'Errors: ' + ret}
        return jsonify(response_object), 400

    try:
        # check for existing user
        user = User.query.filter(
            or_(User.username == username, User.email == email)).first()
        if not user:
            # add new user to db
            new_user = User(username=username, email=email, password=password)
            new_user.timezone = 'Europe/Paris'
            db.session.add(new_user)
            db.session.commit()
            # generate auth token
            auth_token = new_user.encode_auth_token(new_user.id)
            response_object = {
                'status': 'success',
                'message': 'Successfully registered.',
                'auth_token': auth_token.decode()
            }
            return jsonify(response_object), 201
        else:
            response_object = {
                'status': 'error',
                'message': 'Sorry. That user already exists.'
            }
            return jsonify(response_object), 400
    # handler errors
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)

        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.'
        }
        return jsonify(response_object), 500
Exemplo n.º 23
0
def delete_activity(auth_user_id, activity_id):
    """
    Delete an activity

    **Example request**:

    .. sourcecode:: http

      DELETE /api/activities/1 HTTP/1.1
      Content-Type: application/json

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 204 NO CONTENT
      Content-Type: application/json

    :param integer auth_user_id: authenticate user id (from JSON Web Token)
    :param integer activity_id: activity id

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 204: activity deleted
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 404: activity not found
    :statuscode 500: Error. Please try again or contact the administrator.

    """

    try:
        activity = Activity.query.filter_by(id=activity_id).first()
        if activity:
            response_object, code = can_view_activity(auth_user_id,
                                                      activity.user_id)
            if response_object:
                return jsonify(response_object), code

            db.session.delete(activity)
            db.session.commit()
            response_object = {'status': 'no content'}
            code = 204
        else:
            response_object = {
                'status': 'not found',
                'data': {
                    'activities': []
                },
            }
            code = 404
    except (
            exc.IntegrityError,
            exc.OperationalError,
            ValueError,
            OSError,
    ) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 24
0
def edit_picture(user_id):
    """
    update authenticated user picture

    **Example request**:

    .. sourcecode:: http

      POST /api/auth/picture HTTP/1.1
      Content-Type: multipart/form-data

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: application/json

      {
        "message": "User picture updated.",
        "status": "success"
      }

    :form file: image file (allowed extensions: .jpg, .png, .gif)

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 200: User picture updated.
    :statuscode 400:
        - Invalid payload.
        - No file part.
        - No selected file.
        - File extension not allowed.
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 413: Error during picture update: file size exceeds 1.0MB.
    :statuscode 500: Error during picture update.

    """
    try:
        response_object, response_code = verify_extension_and_size(
            'picture', request)
    except RequestEntityTooLarge as e:
        appLog.error(e)
        max_file_size = current_app.config['MAX_CONTENT_LENGTH']
        response_object = {
            'status':
            'fail',
            'message':
            'Error during picture update, file size exceeds '
            f'{display_readable_file_size(max_file_size)}.',
        }
        return jsonify(response_object), 413
    if response_object['status'] != 'success':
        return jsonify(response_object), response_code

    file = request.files['file']
    filename = secure_filename(file.filename)
    dirpath = os.path.join(current_app.config['UPLOAD_FOLDER'], 'pictures',
                           str(user_id))
    if not os.path.exists(dirpath):
        os.makedirs(dirpath)
    absolute_picture_path = os.path.join(dirpath, filename)
    relative_picture_path = os.path.join('pictures', str(user_id), filename)

    try:
        user = User.query.filter_by(id=user_id).first()
        if user.picture is not None:
            old_picture_path = get_absolute_file_path(user.picture)
            if os.path.isfile(get_absolute_file_path(old_picture_path)):
                os.remove(old_picture_path)
        file.save(absolute_picture_path)
        user.picture = relative_picture_path
        db.session.commit()

        response_object = {
            'status': 'success',
            'message': 'User picture updated.',
        }
        return jsonify(response_object), 200

    except (exc.IntegrityError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'fail',
            'message': 'Error during picture update.',
        }
        return jsonify(response_object), 500
Exemplo n.º 25
0
def edit_user(user_id):
    """
    edit authenticated user

    **Example request**:

    .. sourcecode:: http

      POST /api/auth/profile/edit HTTP/1.1
      Content-Type: application/json

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: application/json

      {
        "data": {
          "admin": false,
          "bio": null,
          "birth_date": null,
          "created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
          "email": "*****@*****.**",
          "first_name": null,
          "id": 2,
          "language": "en",
          "last_name": null,
          "location": null,
          "nb_activities": 6,
          "nb_sports": 3,
          "picture": false,
          "timezone": "Europe/Paris",
          "total_distance": 67.895,
          "total_duration": "6:50:27",
          "username": "******"
          "weekm": true,
        },
        "message": "User profile updated.",
        "status": "success"
      }

    :<json string first_name: user first name
    :<json string last_name: user last name
    :<json string location: user location
    :<json string bio: user biography
    :<json string birth_date: user birth date (format: ``%Y-%m-%d``)
    :<json string password: user password
    :<json string password_conf: user password confirmation
    :<json string timezone: user time zone
    :<json string weekm: does week start on Monday?
    :<json string language: language preferences

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 200: User profile updated.
    :statuscode 400:
        - Invalid payload.
        - Password and password confirmation don't match.
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 500: Error. Please try again or contact the administrator.

    """
    # get post data
    post_data = request.get_json()
    user_mandatory_data = {
        'first_name',
        'last_name',
        'bio',
        'birth_date',
        'language',
        'location',
        'timezone',
        'weekm',
    }
    if not post_data or not post_data.keys() >= user_mandatory_data:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400
    first_name = post_data.get('first_name')
    last_name = post_data.get('last_name')
    bio = post_data.get('bio')
    birth_date = post_data.get('birth_date')
    language = post_data.get('language')
    location = post_data.get('location')
    password = post_data.get('password')
    password_conf = post_data.get('password_conf')
    timezone = post_data.get('timezone')
    weekm = post_data.get('weekm')

    if password is not None and password != '':
        if password_conf != password:
            message = 'Password and password confirmation don\'t match.\n'
            response_object = {'status': 'error', 'message': message}
            return jsonify(response_object), 400
        else:
            password = bcrypt.generate_password_hash(
                password,
                current_app.config.get('BCRYPT_LOG_ROUNDS')).decode()

    try:
        user = User.query.filter_by(id=user_id).first()
        user.first_name = first_name
        user.last_name = last_name
        user.bio = bio
        user.language = language
        user.location = location
        user.birth_date = (datetime.datetime.strptime(birth_date, '%Y-%m-%d')
                           if birth_date else None)
        if password is not None and password != '':
            user.password = password
        user.timezone = timezone
        user.weekm = weekm
        db.session.commit()

        response_object = {
            'status': 'success',
            'message': 'User profile updated.',
            'data': user.serialize(),
        }
        return jsonify(response_object), 200

    # handler errors
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        return jsonify(response_object), 500
Exemplo n.º 26
0
def get_activities(auth_user_id):
    """
    Get activities for the authenticated user.

    **Example requests**:

    - without parameters

    .. sourcecode:: http

      GET /api/activities/ HTTP/1.1

    - with some query parameters

    .. sourcecode:: http

      GET /api/activities?from=2019-07-02&to=2019-07-31&sport_id=1  HTTP/1.1

    **Example responses**:

    - returning at least one activity

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: application/json

        {
          "data": {
            "activities": [
              {
                "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                "ascent": null,
                "ave_speed": 10.0,
                "bounds": [],
                "creation_date": "Sun, 14 Jul 2019 13:51:01 GMT",
                "descent": null,
                "distance": 10.0,
                "duration": "0:17:04",
                "id": 1,
                "map": null,
                "max_alt": null,
                "max_speed": 10.0,
                "min_alt": null,
                "modification_date": null,
                "moving": "0:17:04",
                "next_activity": 3,
                "notes": null,
                "pauses": null,
                "previous_activity": null,
                "records": [
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 4,
                    "record_type": "MS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 3,
                    "record_type": "LD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": "0:17:04"
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 2,
                    "record_type": "FD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 1,
                    "record_type": "AS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  }
                ],
                "segments": [],
                "sport_id": 1,
                "title": null,
                "user_id": 1,
                "weather_end": null,
                "weather_start": null,
                "with_gpx": false
              }
            ]
          },
          "status": "success"
        }

    - returning no activities

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: application/json

        {
            "data": {
                "activities": []
            },
            "status": "success"
        }

    :param integer auth_user_id: authenticate user id (from JSON Web Token)

    :query integer page: page if using pagination (default: 1)
    :query integer per_page: number of activities per page (default: 5)
    :query integer sport_id: sport id
    :query string from: start date (format: ``%Y-%m-%d``)
    :query string to: end date (format: ``%Y-%m-%d``)
    :query float distance_from: minimal distance
    :query float distance_to: maximal distance
    :query string duration_from: minimal duration (format: ``%H:%M``)
    :query string duration_to: maximal distance (format: ``%H:%M``)
    :query float ave_speed_from: minimal average speed
    :query float ave_speed_to: maximal average speed
    :query float max_speed_from: minimal max. speed
    :query float max_speed_to: maximal max. speed
    :query string order: sorting order (default: ``desc``)

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 200: success
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 500:

    """
    try:
        user = User.query.filter_by(id=auth_user_id).first()
        params = request.args.copy()
        page = 1 if 'page' not in params.keys() else int(params.get('page'))
        date_from = params.get('from')
        if date_from:
            date_from = datetime.strptime(date_from, '%Y-%m-%d')
            _, date_from = get_datetime_with_tz(user.timezone, date_from)
        date_to = params.get('to')
        if date_to:
            date_to = datetime.strptime(f'{date_to} 23:59:59',
                                        '%Y-%m-%d %H:%M:%S')
            _, date_to = get_datetime_with_tz(user.timezone, date_to)
        distance_from = params.get('distance_from')
        distance_to = params.get('distance_to')
        duration_from = params.get('duration_from')
        duration_to = params.get('duration_to')
        ave_speed_from = params.get('ave_speed_from')
        ave_speed_to = params.get('ave_speed_to')
        max_speed_from = params.get('max_speed_from')
        max_speed_to = params.get('max_speed_to')
        order = params.get('order')
        sport_id = params.get('sport_id')
        per_page = int(params.get('per_page')) if params.get('per_page') else 5
        activities = (Activity.query.filter(
            Activity.user_id == auth_user_id,
            Activity.sport_id == sport_id if sport_id else True,
            Activity.activity_date >= date_from if date_from else True,
            Activity.activity_date < date_to + timedelta(seconds=1)
            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.moving >= convert_in_duration(duration_from)
            if duration_from else True,
            Activity.moving <= 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,
            Activity.max_speed >= float(max_speed_from)
            if max_speed_from else True,
            Activity.max_speed <= float(max_speed_to)
            if max_speed_to else True,
        ).order_by(Activity.activity_date.asc() if order ==
                   'asc' else Activity.activity_date.desc()).paginate(
                       page, per_page, False).items)
        response_object = {
            'status': 'success',
            'data': {
                'activities':
                [activity.serialize(params) for activity in activities]
            },
        }
        code = 200
    except Exception as e:
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 27
0
def get_activities(user_id, filter_type):
    try:
        user = User.query.filter_by(id=user_id).first()
        if not user:
            response_object = {
                'status': 'not found',
                'message': 'User does not exist.',
            }
            return jsonify(response_object), 404

        params = request.args.copy()
        date_from = params.get('from')
        if date_from:
            date_from = datetime.strptime(date_from, '%Y-%m-%d')
            _, date_from = get_datetime_with_tz(user.timezone, date_from)
        date_to = params.get('to')
        if date_to:
            date_to = datetime.strptime(f'{date_to} 23:59:59',
                                        '%Y-%m-%d %H:%M:%S')
            _, date_to = get_datetime_with_tz(user.timezone, date_to)
        sport_id = params.get('sport_id')
        time = params.get('time')

        if filter_type == 'by_sport':
            sport_id = params.get('sport_id')
            if sport_id:
                sport = Sport.query.filter_by(id=sport_id).first()
                if not sport:
                    response_object = {
                        'status': 'not found',
                        'message': 'Sport does not exist.',
                    }
                    return jsonify(response_object), 404

        activities = (Activity.query.filter(
            Activity.user_id == user_id,
            Activity.activity_date >= date_from if date_from else True,
            Activity.activity_date < date_to + timedelta(seconds=1)
            if date_to else True,
            Activity.sport_id == sport_id if sport_id else True,
        ).order_by(Activity.activity_date.asc()).all())

        activities_list = {}
        for activity in activities:
            if filter_type == 'by_sport':
                sport_id = activity.sport_id
                if sport_id not in activities_list:
                    activities_list[sport_id] = {
                        'nb_activities': 0,
                        'total_distance': 0.0,
                        'total_duration': 0,
                    }
                activities_list[sport_id]['nb_activities'] += 1
                activities_list[sport_id]['total_distance'] += float(
                    activity.distance)
                activities_list[sport_id][
                    'total_duration'] += convert_timedelta_to_integer(
                        activity.moving)

            else:
                if time == 'week':
                    activity_date = activity.activity_date - timedelta(
                        days=(activity.activity_date.isoweekday(
                        ) if activity.activity_date.isoweekday() < 7 else 0))
                    time_period = datetime.strftime(activity_date, "%Y-%m-%d")
                elif time == 'weekm':  # week start Monday
                    activity_date = activity.activity_date - timedelta(
                        days=activity.activity_date.weekday())
                    time_period = datetime.strftime(activity_date, "%Y-%m-%d")
                elif time == 'month':
                    time_period = datetime.strftime(activity.activity_date,
                                                    "%Y-%m")  # noqa
                elif time == 'year' or not time:
                    time_period = datetime.strftime(activity.activity_date,
                                                    "%Y")  # noqa
                else:
                    response_object = {
                        'status': 'fail',
                        'message': 'Invalid time period.',
                    }
                    return jsonify(response_object), 400
                sport_id = activity.sport_id
                if time_period not in activities_list:
                    activities_list[time_period] = {}
                if sport_id not in activities_list[time_period]:
                    activities_list[time_period][sport_id] = {
                        'nb_activities': 0,
                        'total_distance': 0.0,
                        'total_duration': 0,
                    }
                activities_list[time_period][sport_id]['nb_activities'] += 1
                activities_list[time_period][sport_id][
                    'total_distance'] += float(activity.distance)
                activities_list[time_period][sport_id][
                    'total_duration'] += convert_timedelta_to_integer(
                        activity.moving)

        response_object = {
            'status': 'success',
            'data': {
                'statistics': activities_list
            },
        }
        code = 200
    except Exception as e:
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 28
0
def update_activity(auth_user_id, activity_id):
    """
    Update an activity

    **Example request**:

    .. sourcecode:: http

      PATCH /api/activities/1 HTTP/1.1
      Content-Type: application/json

    **Example response**:

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: application/json

       {
          "data": {
            "activities": [
              {
                "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                "ascent": null,
                "ave_speed": 10.0,
                "bounds": [],
                "creation_date": "Sun, 14 Jul 2019 13:51:01 GMT",
                "descent": null,
                "distance": 10.0,
                "duration": "0:17:04",
                "id": 1,
                "map": null,
                "max_alt": null,
                "max_speed": 10.0,
                "min_alt": null,
                "modification_date": null,
                "moving": "0:17:04",
                "next_activity": 3,
                "notes": null,
                "pauses": null,
                "previous_activity": null,
                "records": [
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 4,
                    "record_type": "MS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 3,
                    "record_type": "LD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": "0:17:04"
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 2,
                    "record_type": "FD",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  },
                  {
                    "activity_date": "Mon, 01 Jan 2018 00:00:00 GMT",
                    "activity_id": 1,
                    "id": 1,
                    "record_type": "AS",
                    "sport_id": 1,
                    "user_id": 1,
                    "value": 10.0
                  }
                ],
                "segments": [],
                "sport_id": 1,
                "title": null,
                "user_id": 1,
                "weather_end": null,
                "weather_start": null,
                "with_gpx": false
              }
            ]
          },
          "status": "success"
        }

    :param integer auth_user_id: authenticate user id (from JSON Web Token)
    :param integer activity_id: activity id

    :<json string activity_date: activity date  (format: ``%Y-%m-%d %H:%M``)
        (only for activity without gpx)
    :<json float distance: activity distance in km
        (only for activity without gpx)
    :<json integer duration: activity duration in seconds
        (only for activity without gpx)
    :<json string notes: notes
    :<json integer sport_id: activity sport id
    :<json string title: activity title

    :reqheader Authorization: OAuth 2.0 Bearer Token

    :statuscode 200: activity updated
    :statuscode 400: invalid payload
    :statuscode 401:
        - Provide a valid auth token.
        - Signature expired. Please log in again.
        - Invalid token. Please log in again.
    :statuscode 404: activity not found
    :statuscode 500:

    """
    activity_data = request.get_json()
    if not activity_data:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400

    try:
        activity = Activity.query.filter_by(id=activity_id).first()
        if activity:
            response_object, code = can_view_activity(auth_user_id,
                                                      activity.user_id)
            if response_object:
                return jsonify(response_object), code

            activity = edit_activity(activity, activity_data, auth_user_id)
            db.session.commit()
            response_object = {
                'status': 'success',
                'data': {
                    'activities': [activity.serialize()]
                },
            }
            code = 200
        else:
            response_object = {
                'status': 'not found',
                'data': {
                    'activities': []
                },
            }
            code = 404
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        code = 500
    return jsonify(response_object), code
Exemplo n.º 29
0
def login_user():
    """
    user login

    **Example request**:

    .. sourcecode:: http

      POST /api/auth/login HTTP/1.1
      Content-Type: application/json

    **Example responses**:

    - successful login

    .. sourcecode:: http

      HTTP/1.1 200 OK
      Content-Type: application/json

      {
        "auth_token": "JSON Web Token",
        "message": "Successfully logged in.",
        "status": "success"
      }

    - error on login

    .. sourcecode:: http

      HTTP/1.1 404 NOT FOUND
      Content-Type: application/json

      {
        "message": "Invalid credentials.",
        "status": "error"
      }

    :<json string email: user email
    :<json string password_conf: password confirmation

    :statuscode 200: Successfully logged in.
    :statuscode 404: Invalid credentials.
    :statuscode 500: Error. Please try again or contact the administrator.

    """
    # get post data
    post_data = request.get_json()
    if not post_data:
        response_object = {'status': 'error', 'message': 'Invalid payload.'}
        return jsonify(response_object), 400
    email = post_data.get('email')
    password = post_data.get('password')
    try:
        # check for existing user
        user = User.query.filter(User.email == email).first()
        if user and bcrypt.check_password_hash(user.password, password):
            # generate auth token
            auth_token = user.encode_auth_token(user.id)
            response_object = {
                'status': 'success',
                'message': 'Successfully logged in.',
                'auth_token': auth_token.decode(),
            }
            return jsonify(response_object), 200
        else:
            response_object = {
                'status': 'error',
                'message': 'Invalid credentials.',
            }
            return jsonify(response_object), 404
    # handler errors
    except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
        db.session.rollback()
        appLog.error(e)
        response_object = {
            'status': 'error',
            'message': 'Error. Please try again or contact the administrator.',
        }
        return jsonify(response_object), 500