def get_profile(id_user, user_input): """ Retrieve the a user profile, in this case the profile only contains maximum calories permitted per day :param id_user: user id to retrieve the profile :type: int :param user_input: user id to which the profile belongs :type: User Model :return: user profile. HTTP status code :type: tuple(json, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection( {ADMIN_ROLE, MANAGER_ROLE}): user = User.query.filter(User.id == id_user).first() profile = Profile.query.filter(Profile.user_id == id_user).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND else: response = marshal(profile, PROFILE_MODEL) code = CODE_OK else: response = dict(RESPONSES_CATALOG['no_permission_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def get_meals(id_user, request, user_input): """ Retrieve the list of meals corresponding to an user :param id_user: user id to retrieve list of meals :type: int :param request: context information about the current request :type: LocalProxy :param user_input: User who made the request :type: User Model :return: list of meals corresponding to the user id :type: tuple(json, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection({ADMIN_ROLE}): user = User.query.filter(User.id == id_user).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND else: args = PAGE_ARGUMENTS_MEALS.parse_args(request) page = args.get('page', 1) per_page = args.get('per_page', 10) sort = args.get('sort', 'id') if sort == 'id': sort = "meal_" + sort if sort not in FIELDS_MEALS: response = dict(RESPONSES_CATALOG['error_sort']) response['error'] = response['error'].format(sort) code = CODE_NOT_ACCEPTABLE return response, code search = args.get('search') filter_query = "" model = 'Meal' try: if search: search = search.lower() query_string = process_query(model, search) filter_query = ".filter({})".format(query_string) string_filter = model + ".query.order_by(Meal." + sort + \ ").filter(" + model + \ ".user_id == user.id)" + filter_query + \ ".paginate(page, per_page, error_out=False)" entries_page = eval(string_filter) response = marshal(entries_page, PAGE_OF_MEALS) code = CODE_OK except ParseException: response = RESPONSES_CATALOG['error_parse_query_values'] code = CODE_NOT_ACCEPTABLE except AttributeError as error: response = dict(RESPONSES_CATALOG['error_parse_query_fields']) response['error'] = response['error'].format(field_error=error) code = CODE_NOT_ACCEPTABLE else: response = dict(RESPONSES_CATALOG['no_permission_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def get_users(request, user): """ Get the list of users registered on the server. The requester must have permission to get list of users. Only admins and managers can request an user from database. :param request: Information about the request done :type: LocalProxy :param user: User who made the request :type: User Model :return: List of users in json format. HTTP status code :type: tuple(json, int) """ roles = get_roles_user(user) if roles.intersection({ADMIN_ROLE, MANAGER_ROLE}): if roles.intersection({ADMIN_ROLE}): model_serializer = PAGE_OF_USERS_ADMIN else: model_serializer = PAGE_OF_USERS_MANAGER args = PAGE_ARGUMENTS_USERS.parse_args(request) page = args.get('page', 1) per_page = args.get('per_page', 10) sort = args.get('sort', 'id') if sort not in FIELDS_USERS: response = {'error': 'Non acceptable ordering: ' + sort} code = CODE_NOT_ACCEPTABLE return response, code search = args.get('search') filter_query = "" model = 'User' try: if search: search = search.lower() query_string = process_query(model, search) filter_query = ".filter({})".format(query_string) string_filter = model + ".query.order_by(" + model + "." + sort + \ ")" + filter_query + ".paginate(page, per_page, " \ "error_out=False)" users_page = eval(string_filter) response = marshal(users_page, model_serializer) code = CODE_OK except ParseException: response = RESPONSES_CATALOG['error_parse_query_values'] code = CODE_NOT_ACCEPTABLE except AttributeError as error: response = dict(RESPONSES_CATALOG['error_parse_query_fields']) response['error'] = response['error'].format(field_error=error) code = CODE_NOT_ACCEPTABLE except Exception as error: response = dict(RESPONSES_CATALOG['internal_error_server']) response['error'] = response['error'] + str(error) code = CODE_INTERNAL_ERROR else: response = dict(RESPONSES_CATALOG['no_permission_resource']) response['error'] = response['error'].format(id_user=user.id) code = CODE_FORBIDDEN return response, code
def create_meal(id_user, data, user_input): """ Create and store a new meal in database. :param id_user: User's id who the new meal will be assigned :type: int :param data: Information about the new meal to create :type: json :param user_input: User who made the request :type: User Model :return: The new meal created is success, error message otherwise. HTTP status :type: tuple(json, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection({ADMIN_ROLE}): user = User.query.filter(User.id == id_user).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND else: try: date_str = data.get('date') date = datetime.strptime(date_str, '%Y-%m-%d') time_in_datetime = datetime.strptime(data.get('time'), '%H:%M:%S') time = datetime.time(time_in_datetime) description = data.get('description') calories = data.get('calories') if calories is None: calories = get_calories_meal(description) profile = \ Profile.query.filter(Profile.user_id == id_user).first() maximum_calories_user = profile.maximum_calories less_calories = compute_calories_day(id_user, date_str, time, calories, maximum_calories_user) new_meal = Meal(date, time, description, calories, user) new_meal.calories_less_expected = less_calories DB.session.add(new_meal) DB.session.commit() response = marshal(new_meal, MEAL_API_MODEL) code = CODE_CREATED except (SyntaxError, ValueError) as error: response = { 'error': "Error parsing input value: " + str(error) } code = CODE_NOT_ACCEPTABLE else: response = dict(RESPONSES_CATALOG['no_permission_update_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def patch_user(id_user, data, user_input): """ Update information of an user on database. Not all fields are required in this case, this function can be used to unblock users accounts for instance :param id_user: user id corresponding to user to update information :type: int :param data: new information to store in the user :type: json :param user_input: User who made the request :type: User Model :return: User updated if success, error message otherwise. HTTP status code :type: tuple(json, int) """ roles = get_roles_user(user_input) if roles.intersection({ADMIN_ROLE, MANAGER_ROLE}): user = User.query.filter(User.id == id_user).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND else: if data.get('username') is not None: user.username = data.get('username') if data.get('first_name') is not None: user.first_name = data.get('first_name') if data.get('last_name') is not None: user.last_name = data.get('last_name') if data.get('confirmed') is not None: user.confirmed = bool(data.get('confirmed')) if data.get('confirmed_on') is not None: user.confirmed_on = datetime.strptime(data.get('confirmed_on'), '%Y-%m-%d') if data.get('attempts_login') is not None: user.attempts_login = int(data.get('attempts_login')) if data.get('blocked') is not None: user.blocked = bool(data.get('blocked')) if data.get('roles') is not None: new_role_user = data.get('roles') user.roles = [] for role in new_role_user: name_role = role["name"] role_object = Role.query.filter( Role.name == name_role).first() user.roles.append(role_object) DB.session.add(user) DB.session.commit() response = marshal(user, USER_DESCRIPTION) code = CODE_OK else: response = dict(RESPONSES_CATALOG['no_permission_update_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def create_invitation(data, user_input): """ Create a new invitation in the server and store in database. Send the email of acceptation to future user :param data: Information about the new invitation (email) :type: json :param user_input: User who made the request :type: User Model :return: A message about the invitation was successfully created, or an error message if not success. HTTP status code :type: tuple(json,int) """ roles = get_roles_user(user_input) if roles.intersection({ADMIN_ROLE}): email = data.get('email') if EMAIL_REGEX.match(email): user_in_db = User.query.filter(User.username == email).first() invitation_in_db = Invitation.query.filter( Invitation.email == email).first() if user_in_db: response = RESPONSES_CATALOG['existing_user'] code = CODE_CONFLICT elif invitation_in_db: response = RESPONSES_CATALOG['existing_invitation'] code = CODE_CONFLICT else: invitation = Invitation(email) DB.session.add(invitation) DB.session.commit() token_account_verification = \ generate_confirmation_token(invitation.email) acceptation_url = \ url_for('api.v1/users/invitations_invitation_confirmation', id_invitation=invitation.id, token=token_account_verification, _external=True) html = render_template('invitation.html', acceptation_url=acceptation_url) subject = "Mangiato: keeping track of your meals and calories" send_email(invitation.email, subject, html) response = marshal(invitation, INVITATION_MODEL) code = CODE_CREATED else: response = RESPONSES_CATALOG['no_format_email_invitation'] code = CODE_NOT_ACCEPTABLE else: response = dict(RESPONSES_CATALOG['no_permission_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def patch_invitation(id_invitation, data, user_input): """ Update information of a invitation on database. In this case only the status of invitation can be change. Updating an email may cause conflicts in the server :param id_invitation: invitation id to update :type: int :param data: new status of invitation :type: json :param user_input: User who made the request :type: User Model :return: Invitation updated if success, error message otherwise. HTTP status code :type: tuple(json, int) """ roles = get_roles_user(user_input) if roles.intersection({ADMIN_ROLE}): invitation = Invitation.query.filter( Invitation.id == id_invitation).first() email = data.get('email') status = data.get('status') if not invitation: response = dict(RESPONSES_CATALOG['no_invitation_server']) response['error'] = \ response['error'].format(id_invitation=id_invitation) code = CODE_NOT_FOUND elif email is not None and email != invitation.email: response = RESPONSES_CATALOG['no_change_email_invitation'] code = CODE_FORBIDDEN elif status in STATUS_INVITATION: invitation.status = data.get('status') DB.session.add(invitation) DB.session.commit() response = marshal(invitation, INVITATION_MODEL) code = CODE_OK else: response = { 'error': 'Possible values for status: pending, ' 'accepted. No available: ' + status } code = CODE_NOT_ACCEPTABLE else: response = dict(RESPONSES_CATALOG['no_permission_update_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def delete_meal(id_user, id_meal, user_input): """ Delete a meal from database server. The requester must have permission to perform deletion. The meal must to belong it or must have admin permission. The deletion of a meal also trigger the recalculation of flag of maximum calories reached for that day :param id_user: user id which the meal belong :type: int :param id_meal: meal id to delete :type: int :param user_input: User who made the request :type: User Model :return: An message of deletion confirmation. error message otherwise. HTTP status code :type: tuple(json, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection({ADMIN_ROLE}): user = User.query.filter(User.id == id_user).first() meal = Meal.query.filter(Meal.meal_id == id_meal).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND elif not meal: response = dict(RESPONSES_CATALOG['no_meal_server']) response['error'] = response['error'].format(id_meal=id_meal) code = CODE_NOT_FOUND elif meal.user_id != id_user: response = dict(RESPONSES_CATALOG['no_meal_user']) response['error'] = response['error'].format(id_meal=id_meal, id_user=id_user) code = CODE_NOT_FOUND else: date_meal = meal.date time_meal = meal.time DB.session.delete(meal) DB.session.commit() _recalculate_calories_less_expected(id_user, date_meal, time_meal) response = RESPONSES_CATALOG['meal_deleted'] code = CODE_NO_CONTENT else: response = dict(RESPONSES_CATALOG['no_permission_delete_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def get_invitations(request, user_input): """ Retrieve the list of invitations made in the API by the administrators :param request: context information about the current request :type: LocalProxy :param user_input: User who made the request :type: User Model :return: list of invitations made by administrators on the Mangiato API :type: tuple(json, int) """ roles = get_roles_user(user_input) if roles.intersection({ADMIN_ROLE}): args = PAGE_ARGUMENTS_INV.parse_args(request) page = args.get('page', 1) per_page = args.get('per_page', 10) sort = args.get('sort', 'id') if sort not in FIELDS_INVITATION: response = {'error': 'Non acceptable ordering: ' + sort} code = CODE_NOT_ACCEPTABLE return response, code search = args.get('search') filter_query = "" model = "Invitation" if search: query_string = process_query(model, search) filter_query = ".filter({})".format(query_string) string_filter = model + ".query.order_by(" + model + "." + sort + \ ")" + filter_query + ".paginate(page, per_page, " \ "error_out=False)" invitation_pages = eval(string_filter) response = marshal(invitation_pages, PAGE_OF_INVITATIONS) code = CODE_OK else: response = dict(RESPONSES_CATALOG['no_permission_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def get_meal(id_user, id_meal, user_input): """ Retrieve a specific meal corresponding to an user with id <id_user> :param id_user: user id to retrieve the meal requested :type: int :param id_meal: meal id to get from server :type: int :param user_input: User who made the request :type: User Model :return: list of meals corresponding to the user id :type: tuple(json, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection({ADMIN_ROLE}): user = User.query.filter(User.id == id_user).first() meal = Meal.query.filter(Meal.meal_id == id_meal).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND elif not meal: response = dict(RESPONSES_CATALOG['no_meal_server']) response['error'] = response['error'].format(id_meal=id_meal) code = CODE_NOT_FOUND elif meal.user.id != id_user: response = dict(RESPONSES_CATALOG['no_meal_user']) response['error'] = response['error'].format(id_meal=id_meal, id_user=id_user) code = CODE_NOT_FOUND else: response = marshal(meal, MEAL_API_MODEL) code = CODE_OK else: response = dict(RESPONSES_CATALOG['no_permission_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def patch_meal(id_user, id_meal, data, user_input): """ Update some or all information of a meal on database. In this case not all fields of meal are required. Having in account a modification of a meal impacts in de flag if the user reach maximum calories permitted per day, if the meal is modified or the date or time, this trigger the re-calculation of the flag for all meals in the day that the change impacts :param id_user: user id corresponding to user which the meal belongs :type: int :param id_meal: meal id to update :type: int :param data: new information to store of the meal :type: json :param user_input: User who made the request :type: User Model :return: Meal updated if success, error message otherwise. HTTP status code :type: tuple(json, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection({ADMIN_ROLE}): user = User.query.filter(User.id == id_user).first() meal = Meal.query.filter(Meal.meal_id == id_meal).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND elif not meal: response = dict(RESPONSES_CATALOG['no_meal_server']) response['error'] = response['error'].format(id_meal=id_meal) code = CODE_NOT_FOUND elif meal.user_id != id_user: response = dict(RESPONSES_CATALOG['no_meal_user']) response['error'] = response['error'].format(id_meal=id_meal, id_user=id_user) code = CODE_NOT_FOUND else: try: old_date_meal = meal.date old_time_entry = meal.time old_description_meal = meal.description if data.get('date') is not None: meal.date = datetime.strptime(data.get('date'), '%Y-%m-%d') if data.get('time') is not None: time_in_datetime = datetime.strptime( data.get('time'), '%H:%M:%S') meal.time = datetime.time(time_in_datetime) if data.get('description') is not None: meal.description = data.get('description') if data.get('calories') is not None: meal.calories = data.get('calories') if old_description_meal != meal.description or \ meal.description is None: meal.calories = get_calories_meal(meal.description) DB.session.add(meal) DB.session.commit() _recalculate_calories_less_expected(id_user, meal.date, meal.time) if old_date_meal != meal.date: _recalculate_calories_less_expected( id_user, old_date_meal, old_time_entry) response = marshal(meal, MEAL_API_MODEL) code = CODE_OK except (SyntaxError, ValueError) as error: response = { 'error': "Error parsing input value: " + str(error) } code = CODE_NOT_ACCEPTABLE else: response = dict(RESPONSES_CATALOG['no_permission_update_resource']) response['error'] = response['error'].format(id_user=user_input.id) code = CODE_FORBIDDEN return response, code
def update_avatar(id_user, files, user_input): """ Update of an user image profile. Get file path from files and upload the new image to replace the last one stored in the server :param id_user: user id corresponding to user to update image profile :type: int :param files: path to upload the new image from the client :type: str :param user_input: User who made the request :type: User Model :return: New Image updated if success, error message otherwise. HTTP status code :type: tuple(image, int) """ roles = get_roles_user(user_input) if id_user == user_input.id or roles.intersection( {ADMIN_ROLE, MANAGER_ROLE}): user = User.query.filter(User.id == id_user).first() profile = Profile.query.filter(Profile.user_id == id_user).first() if not user: response = dict(RESPONSES_CATALOG['no_user_server']) response['error'] = response['error'].format(id_user=id_user) code = CODE_NOT_FOUND else: if 'file' not in files: return RESPONSES_CATALOG['no_file_found'], CODE_BAD_REQUEST file = files['file'] if file and allowed_file(file.filename): try: file.save( os.path.join(APP.config['AVATARS_SAVE_PATH'], profile.name_picture_l)) except Exception: response = RESPONSES_CATALOG['internal_error'] code = CODE_INTERNAL_ERROR return response, code pic = Image.open( os.path.join(APP.config['AVATARS_SAVE_PATH'], profile.name_picture_l)) pic_small = pic.resize((SMALL_SIZE_AVATAR, SMALL_SIZE_AVATAR), Image.ANTIALIAS) pic_small.save( os.path.join(APP.config['AVATARS_SAVE_PATH'], profile.name_picture_s)) pic_medium = pic.resize((MEDIUM_SIZE_AVATAR, MEDIUM_SIZE_AVATAR), Image.ANTIALIAS) pic_medium.save( os.path.join(APP.config['AVATARS_SAVE_PATH'], profile.name_picture_m)) pic_large = pic.resize((LARGE_SIZE_AVATAR, LARGE_SIZE_AVATAR), Image.ANTIALIAS) pic_large.save( os.path.join(APP.config['AVATARS_SAVE_PATH'], profile.name_picture_l)) response = RESPONSES_CATALOG['profile_image_changed'] code = CODE_OK else: response = dict(RESPONSES_CATALOG['no_permission_other_user']) response['error'] = response['error'].format(id_user=user_input.id, id_user_2=id_user) code = CODE_FORBIDDEN return response, code