def test_set_v2_date_estimates_deadline_recalculation(self): proposal_data = test_proposal.copy() proposal = self.init_proposal(proposal_data) first_ms = proposal.milestones[0] second_ms = proposal.milestones[1] first_ms.stage = MilestoneStage.PAID first_ms.date_paid = datetime.datetime.now() expected_base_date = datetime.datetime.now() + datetime.timedelta( days=42) second_ms.stage = MilestoneStage.PAID second_ms.date_paid = expected_base_date db.session.add(proposal) db.session.commit() Milestone.set_v2_date_estimates(proposal) proposal = Proposal.query.get(proposal.id) third_ms = proposal.milestones[2] expected_date_estimated = expected_base_date + datetime.timedelta( days=int(third_ms.days_estimated)) # ensure `date_estimated` was recalculated as expected self.assertEqual(third_ms.date_estimated, expected_date_estimated)
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 create_proposals(count): user = User.query.filter_by().first() for i in range(count): if i < 5: stage = ProposalStage.WIP else: stage = ProposalStage.COMPLETED p = Proposal.create( stage=stage, status=ProposalStatus.LIVE, title=f'Fake Proposal #{i}', content=f'My fake proposal content, numero {i}', brief=f'This is proposal {i} generated by "flask create-proposals"', category=Category.ACCESSIBILITY, target="123.456", payout_address="fake123", deadline_duration=100) p.date_published = datetime.datetime.now() p.team.append(user) p.date_approved = datetime.datetime.now() p.accepted_with_funding = True p.version = '2' p.fully_fund_contibution_bounty() db.session.add(p) db.session.flush() num_ms = randint(1, 9) for j in range(num_ms): m = Milestone( title=f'Fake MS {j}', content=f'Fake milestone #{j} on fake proposal #{i}!', days_estimated='10', payout_percent=str(floor(1 / num_ms * 100)), immediate_payout=j == 0, proposal_id=p.id, index=j) db.session.add(m) for j in range(100): c = Comment(proposal_id=p.id, user_id=user.id, parent_comment_id=None, content=f'Fake comment #{j} on fake proposal #{i}!') db.session.add(c) Milestone.set_v2_date_estimates(p) db.session.add(p) db.session.commit() print(f'Added {count} LIVE fake proposals')
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 update_proposal(milestones, proposal_id, **kwargs): # Update the base proposal fields try: if g.current_proposal.status not in [ ProposalStatus.DRAFT, ProposalStatus.REJECTED ]: raise ValidationException( f"Proposal with status: {g.current_proposal.status} are not authorized for updates" ) g.current_proposal.update(**kwargs) except ValidationException as e: return {"message": "{}".format(str(e))}, 400 db.session.add(g.current_proposal) Milestone.make(milestones, g.current_proposal) # Commit db.session.commit() return proposal_schema.dump(g.current_proposal), 200
def create_proposals(count, category, stage, with_comments=False): user = User.query.filter_by().first() for i in range(count): target = "123.456" p = Proposal.create( stage=stage, status=ProposalStatus.LIVE, title=f'Fake Proposal #{i} {category} {stage}', content=f'My fake proposal content, numero {i}', brief=f'This is proposal {i} generated by e2e testing', category=category, target=target, payout_address="fake123", deadline_duration=100) p.date_published = datetime.now() p.team.append(user) db.session.add(p) db.session.flush() num_ms = randint(1, 9) for j in range(num_ms): m = Milestone( title=f'Fake MS {j}', content= f'Fake milestone #{j} on fake proposal #{i} {category} {stage}!', date_estimated=datetime.now(), payout_amount=str(float(target) / num_ms), immediate_payout=j == 0, proposal_id=p.id, index=j) db.session.add(m) # limit comment creation as it is slow if i == 0 and with_comments: for j in range(31): c = Comment( proposal_id=p.id, user_id=user.id, parent_comment_id=None, content= f'Fake comment #{j} on fake proposal #{i} {category} {stage}!' ) db.session.add(c) if stage == ProposalStage.FUNDING_REQUIRED: stake = p.create_contribution('1', None, True) stake.confirm('fakestaketxid', '1') db.session.add(stake) db.session.flush() fund = p.create_contribution('100', None, False) fund.confirm('fakefundtxid0', '100') db.session.add(fund) db.session.flush() p.status = ProposalStatus.LIVE db.session.add(p) db.session.flush()
def setUp(self): super().setUp() self._proposal = Proposal.create( status=ProposalStatus.DRAFT, title=test_proposal["title"], content=test_proposal["content"], brief=test_proposal["brief"], category=test_proposal["category"], target=test_proposal["target"], ) self._proposal.team.append(self.user) db.session.add(self._proposal) db.session.flush() milestones = [ { "title": "Milestone 1", "content": "Content 1", "date_estimated": (datetime.now() + timedelta(days=364)).timestamp(), # random unix time in the future "payout_amount": 2, "immediate_payout": True }, { "title": "Milestone 2", "content": "Content 2", "date_estimated": (datetime.now() + timedelta(days=365)).timestamp(), # random unix time in the future "payout_amount": 3, "immediate_payout": False } ] Milestone.make(milestones, self._proposal) self._other_proposal = Proposal.create(status=ProposalStatus.DRAFT) self._other_proposal.team.append(self.other_user) db.session.add(self._other_proposal) db.session.commit() self._proposal_id = self._proposal.id self._other_proposal_id = self._other_proposal.id
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 setUp(self): super().setUp() self._proposal = Proposal.create( status=ProposalStatus.DRAFT, title=test_proposal["title"], content=test_proposal["content"], brief=test_proposal["brief"], category=test_proposal["category"], target=test_proposal["target"], payout_address=test_proposal["payoutAddress"], deadline_duration=test_proposal["deadlineDuration"]) self._proposal.team.append(self.user) db.session.add(self._proposal) db.session.flush() milestones = [{ "title": "Milestone 1", "content": "Content 1", "days_estimated": "30", "payout_percent": 50, "immediate_payout": True }, { "title": "Milestone 2", "content": "Content 2", "days_estimated": "20", "payout_percent": 50, "immediate_payout": False }] Milestone.make(milestones, self._proposal) self._other_proposal = Proposal.create(status=ProposalStatus.DRAFT) self._other_proposal.team.append(self.other_user) db.session.add(self._other_proposal) db.session.commit() self._proposal_id = self._proposal.id self._other_proposal_id = self._other_proposal.id
def update_proposal(milestones, proposal_id, rfp_opt_in, **kwargs): # Update the base proposal fields try: if g.current_proposal.status not in [ ProposalStatus.DRAFT, ProposalStatus.LIVE_DRAFT, ProposalStatus.REJECTED ]: raise ValidationException( f"Proposal with status: {g.current_proposal.status} are not authorized for updates" ) g.current_proposal.update(**kwargs) except ValidationException as e: return {"message": "{}".format(str(e))}, 400 db.session.add(g.current_proposal) # twiddle rfp opt-in (modifies proposal matching and/or bounty) if rfp_opt_in is not None: g.current_proposal.update_rfp_opt_in(rfp_opt_in) Milestone.make(milestones, g.current_proposal) # Commit db.session.commit() return proposal_schema.dump(g.current_proposal), 200
def create_proposals(count): user = User.query.filter_by().first() for i in range(count): if i < 5: stage = ProposalStageEnum.WIP else: stage = ProposalStageEnum.COMPLETED target = str(randint(1, 10)) p = Proposal.create( stage=stage, status=ProposalStatus.LIVE, title=f'Fake Proposal #{i}', content=f'My fake proposal content, numero {i}', brief= f'This is proposal {i} generated by "flask create-proposals", a useful tool for generating test proposal data.', category=Category.random(), target=target, ) p.date_published = datetime.datetime.now() p.team.append(user) db.session.add(p) db.session.flush() num_ms = randint(1, 9) for j in range(num_ms): m = Milestone( title=f'Fake MS {j}', content=f'Fake milestone #{j} on fake proposal #{i}!', date_estimated=datetime.datetime.now(), payout_amount=str(float(target) / num_ms), immediate_payout=j == 0, proposal_id=p.id, index=j) db.session.add(m) for j in range(100): c = Comment(proposal_id=p.id, user_id=user.id, parent_comment_id=None, content=f'Fake comment #{j} on fake proposal #{i}!') db.session.add(c) db.session.commit() print(f'Added {count} LIVE fake proposals')
def test_milestone_deadline_noops(self, mock_send_email): # make sure all milestone deadline noop states work as expected def proposal_delete(p, m): db.session.delete(p) def proposal_status(p, m): p.status = ProposalStatus.DELETED db.session.add(p) def proposal_stage(p, m): p.stage = ProposalStage.CANCELED db.session.add(p) def milestone_delete(p, m): db.session.delete(m) def milestone_date_requested(p, m): m.date_requested = datetime.now() db.session.add(m) def update_posted(p, m): # login and post proposal update self.login_default_user() resp = self.app.post("/api/v1/proposals/{}/updates".format( proposal.id), data=json.dumps(test_update), content_type='application/json') self.assertStatus(resp, 201) modifiers = [ proposal_delete, proposal_status, proposal_stage, milestone_delete, milestone_date_requested, update_posted ] for modifier in modifiers: # make proposal and milestone proposal = Proposal.create(status=ProposalStatus.LIVE) proposal.arbiter.user = self.other_user proposal.team.append(self.user) proposal_id = proposal.id Milestone.make(milestones_data, proposal) db.session.add(proposal) db.session.commit() # grab update count for blob update_count = len( ProposalUpdate.query.filter_by(proposal_id=proposal_id).all()) # run modifications to trigger noop proposal = Proposal.query.get(proposal_id) milestone = proposal.milestones[0] milestone_id = milestone.id modifier(proposal, milestone) db.session.commit() # make task blob = { "proposal_id": proposal_id, "milestone_id": milestone_id, "update_count": update_count } task = Task(job_type=MilestoneDeadline.JOB_TYPE, blob=blob, execute_after=datetime.now()) # run task MilestoneDeadline.process_task(task) # check to make sure noop occurred mock_send_email.assert_not_called()
def milestones(p): Milestone.make(milestones_data, p)
def make_proposal(crowd_fund_contract_address, content, title, milestones, category, team): existing_proposal = Proposal.query.filter_by(proposal_address=crowd_fund_contract_address).first() if existing_proposal: return {"message": "Oops! Something went wrong."}, 409 proposal = Proposal.create( stage="FUNDING_REQUIRED", proposal_address=crowd_fund_contract_address, content=content, title=title, category=category ) db.session.add(proposal) if not len(team) > 0: return {"message": "Team must be at least 1"}, 400 for team_member in team: account_address = team_member.get("accountAddress") display_name = team_member.get("displayName") email_address = team_member.get("emailAddress") title = team_member.get("title") user = User.query.filter( (User.account_address == account_address) | (User.email_address == email_address)).first() if not user: user = User( account_address=account_address, email_address=email_address, display_name=display_name, title=title ) db.session.add(user) db.session.flush() avatar_data = team_member.get("avatar") if avatar_data: avatar = Avatar(image_url=avatar_data.get('link'), user_id=user.id) db.session.add(avatar) social_medias = team_member.get("socialMedias") if social_medias: for social_media in social_medias: sm = SocialMedia(social_media_link=social_media.get("link"), user_id=user.id) db.session.add(sm) proposal.team.append(user) for each_milestone in milestones: m = Milestone( title=each_milestone["title"], content=each_milestone["description"], date_estimated=datetime.strptime(each_milestone["date"], '%B %Y'), payout_percent=str(each_milestone["payoutPercent"]), immediate_payout=each_milestone["immediatePayout"], proposal_id=proposal.id ) db.session.add(m) try: db.session.commit() except IntegrityError as e: print(e) return {"message": "Oops! Something went wrong."}, 409 results = proposal_schema.dump(proposal) return results, 201