2018-11-16 19:33:25 -08:00
|
|
|
from flask import Blueprint, g, request
|
2018-10-22 15:31:33 -07:00
|
|
|
from flask_yoloapi import endpoint, parameter
|
2018-09-25 13:09:25 -07:00
|
|
|
|
2018-11-30 15:52:00 -08:00
|
|
|
from grant.comment.models import Comment, user_comments_schema
|
|
|
|
from grant.proposal.models import (
|
|
|
|
Proposal,
|
|
|
|
proposals_schema,
|
|
|
|
proposal_team,
|
|
|
|
ProposalTeamInvite,
|
|
|
|
invites_with_proposal_schema,
|
|
|
|
user_proposals_schema
|
|
|
|
)
|
2018-12-14 11:36:22 -08:00
|
|
|
from grant.utils.auth import requires_auth, requires_same_user_auth
|
|
|
|
from grant.utils.upload import remove_avatar, sign_avatar_upload, AvatarException
|
2018-12-17 10:33:33 -08:00
|
|
|
from grant.email.models import EmailRecovery
|
2018-12-14 11:36:22 -08:00
|
|
|
|
2018-11-13 06:17:06 -08:00
|
|
|
from .models import User, SocialMedia, Avatar, users_schema, user_schema, db
|
2018-09-25 13:09:25 -07:00
|
|
|
|
|
|
|
blueprint = Blueprint('user', __name__, url_prefix='/api/v1/users')
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/", methods=["GET"])
|
2018-10-22 15:31:33 -07:00
|
|
|
@endpoint.api(
|
|
|
|
parameter('proposalId', type=str, required=False)
|
|
|
|
)
|
|
|
|
def get_users(proposal_id):
|
2018-11-07 09:33:19 -08:00
|
|
|
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
2018-09-25 13:09:25 -07:00
|
|
|
if not proposal:
|
|
|
|
users = User.query.all()
|
|
|
|
else:
|
2018-11-13 08:07:09 -08:00
|
|
|
users = (
|
|
|
|
User.query
|
|
|
|
.join(proposal_team)
|
|
|
|
.join(Proposal)
|
|
|
|
.filter(proposal_team.c.proposal_id == proposal.id)
|
|
|
|
.all()
|
|
|
|
)
|
2018-09-25 13:09:25 -07:00
|
|
|
result = users_schema.dump(users)
|
2018-10-22 15:31:33 -07:00
|
|
|
return result
|
2018-09-26 12:42:40 -07:00
|
|
|
|
|
|
|
|
2018-10-19 22:18:27 -07:00
|
|
|
@blueprint.route("/me", methods=["GET"])
|
2018-12-14 11:36:22 -08:00
|
|
|
@requires_auth
|
2018-11-04 10:33:22 -08:00
|
|
|
@endpoint.api()
|
2018-10-19 22:18:27 -07:00
|
|
|
def get_me():
|
|
|
|
dumped_user = user_schema.dump(g.current_user)
|
2018-11-04 10:33:22 -08:00
|
|
|
return dumped_user
|
2018-10-19 22:18:27 -07:00
|
|
|
|
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/<user_id>", methods=["GET"])
|
2018-11-30 15:52:00 -08:00
|
|
|
@endpoint.api(
|
|
|
|
parameter("withProposals", type=bool, required=False),
|
|
|
|
parameter("withComments", type=bool, required=False),
|
|
|
|
parameter("withFunded", type=bool, required=False)
|
|
|
|
)
|
2018-12-14 11:36:22 -08:00
|
|
|
def get_user(user_id, with_proposals, with_comments, with_funded):
|
|
|
|
user = User.get_by_id(user_id)
|
2018-09-26 12:42:40 -07:00
|
|
|
if user:
|
|
|
|
result = user_schema.dump(user)
|
2018-11-30 15:52:00 -08:00
|
|
|
if with_proposals:
|
|
|
|
proposals = Proposal.get_by_user(user)
|
|
|
|
proposals_dump = user_proposals_schema.dump(proposals)
|
2018-12-14 11:36:22 -08:00
|
|
|
result["createdProposals"] = proposals_dump
|
2018-11-30 15:52:00 -08:00
|
|
|
if with_funded:
|
|
|
|
contributions = Proposal.get_by_user_contribution(user)
|
|
|
|
contributions_dump = user_proposals_schema.dump(contributions)
|
2018-12-14 11:36:22 -08:00
|
|
|
result["fundedProposals"] = contributions_dump
|
2018-11-30 15:52:00 -08:00
|
|
|
if with_comments:
|
|
|
|
comments = Comment.get_by_user(user)
|
|
|
|
comments_dump = user_comments_schema.dump(comments)
|
|
|
|
result["comments"] = comments_dump
|
2018-11-04 10:33:22 -08:00
|
|
|
return result
|
2018-09-26 12:42:40 -07:00
|
|
|
else:
|
2018-12-14 11:36:22 -08:00
|
|
|
message = "User with id matching {} not found".format(user_id)
|
2018-11-04 10:33:22 -08:00
|
|
|
return {"message": message}, 404
|
2018-10-01 16:22:56 -07:00
|
|
|
|
2018-10-19 22:18:27 -07:00
|
|
|
|
2018-10-01 16:22:56 -07:00
|
|
|
@blueprint.route("/", methods=["POST"])
|
2018-10-22 15:31:33 -07:00
|
|
|
@endpoint.api(
|
|
|
|
parameter('emailAddress', type=str, required=True),
|
2018-12-14 11:36:22 -08:00
|
|
|
parameter('password', type=str, required=True),
|
2018-10-22 15:31:33 -07:00
|
|
|
parameter('displayName', type=str, required=True),
|
2018-12-14 11:36:22 -08:00
|
|
|
parameter('title', type=str, required=True)
|
2018-10-22 15:31:33 -07:00
|
|
|
)
|
2018-11-07 11:08:42 -08:00
|
|
|
def create_user(
|
2018-11-13 06:17:06 -08:00
|
|
|
email_address,
|
2018-12-14 11:36:22 -08:00
|
|
|
password,
|
2018-11-13 06:17:06 -08:00
|
|
|
display_name,
|
2018-12-14 11:36:22 -08:00
|
|
|
title
|
2018-11-07 11:08:42 -08:00
|
|
|
):
|
2018-12-14 11:36:22 -08:00
|
|
|
existing_user = User.get_by_email(email_address)
|
2018-10-01 16:22:56 -07:00
|
|
|
if existing_user:
|
2018-12-14 11:36:22 -08:00
|
|
|
return {"message": "User with that email already exists"}, 409
|
2018-10-01 16:22:56 -07:00
|
|
|
|
2018-11-02 09:07:06 -07:00
|
|
|
user = User.create(
|
2018-10-01 16:22:56 -07:00
|
|
|
email_address=email_address,
|
2018-12-14 11:36:22 -08:00
|
|
|
password=password,
|
2018-10-01 16:22:56 -07:00
|
|
|
display_name=display_name,
|
|
|
|
title=title
|
|
|
|
)
|
2018-12-14 11:36:22 -08:00
|
|
|
user.login()
|
2018-10-01 16:22:56 -07:00
|
|
|
result = user_schema.dump(user)
|
2018-11-27 11:07:09 -08:00
|
|
|
return result, 201
|
2018-10-30 07:40:21 -07:00
|
|
|
|
2018-11-13 06:17:06 -08:00
|
|
|
|
2018-11-07 11:08:42 -08:00
|
|
|
@blueprint.route("/auth", methods=["POST"])
|
|
|
|
@endpoint.api(
|
2018-12-14 11:36:22 -08:00
|
|
|
parameter('email', type=str, required=True),
|
|
|
|
parameter('password', type=str, required=True)
|
2018-11-07 11:08:42 -08:00
|
|
|
)
|
2018-12-14 11:36:22 -08:00
|
|
|
def auth_user(email, password):
|
|
|
|
existing_user = User.get_by_email(email)
|
2018-11-07 11:08:42 -08:00
|
|
|
if not existing_user:
|
2018-12-14 11:36:22 -08:00
|
|
|
return {"message": "No user exists with that email"}, 400
|
|
|
|
if not existing_user.check_password(password):
|
|
|
|
return {"message": "Invalid password"}, 403
|
|
|
|
existing_user.login()
|
|
|
|
return user_schema.dump(existing_user)
|
2018-11-07 11:08:42 -08:00
|
|
|
|
2018-11-13 06:17:06 -08:00
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/password", methods=["PUT"])
|
|
|
|
@requires_auth
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('currentPassword', type=str, required=True),
|
|
|
|
parameter('password', type=str, required=True),
|
|
|
|
)
|
|
|
|
def update_user_password(current_password, password):
|
|
|
|
if not g.current_user.check_password(current_password):
|
|
|
|
return {"message": "Current password incorrect"}, 403
|
|
|
|
g.current_user.set_password(password)
|
|
|
|
return None, 200
|
2018-10-30 07:40:21 -07:00
|
|
|
|
2018-11-13 06:17:06 -08:00
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/logout", methods=["POST"])
|
|
|
|
@requires_auth
|
2018-11-16 19:33:25 -08:00
|
|
|
@endpoint.api()
|
2018-12-14 11:36:22 -08:00
|
|
|
def logout_user():
|
|
|
|
User.logout_current_user()
|
|
|
|
return None, 200
|
|
|
|
|
|
|
|
|
2018-12-17 10:33:33 -08:00
|
|
|
@blueprint.route("/recover", methods=["POST"])
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('email', type=str, required=True)
|
|
|
|
)
|
|
|
|
def recover_user(email):
|
|
|
|
existing_user = User.get_by_email(email)
|
|
|
|
if not existing_user:
|
|
|
|
return {"message": "No user exists with that email"}, 400
|
|
|
|
existing_user.send_recovery_email()
|
|
|
|
return None, 200
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/recover/<code>", methods=["POST"])
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('password', type=str, required=True),
|
|
|
|
)
|
|
|
|
def recover_email(code, password):
|
|
|
|
er = EmailRecovery.query.filter_by(code=code).first()
|
|
|
|
if er:
|
|
|
|
if er.is_expired():
|
|
|
|
return {"message": "Reset code expired"}, 401
|
|
|
|
er.user.set_password(password)
|
|
|
|
db.session.delete(er)
|
|
|
|
db.session.commit()
|
|
|
|
return None, 200
|
|
|
|
|
|
|
|
return {"message": "Invalid reset code"}, 400
|
|
|
|
|
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/avatar", methods=["POST"])
|
|
|
|
@requires_auth
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('mimetype', type=str, required=True)
|
|
|
|
)
|
|
|
|
def upload_avatar(mimetype):
|
2018-11-16 19:33:25 -08:00
|
|
|
user = g.current_user
|
|
|
|
try:
|
2018-12-14 11:36:22 -08:00
|
|
|
signed_post = sign_avatar_upload(mimetype, user.id)
|
|
|
|
return signed_post
|
|
|
|
except AvatarException as e:
|
2018-11-16 19:33:25 -08:00
|
|
|
return {"message": str(e)}, 400
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/avatar", methods=["DELETE"])
|
2018-12-14 11:36:22 -08:00
|
|
|
@requires_auth
|
2018-11-16 19:33:25 -08:00
|
|
|
@endpoint.api(
|
|
|
|
parameter('url', type=str, required=True)
|
|
|
|
)
|
|
|
|
def delete_avatar(url):
|
|
|
|
user = g.current_user
|
|
|
|
remove_avatar(url, user.id)
|
|
|
|
|
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/<user_id>", methods=["PUT"])
|
|
|
|
@requires_auth
|
2018-11-07 11:19:12 -08:00
|
|
|
@requires_same_user_auth
|
2018-10-30 07:40:21 -07:00
|
|
|
@endpoint.api(
|
2018-11-13 06:17:06 -08:00
|
|
|
parameter('displayName', type=str, required=True),
|
|
|
|
parameter('title', type=str, required=True),
|
|
|
|
parameter('socialMedias', type=list, required=True),
|
2018-11-16 15:05:17 -08:00
|
|
|
parameter('avatar', type=str, required=True)
|
2018-10-30 07:40:21 -07:00
|
|
|
)
|
2018-12-14 11:36:22 -08:00
|
|
|
def update_user(user_id, display_name, title, social_medias, avatar):
|
2018-11-13 06:17:06 -08:00
|
|
|
user = g.current_user
|
2018-10-30 07:40:21 -07:00
|
|
|
|
|
|
|
if display_name is not None:
|
|
|
|
user.display_name = display_name
|
|
|
|
|
|
|
|
if title is not None:
|
|
|
|
user.title = title
|
|
|
|
|
2018-11-16 15:05:17 -08:00
|
|
|
db_socials = SocialMedia.query.filter_by(user_id=user.id).all()
|
|
|
|
for db_social in db_socials:
|
|
|
|
db.session.delete(db_social)
|
2018-10-30 07:40:21 -07:00
|
|
|
if social_medias is not None:
|
|
|
|
for social_media in social_medias:
|
2018-11-16 15:05:17 -08:00
|
|
|
sm = SocialMedia(social_media_link=social_media, user_id=user.id)
|
2018-10-30 07:40:21 -07:00
|
|
|
db.session.add(sm)
|
2018-11-16 15:05:17 -08:00
|
|
|
|
|
|
|
db_avatar = Avatar.query.filter_by(user_id=user.id).first()
|
|
|
|
if db_avatar:
|
|
|
|
db.session.delete(db_avatar)
|
|
|
|
if avatar:
|
|
|
|
new_avatar = Avatar(image_url=avatar, user_id=user.id)
|
|
|
|
db.session.add(new_avatar)
|
2018-10-30 07:40:21 -07:00
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
old_avatar_url = db_avatar and db_avatar.image_url
|
|
|
|
if old_avatar_url and old_avatar_url != avatar:
|
|
|
|
remove_avatar(old_avatar_url, user.id)
|
2018-11-16 19:33:25 -08:00
|
|
|
|
2018-10-30 07:40:21 -07:00
|
|
|
db.session.commit()
|
|
|
|
result = user_schema.dump(user)
|
|
|
|
return result
|
2018-11-16 10:50:47 -08:00
|
|
|
|
2018-11-30 15:52:00 -08:00
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/<user_id>/invites", methods=["GET"])
|
2018-11-16 10:50:47 -08:00
|
|
|
@requires_same_user_auth
|
|
|
|
@endpoint.api()
|
2018-12-14 11:36:22 -08:00
|
|
|
def get_user_invites(user_id):
|
2018-11-16 10:50:47 -08:00
|
|
|
invites = ProposalTeamInvite.get_pending_for_user(g.current_user)
|
|
|
|
return invites_with_proposal_schema.dump(invites)
|
|
|
|
|
2018-11-30 15:52:00 -08:00
|
|
|
|
2018-12-14 11:36:22 -08:00
|
|
|
@blueprint.route("/<user_id>/invites/<invite_id>/respond", methods=["PUT"])
|
2018-11-16 10:50:47 -08:00
|
|
|
@requires_same_user_auth
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('response', type=bool, required=True)
|
|
|
|
)
|
2018-12-14 11:36:22 -08:00
|
|
|
def respond_to_invite(user_id, invite_id, response):
|
2018-11-16 10:50:47 -08:00
|
|
|
invite = ProposalTeamInvite.query.filter_by(id=invite_id).first()
|
|
|
|
if not invite:
|
|
|
|
return {"message": "No invite found with id {}".format(invite_id)}, 404
|
2018-11-30 15:52:00 -08:00
|
|
|
|
2018-11-16 10:50:47 -08:00
|
|
|
invite.accepted = response
|
|
|
|
db.session.add(invite)
|
|
|
|
|
|
|
|
if invite.accepted:
|
|
|
|
invite.proposal.team.append(g.current_user)
|
|
|
|
db.session.add(invite)
|
2018-11-30 15:52:00 -08:00
|
|
|
|
2018-11-16 10:50:47 -08:00
|
|
|
db.session.commit()
|
|
|
|
return None, 200
|