API & Client: Profile picture
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
import os
|
||||
from flask import current_app
|
||||
|
||||
|
||||
class BaseConfig:
|
||||
"""Base configuration"""
|
||||
DEBUG = False
|
||||
@ -6,6 +10,10 @@ class BaseConfig:
|
||||
BCRYPT_LOG_ROUNDS = 13
|
||||
TOKEN_EXPIRATION_DAYS = 30
|
||||
TOKEN_EXPIRATION_SECONDS = 0
|
||||
UPLOAD_FOLDER = os.path.join(
|
||||
current_app.root_path, 'uploads'
|
||||
)
|
||||
PICTURE_ALLOWED_EXTENSIONS = {'jpg', 'png', 'gif'}
|
||||
|
||||
|
||||
class DevelopmentConfig(BaseConfig):
|
||||
|
@ -1,5 +1,6 @@
|
||||
import json
|
||||
import time
|
||||
from io import BytesIO
|
||||
|
||||
from mpwo_api.tests.base import BaseTestCase
|
||||
from mpwo_api.tests.utils import add_user, add_user_full
|
||||
@ -485,3 +486,31 @@ class TestAuthBlueprint(BaseTestCase):
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn('Invalid payload.', data['message'])
|
||||
self.assertIn('error', data['status'])
|
||||
|
||||
def test_update_user_picture(self):
|
||||
add_user('test', 'test@test.com', '12345678')
|
||||
|
||||
with self.client:
|
||||
resp_login = self.client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = self.client.post(
|
||||
'/api/auth/picture',
|
||||
data=dict(
|
||||
file=(BytesIO(b'avatar'), 'avatar.png')
|
||||
),
|
||||
headers=dict(
|
||||
content_type='multipart/form-data',
|
||||
authorization='Bearer ' +
|
||||
json.loads(resp_login.data.decode())['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
self.assertTrue(data['status'] == 'success')
|
||||
self.assertTrue(data['message'] == 'User picture updated.')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -1,11 +1,13 @@
|
||||
import datetime
|
||||
from flask import Blueprint, current_app, jsonify, request
|
||||
import os
|
||||
from flask import Blueprint, current_app, jsonify, request, send_from_directory
|
||||
from sqlalchemy import exc, or_
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from mpwo_api import appLog, bcrypt, db
|
||||
|
||||
from .models import User
|
||||
from .utils import authenticate, register_controls
|
||||
from .utils import allowed_picture, authenticate, register_controls
|
||||
|
||||
auth_blueprint = Blueprint('auth', __name__)
|
||||
|
||||
@ -161,6 +163,7 @@ def get_user_status(user_id):
|
||||
'bio': user.bio,
|
||||
'location': user.location,
|
||||
'birth_date': user.birth_date,
|
||||
'picture': True if user.picture else False,
|
||||
}
|
||||
}
|
||||
return jsonify(response_object), 200
|
||||
@ -227,3 +230,81 @@ def edit_user(user_id):
|
||||
'message': 'Error. Please try again or contact the administrator.'
|
||||
}
|
||||
return jsonify(response_object), 500
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/picture', methods=['POST'])
|
||||
@authenticate
|
||||
def edit_picture(user_id):
|
||||
code = 400
|
||||
if 'file' not in request.files:
|
||||
response_object = {'status': 'fail', 'message': 'No file part.'}
|
||||
return jsonify(response_object), code
|
||||
file = request.files['file']
|
||||
if file.filename == '':
|
||||
response_object = {'status': 'fail', 'message': 'No selected file.'}
|
||||
return jsonify(response_object), code
|
||||
if not allowed_picture(file.filename):
|
||||
response_object = {
|
||||
'status': 'fail',
|
||||
'message': 'File extension not allowed.'
|
||||
}
|
||||
return jsonify(response_object), code
|
||||
|
||||
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)
|
||||
filepath = os.path.join(dirpath, filename)
|
||||
|
||||
try:
|
||||
user = User.query.filter_by(id=user_id).first()
|
||||
if user.picture is not None and os.path.isfile(user.picture):
|
||||
os.remove(user.picture)
|
||||
file.save(filepath)
|
||||
user.picture = filepath
|
||||
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
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/picture', methods=['DELETE'])
|
||||
@authenticate
|
||||
def del_picture(user_id):
|
||||
try:
|
||||
user = User.query.filter_by(id=user_id).first()
|
||||
if os.path.isfile(user.picture):
|
||||
os.remove(user.picture)
|
||||
user.picture = None
|
||||
db.session.commit()
|
||||
|
||||
response_object = {
|
||||
'status': 'success',
|
||||
'message': 'User picture delete.'
|
||||
}
|
||||
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 deletion.'
|
||||
}
|
||||
return jsonify(response_object), 500
|
||||
|
@ -19,6 +19,7 @@ class User(db.Model):
|
||||
birth_date = db.Column(db.DateTime, nullable=True)
|
||||
location = db.Column(db.String(80), nullable=True)
|
||||
bio = db.Column(db.String(200), nullable=True)
|
||||
picture = db.Column(db.String(255), nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.username
|
||||
|
@ -1,4 +1,4 @@
|
||||
from flask import Blueprint, jsonify
|
||||
from flask import Blueprint, jsonify, send_file
|
||||
|
||||
from .models import User
|
||||
|
||||
@ -52,6 +52,22 @@ def get_single_user(user_id):
|
||||
return jsonify(response_object), 404
|
||||
|
||||
|
||||
@users_blueprint.route('/users/<user_id>/picture', methods=['GET'])
|
||||
def get_picture(user_id):
|
||||
response_object = {
|
||||
'status': 'fail',
|
||||
'message': 'User does not exist'
|
||||
}
|
||||
try:
|
||||
user = User.query.filter_by(id=int(user_id)).first()
|
||||
if not user:
|
||||
return jsonify(response_object), 404
|
||||
else:
|
||||
return send_file(user.picture)
|
||||
except ValueError:
|
||||
return jsonify(response_object), 404
|
||||
|
||||
|
||||
@users_blueprint.route('/ping', methods=['GET'])
|
||||
def ping_pong():
|
||||
return jsonify({
|
||||
|
@ -1,11 +1,17 @@
|
||||
from functools import wraps
|
||||
import re
|
||||
|
||||
from flask import request, jsonify
|
||||
from flask import current_app, jsonify, request
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
def allowed_picture(filename):
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in \
|
||||
current_app.config.get('PICTURE_ALLOWED_EXTENSIONS')
|
||||
|
||||
|
||||
def authenticate(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
|
Reference in New Issue
Block a user