2018-11-14 09:59:48 -08:00
|
|
|
from dateutil.parser import parse
|
2018-11-07 11:19:12 -08:00
|
|
|
from functools import wraps
|
2018-09-10 09:55:26 -07:00
|
|
|
|
2018-11-07 11:19:12 -08:00
|
|
|
from flask import Blueprint, g
|
2018-10-22 15:31:33 -07:00
|
|
|
from flask_yoloapi import endpoint, parameter
|
2018-09-10 09:55:26 -07:00
|
|
|
from sqlalchemy.exc import IntegrityError
|
|
|
|
|
2018-09-18 15:17:34 -07:00
|
|
|
from grant.comment.models import Comment, comment_schema
|
2018-09-10 09:55:26 -07:00
|
|
|
from grant.milestone.models import Milestone
|
2018-09-25 13:09:25 -07:00
|
|
|
from grant.user.models import User, SocialMedia, Avatar
|
2018-11-07 11:19:12 -08:00
|
|
|
from grant.utils.auth import requires_sm, requires_team_member_auth
|
2018-11-13 08:07:09 -08:00
|
|
|
from grant.utils.exceptions import ValidationException
|
|
|
|
from .models import Proposal, proposals_schema, proposal_schema, ProposalUpdate, proposal_update_schema, proposal_team, db
|
2018-09-10 09:55:26 -07:00
|
|
|
|
2018-09-18 15:20:17 -07:00
|
|
|
blueprint = Blueprint("proposal", __name__, url_prefix="/api/v1/proposals")
|
2018-09-10 09:55:26 -07:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/<proposal_id>", methods=["GET"])
|
2018-11-04 10:33:22 -08:00
|
|
|
@endpoint.api()
|
2018-09-10 09:55:26 -07:00
|
|
|
def get_proposal(proposal_id):
|
2018-11-07 09:33:19 -08:00
|
|
|
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
2018-09-10 09:55:26 -07:00
|
|
|
if proposal:
|
|
|
|
dumped_proposal = proposal_schema.dump(proposal)
|
2018-11-04 10:33:22 -08:00
|
|
|
return dumped_proposal
|
2018-09-10 09:55:26 -07:00
|
|
|
else:
|
2018-11-04 10:33:22 -08:00
|
|
|
return {"message": "No proposal matching id"}, 404
|
2018-09-10 09:55:26 -07:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/<proposal_id>/comments", methods=["GET"])
|
2018-11-04 10:33:22 -08:00
|
|
|
@endpoint.api()
|
2018-09-10 09:55:26 -07:00
|
|
|
def get_proposal_comments(proposal_id):
|
2018-11-07 09:33:19 -08:00
|
|
|
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
2018-09-18 15:17:34 -07:00
|
|
|
if proposal:
|
|
|
|
dumped_proposal = proposal_schema.dump(proposal)
|
2018-11-04 10:33:22 -08:00
|
|
|
return {
|
|
|
|
"proposalId": proposal_id,
|
|
|
|
"totalComments": len(dumped_proposal["comments"]),
|
|
|
|
"comments": dumped_proposal["comments"]
|
|
|
|
}
|
2018-09-10 09:55:26 -07:00
|
|
|
else:
|
2018-11-04 10:33:22 -08:00
|
|
|
return {"message": "No proposal matching id"}, 404
|
2018-09-10 09:55:26 -07:00
|
|
|
|
|
|
|
|
2018-09-18 15:17:34 -07:00
|
|
|
@blueprint.route("/<proposal_id>/comments", methods=["POST"])
|
2018-11-07 11:19:12 -08:00
|
|
|
@requires_sm
|
2018-10-22 15:31:33 -07:00
|
|
|
@endpoint.api(
|
|
|
|
parameter('content', type=str, required=True)
|
|
|
|
)
|
|
|
|
def post_proposal_comments(proposal_id, user_id, content):
|
2018-11-07 09:33:19 -08:00
|
|
|
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
2018-09-18 15:17:34 -07:00
|
|
|
if proposal:
|
2018-11-07 11:19:12 -08:00
|
|
|
comment = Comment(
|
|
|
|
proposal_id=proposal_id,
|
|
|
|
user_id=g.current_user.id,
|
|
|
|
content=content
|
|
|
|
)
|
|
|
|
db.session.add(comment)
|
|
|
|
db.session.commit()
|
|
|
|
dumped_comment = comment_schema.dump(comment)
|
|
|
|
return dumped_comment, 201
|
2018-09-18 15:17:34 -07:00
|
|
|
else:
|
2018-10-22 15:31:33 -07:00
|
|
|
return {"message": "No proposal matching id"}, 404
|
2018-09-18 15:17:34 -07:00
|
|
|
|
|
|
|
|
2018-09-10 09:55:26 -07:00
|
|
|
@blueprint.route("/", methods=["GET"])
|
2018-10-22 15:31:33 -07:00
|
|
|
@endpoint.api(
|
|
|
|
parameter('stage', type=str, required=False)
|
|
|
|
)
|
|
|
|
def get_proposals(stage):
|
2018-09-10 09:55:26 -07:00
|
|
|
if stage:
|
|
|
|
proposals = (
|
2018-11-13 08:07:09 -08:00
|
|
|
Proposal.query.filter_by(status="LIVE", stage=stage)
|
2018-11-13 06:17:06 -08:00
|
|
|
.order_by(Proposal.date_created.desc())
|
|
|
|
.all()
|
2018-09-10 09:55:26 -07:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
proposals = Proposal.query.order_by(Proposal.date_created.desc()).all()
|
2018-09-25 13:09:25 -07:00
|
|
|
dumped_proposals = proposals_schema.dump(proposals)
|
2018-10-22 15:31:33 -07:00
|
|
|
return dumped_proposals
|
2018-09-10 09:55:26 -07:00
|
|
|
|
2018-11-13 08:07:09 -08:00
|
|
|
@blueprint.route("/drafts", methods=["POST"])
|
|
|
|
@requires_sm
|
|
|
|
@endpoint.api()
|
|
|
|
def make_proposal_draft():
|
|
|
|
proposal = Proposal.create(status="DRAFT")
|
|
|
|
proposal.team.append(g.current_user)
|
|
|
|
db.session.add(proposal)
|
|
|
|
db.session.commit()
|
|
|
|
return proposal_schema.dump(proposal), 201
|
2018-09-10 09:55:26 -07:00
|
|
|
|
2018-11-13 08:07:09 -08:00
|
|
|
@blueprint.route("/drafts", methods=["GET"])
|
2018-11-07 11:19:12 -08:00
|
|
|
@requires_sm
|
2018-11-13 08:07:09 -08:00
|
|
|
@endpoint.api()
|
|
|
|
def get_proposal_drafts():
|
|
|
|
proposals = (
|
|
|
|
Proposal.query
|
|
|
|
.filter_by(status="DRAFT")
|
|
|
|
.join(proposal_team)
|
|
|
|
.filter(proposal_team.c.user_id == g.current_user.id)
|
|
|
|
.order_by(Proposal.date_created.desc())
|
|
|
|
.all()
|
2018-09-10 09:55:26 -07:00
|
|
|
)
|
2018-11-13 08:07:09 -08:00
|
|
|
return proposals_schema.dump(proposals), 200
|
2018-09-10 09:55:26 -07:00
|
|
|
|
2018-11-13 08:07:09 -08:00
|
|
|
@blueprint.route("/<proposal_id>", methods=["PUT"])
|
|
|
|
@requires_team_member_auth
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('title', type=str),
|
|
|
|
parameter('brief', type=str),
|
|
|
|
parameter('category', type=str),
|
2018-11-14 09:27:40 -08:00
|
|
|
parameter('content', type=str),
|
2018-11-13 08:07:09 -08:00
|
|
|
parameter('target', type=str),
|
|
|
|
parameter('payoutAddress', type=str),
|
|
|
|
parameter('trustees', type=list),
|
|
|
|
parameter('deadlineDuration', type=int),
|
|
|
|
parameter('voteDuration', type=int),
|
|
|
|
parameter('milestones', type=list)
|
|
|
|
)
|
2018-11-14 08:43:00 -08:00
|
|
|
def update_proposal(milestones, proposal_id, **kwargs):
|
2018-11-13 08:07:09 -08:00
|
|
|
# Update the base proposal fields
|
|
|
|
try:
|
2018-11-14 08:43:00 -08:00
|
|
|
g.current_proposal.update(**kwargs)
|
2018-11-13 08:07:09 -08:00
|
|
|
except ValidationException as e:
|
|
|
|
return {"message": "Invalid proposal parameters: {}".format(str(e))}, 400
|
|
|
|
db.session.add(g.current_proposal)
|
2018-09-26 01:46:30 -07:00
|
|
|
|
2018-11-13 08:07:09 -08:00
|
|
|
# Delete & re-add milestones
|
2018-11-14 09:59:48 -08:00
|
|
|
[db.session.delete(x) for x in g.current_proposal.milestones]
|
2018-11-13 08:07:09 -08:00
|
|
|
if milestones:
|
|
|
|
for mdata in milestones:
|
|
|
|
m = Milestone(
|
|
|
|
title=mdata["title"],
|
2018-11-14 09:59:48 -08:00
|
|
|
content=mdata["content"],
|
|
|
|
date_estimated=parse(mdata["dateEstimated"]),
|
2018-11-13 08:07:09 -08:00
|
|
|
payout_percent=str(mdata["payoutPercent"]),
|
|
|
|
immediate_payout=mdata["immediatePayout"],
|
|
|
|
proposal_id=g.current_proposal.id
|
2018-09-25 13:09:25 -07:00
|
|
|
)
|
2018-11-13 08:07:09 -08:00
|
|
|
db.session.add(m)
|
|
|
|
|
|
|
|
# Commit
|
|
|
|
db.session.commit()
|
|
|
|
return proposal_schema.dump(g.current_proposal), 200
|
2018-09-10 09:55:26 -07:00
|
|
|
|
2018-11-14 08:43:00 -08:00
|
|
|
|
|
|
|
@blueprint.route("/<proposal_id>", methods=["DELETE"])
|
2018-11-13 08:07:09 -08:00
|
|
|
@requires_team_member_auth
|
|
|
|
@endpoint.api()
|
2018-11-15 08:02:16 -08:00
|
|
|
def delete_proposal_draft(proposal_id):
|
2018-11-13 08:07:09 -08:00
|
|
|
if g.current_proposal.status != 'DRAFT':
|
|
|
|
return {"message": "Cannot delete non-draft proposals"}, 400
|
|
|
|
db.session.delete(g.current_proposal)
|
|
|
|
db.session.commit()
|
|
|
|
return None, 202
|
2018-09-26 01:46:30 -07:00
|
|
|
|
2018-11-14 08:43:00 -08:00
|
|
|
|
2018-11-13 08:07:09 -08:00
|
|
|
@blueprint.route("/<proposal_id>/publish", methods=["PUT"])
|
|
|
|
@requires_team_member_auth
|
|
|
|
@endpoint.api(
|
|
|
|
parameter('contractAddress', type=str, required=True)
|
|
|
|
)
|
2018-11-14 14:03:50 -08:00
|
|
|
def publish_proposal(proposal_id, contract_address):
|
2018-09-10 09:55:26 -07:00
|
|
|
try:
|
2018-11-13 08:07:09 -08:00
|
|
|
g.current_proposal.proposal_address = contract_address
|
|
|
|
g.current_proposal.publish()
|
|
|
|
except ValidationException as e:
|
|
|
|
return {"message": "Invalid proposal parameters: {}".format(str(e))}, 400
|
|
|
|
db.session.add(g.current_proposal)
|
|
|
|
db.session.commit()
|
|
|
|
return proposal_schema.dump(g.current_proposal), 200
|
|
|
|
|
|
|
|
|
2018-11-02 09:24:28 -07:00
|
|
|
@blueprint.route("/<proposal_id>/updates", methods=["GET"])
|
|
|
|
@endpoint.api()
|
|
|
|
def get_proposal_updates(proposal_id):
|
2018-11-07 09:33:19 -08:00
|
|
|
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
2018-11-02 09:24:28 -07:00
|
|
|
if proposal:
|
|
|
|
dumped_proposal = proposal_schema.dump(proposal)
|
|
|
|
return dumped_proposal["updates"]
|
|
|
|
else:
|
|
|
|
return {"message": "No proposal matching id"}, 404
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/<proposal_id>/updates/<update_id>", methods=["GET"])
|
|
|
|
@endpoint.api()
|
|
|
|
def get_proposal_update(proposal_id, update_id):
|
2018-11-07 09:33:19 -08:00
|
|
|
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
2018-11-02 09:24:28 -07:00
|
|
|
if proposal:
|
|
|
|
update = ProposalUpdate.query.filter_by(proposal_id=proposal.id, id=update_id).first()
|
|
|
|
if update:
|
|
|
|
return update
|
|
|
|
else:
|
|
|
|
return {"message": "No update matching id"}
|
|
|
|
else:
|
|
|
|
return {"message": "No proposal matching id"}, 404
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/<proposal_id>/updates", methods=["POST"])
|
2018-11-07 11:19:12 -08:00
|
|
|
@requires_team_member_auth
|
2018-11-13 06:17:06 -08:00
|
|
|
@requires_sm
|
2018-11-02 09:24:28 -07:00
|
|
|
@endpoint.api(
|
|
|
|
parameter('title', type=str, required=True),
|
|
|
|
parameter('content', type=str, required=True)
|
|
|
|
)
|
|
|
|
def post_proposal_update(proposal_id, title, content):
|
2018-11-07 11:19:12 -08:00
|
|
|
update = ProposalUpdate(
|
|
|
|
proposal_id=g.current_proposal.id,
|
|
|
|
title=title,
|
|
|
|
content=content
|
|
|
|
)
|
|
|
|
db.session.add(update)
|
|
|
|
db.session.commit()
|
2018-11-02 09:24:28 -07:00
|
|
|
|
2018-11-07 11:19:12 -08:00
|
|
|
dumped_update = proposal_update_schema.dump(update)
|
|
|
|
return dumped_update, 201
|