def reject_permanently_proposal(proposal_id, reject_reason): proposal = Proposal.query.get(proposal_id) if not proposal: return {"message": "No proposal found."}, 404 reject_permanently_statuses = [ ProposalStatus.REJECTED, ProposalStatus.PENDING ] if proposal.status not in reject_permanently_statuses: return {"message": "Proposal status is not REJECTED or PENDING."}, 401 proposal.status = ProposalStatus.REJECTED_PERMANENTLY proposal.reject_reason = reject_reason db.session.add(proposal) db.session.commit() for user in proposal.team: send_email( user.email_address, 'proposal_rejected_permanently', { 'user': user, 'proposal': proposal, 'proposal_url': make_url(f'/proposals/{proposal.id}'), 'admin_note': reject_reason, 'profile_rejected_url': make_url('/profile?tab=rejected'), }) return proposal_schema.dump(proposal)
def paid_milestone_payout_request(id, mid): proposal = Proposal.query.filter_by(id=id).first() if not proposal: return {"message": "No proposal matching id"}, 404 if not proposal.status == ProposalStatus.LIVE: return {"message": "Proposal is not live"}, 400 for ms in proposal.milestones: if ms.id == int(mid): ms.mark_paid() admin.admin_log("MILESTONE_PAID", f"Paid milestone #{ms.index + 1} ({ms.title}) for proposal {proposal.id} ({proposal.title})") db.session.add(ms) db.session.flush() # check if this is the final ms, and update proposal.stage num_paid = reduce(lambda a, x: a + (1 if x.stage == MilestoneStage.PAID else 0), proposal.milestones, 0) if num_paid == len(proposal.milestones): proposal.stage = ProposalStage.COMPLETED # WIP -> COMPLETED db.session.add(proposal) db.session.flush() db.session.commit() # email TEAM that payout request was PAID for member in proposal.team: send_email(member.email_address, 'milestone_paid', { 'proposal': proposal, 'milestone': ms, 'amount': ms.payout_amount, 'proposal_milestones_url': make_url(f'/proposals/{proposal.id}?tab=milestones'), }) # email FOLLOWERS that milestone was accepted proposal.send_follower_email('followed_proposal_milestone', email_args={'milestone':ms}, url_suffix='?tab=milestones') return proposal_schema.dump(proposal), 200 return {"message": "No milestone matching id"}, 404
def cancel_proposal(id): proposal = Proposal.query.filter_by(id=id).first() if not proposal: return {"message": "No proposal found."}, 404 proposal.cancel() db.session.add(proposal) db.session.commit() return proposal_schema.dump(proposal)
def resolve_changes_discussion(proposal_id): proposal = Proposal.query.get(proposal_id) if not proposal: return {"message": "No proposal found"}, 404 proposal.resolve_changes_discussion() db.session.add(proposal) db.session.commit() return proposal_schema.dump(proposal)
def open_proposal_for_discussion(proposal_id, is_open_for_discussion, reject_reason=None): proposal = Proposal.query.get(proposal_id) if not proposal: return {"message": "No Proposal found."}, 404 proposal.approve_discussion(is_open_for_discussion, reject_reason) db.session.commit() return proposal_schema.dump(proposal)
def cancel_proposal(id): proposal = Proposal.query.filter_by(id=id).first() if not proposal: return {"message": "No proposal found."}, 404 proposal.cancel() admin.admin_log("PROPOSAL_CANCEL", f"Canceled proposal {proposal.id} ({proposal.title})") db.session.add(proposal) db.session.commit() return proposal_schema.dump(proposal)
def paid_milestone_payout_request(id, mid, tx_id): proposal = Proposal.query.filter_by(id=id).first() if not proposal: return {"message": "No proposal matching id"}, 404 if not proposal.is_funded: return {"message": "Proposal is not fully funded"}, 400 for ms in proposal.milestones: if ms.id == int(mid): is_final_milestone = False ms.mark_paid(tx_id) db.session.add(ms) db.session.flush() # check if this is the final ms, and update proposal.stage num_paid = reduce( lambda a, x: a + (1 if x.stage == MilestoneStage.PAID else 0), proposal.milestones, 0) if num_paid == len(proposal.milestones): is_final_milestone = True proposal.stage = ProposalStage.COMPLETED # WIP -> COMPLETED db.session.add(proposal) db.session.flush() db.session.commit() # email TEAM that payout request was PAID amount = Decimal(ms.payout_percent) * Decimal( proposal.target) / 100 for member in proposal.team: send_email( member.email_address, 'milestone_paid', { 'proposal': proposal, 'milestone': ms, 'amount': amount, 'tx_explorer_url': make_explore_url(tx_id), 'proposal_milestones_url': make_url(f'/proposals/{proposal.id}?tab=milestones'), }) # email FOLLOWERS that milestone was accepted proposal.send_follower_email( "followed_proposal_milestone", email_args={"milestone": ms}, url_suffix="?tab=milestones", ) if not is_final_milestone: Milestone.set_v2_date_estimates(proposal) db.session.commit() return proposal_schema.dump(proposal), 200 return {"message": "No milestone matching id"}, 404
def approve_proposal(id, is_approve, reject_reason=None): proposal = Proposal.query.filter_by(id=id).first() if proposal: proposal.approve_pending(is_approve, reject_reason) if is_approve: admin.admin_log("PROPOSAL_APPROVE", f"Approved proposal {proposal.id} ({proposal.title})") else: admin.admin_log("PROPOSAL_REJECT", f"Rejected proposal {proposal.id} ({proposal.title}) for reason '{reject_reason}'") db.session.commit() return proposal_schema.dump(proposal) return {"message": "No proposal found."}, 404
def update_proposal(id, private): proposal = Proposal.query.filter(Proposal.id == id).first() if not proposal: return {"message": f"Could not find proposal with id {id}"}, 404 if private != None: proposal.private = private db.session.add(proposal) db.session.commit() return proposal_schema.dump(proposal)
def accept_milestone_payout_request(proposal_id, milestone_id): proposal = Proposal.query.filter_by(id=proposal_id).first() if not proposal: return {"message": "No proposal matching id"}, 404 for ms in proposal.milestones: if ms.id == int(milestone_id): ms.accept_request() admin.admin_log("MILESTONE_APPROVE", f"Approved milestone #{ms.index + 1} ({ms.title}) for proposal {proposal.id} ({proposal.title})") db.session.commit() # NOTE - no notification here, do it on PAID return proposal_schema.dump(proposal), 200 return {"message": "No milestone matching id"}, 404
def accept_proposal(id, is_accepted, with_funding, changes_requested_reason): proposal = Proposal.query.get(id) if not proposal: return {"message": "No proposal found."}, 404 if is_accepted: proposal.accept_proposal(with_funding) if with_funding: Milestone.set_v2_date_estimates(proposal) else: proposal.request_changes_discussion(changes_requested_reason) db.session.add(proposal) db.session.commit() return proposal_schema.dump(proposal)
def set_arbiter(proposal_id, user_id): proposal = Proposal.query.filter(Proposal.id == proposal_id).first() if not proposal: return {"message": "Proposal not found"}, 404 for member in proposal.team: if member.id == user_id: return { "message": "Cannot set proposal team member as arbiter" }, 400 if proposal.is_failed: return {"message": "Cannot set arbiter on failed proposal"}, 400 if proposal.version == '2' and not proposal.accepted_with_funding: return { "message": "Cannot set arbiter, proposal has not been accepted with funding" }, 400 user = User.query.filter(User.id == user_id).first() if not user: return {"message": "User not found"}, 404 # send email code = user.email_verification.code send_email( user.email_address, 'proposal_arbiter', { 'proposal': proposal, 'proposal_url': make_url(f'/proposals/{proposal.id}'), 'accept_url': make_url(f'/email/arbiter?code={code}&proposalId={proposal.id}'), }) proposal.arbiter.user = user proposal.arbiter.status = ProposalArbiterStatus.NOMINATED db.session.add(proposal.arbiter) db.session.commit() return { 'proposal': proposal_schema.dump(proposal), 'user': admin_user_schema.dump(user) }, 200
def reject_milestone_payout_request(proposal_id, milestone_id, reason): proposal = Proposal.query.filter_by(id=proposal_id).first() if not proposal: return {"message": "No proposal matching id"}, 404 for ms in proposal.milestones: if ms.id == int(milestone_id): ms.reject_request(reason) admin.admin_log("MILESTONE_REJECT", f"Rejected milestone #{ms.index + 1} ({ms.title}) for proposal {proposal.id} ({proposal.title}) with reason '{reason}'") db.session.add(ms) db.session.commit() # email TEAM that payout request was rejected for member in proposal.team: send_email(member.email_address, 'milestone_reject', { 'proposal': proposal, 'admin_note': reason, 'proposal_milestones_url': make_url(f'/proposals/{proposal.id}?tab=milestones'), }) return proposal_schema.dump(proposal), 200 return {"message": "No milestone matching id"}, 404
def change_proposal_to_accepted_with_funding(id): proposal = Proposal.query.filter_by(id=id).first() if not proposal: return {"message": "No proposal found."}, 404 if proposal.accepted_with_funding: return {"message": "Proposal already accepted with funding."}, 404 if proposal.version != '2': return { "message": "Only version two proposals can be accepted with funding" }, 404 if proposal.status != ProposalStatus.LIVE and proposal.status != ProposalStatus.APPROVED: return { "message": "Only live or approved proposals can be modified by this endpoint" }, 404 proposal.update_proposal_with_funding() Milestone.set_v2_date_estimates(proposal) db.session.add(proposal) db.session.commit() return proposal_schema.dump(proposal)
def init_proposal(self, proposal_data): self.login_default_user() resp = self.app.post("/api/v1/proposals/drafts") self.assertStatus(resp, 201) proposal_id = resp.json["proposalId"] resp = self.app.put(f"/api/v1/proposals/{proposal_id}", data=json.dumps(proposal_data), content_type='application/json') self.assert200(resp) proposal = Proposal.query.get(proposal_id) proposal.status = ProposalStatus.DISCUSSION # accept with funding proposal.accept_proposal(True) Milestone.set_v2_date_estimates(proposal) db.session.add(proposal) db.session.commit() print(proposal_schema.dump(proposal)) return proposal
def get_proposal(id): proposal = Proposal.query.filter(Proposal.id == id).first() if proposal: return proposal_schema.dump(proposal) return {"message": f"Could not find proposal with id {id}"}, 404