def approve_pending(self, is_approve, reject_reason=None): self.validate_publishable() # specific validation if not self.status == ProposalStatus.PENDING: raise ValidationException(f"Proposal must be pending to approve or reject") if is_approve: self.status = ProposalStatus.APPROVED self.date_approved = datetime.datetime.now() for t in self.team: send_email(t.email_address, 'proposal_approved', { 'user': t, 'proposal': self, 'proposal_url': make_url(f'/proposals/{self.id}'), 'admin_note': 'Congratulations! Your proposal has been approved.' }) else: if not reject_reason: raise ValidationException("Please provide a reason for rejecting the proposal") self.status = ProposalStatus.REJECTED self.reject_reason = reject_reason for t in self.team: send_email(t.email_address, 'proposal_rejected', { 'user': t, 'proposal': self, 'proposal_url': make_url(f'/proposals/{self.id}'), 'admin_note': reject_reason })
def rfw_exists_check(id): rfw = RFW.query.get(id) if not rfw: raise ValidationException("No RFW matching id") if rfw.status in [RFWStatus.DRAFT]: raise ValidationException("RFW is not viewable") return rfw
def simple_validate(proposal): # Validate fields to be database save-able. # Stricter validation is done in validate_publishable. stage = proposal.get('stage') category = proposal.get('category') if stage and not ProposalStage.includes(stage): raise ValidationException("Proposal stage {} is not a valid stage".format(stage)) if category and not Category.includes(category): raise ValidationException("Category {} not a valid category".format(category))
def validate_publishable(self): # Require certain fields required_fields = ['title', 'content', 'brief', 'target'] for field in required_fields: if not hasattr(self, field): raise ValidationException("Proposal must have a {}".format(field)) # Stricter limits on certain fields if len(self.title) > 60: raise ValidationException("Proposal title cannot be longer than 60 characters") if len(self.brief) > 140: raise ValidationException("Brief cannot be longer than 140 characters") if len(self.content) > 250000: raise ValidationException("Content cannot be longer than 250,000 characters")
def validate(user): em = user.get('email_address') if not em: raise ValidationException('Must have email address') if not is_email(em): raise ValidationException('Email address looks invalid') t = user.get('title') if t and len(t) > 255: raise ValidationException('Title is too long') dn = user.get('display_name') if dn and len(dn) > 255: raise ValidationException('Display name is too long')
def submit_for_approval(self): self.validate_publishable() allowed_statuses = [CCRStatus.DRAFT, CCRStatus.REJECTED] # specific validation if self.status not in allowed_statuses: raise ValidationException(f"CCR status must be draft or rejected to submit for approval") self.set_pending()
def publish(self): self.validate_publishable() # specific validation if not self.status == ProposalStatus.APPROVED: raise ValidationException(f"Proposal status must be approved") self.date_published = datetime.datetime.now() self.status = ProposalStatus.LIVE self.stage = ProposalStage.WIP
def approve_pending(self, is_approve, reject_reason=None): from grant.rfp.models import RFP self.validate_publishable() # specific validation if not self.status == CCRStatus.PENDING: raise ValidationException(f"CCR must be pending to approve or reject") if is_approve: self.status = CCRStatus.LIVE rfp = RFP( title=self.title, brief=self.brief, content=self.content, bounty=self._target, date_closes=datetime.now() + timedelta(days=90), ) db.session.add(self) db.session.add(rfp) db.session.flush() self.rfp_id = rfp.id db.session.add(rfp) db.session.flush() # for emails db.session.commit() send_email(self.author.email_address, 'ccr_approved', { 'user': self.author, 'ccr': self, 'admin_note': f'Congratulations! Your Request has been accepted. There may be a delay between acceptance and final posting as required by the Zcash Foundation.' }) return rfp.id else: if not reject_reason: raise ValidationException("Please provide a reason for rejecting the ccr") self.status = CCRStatus.REJECTED self.reject_reason = reject_reason # for emails db.session.add(self) db.session.commit() send_email(self.author.email_address, 'ccr_rejected', { 'user': self.author, 'ccr': self, 'admin_note': reject_reason }) return None
def validate_publishable_milestones(self): payout_total = 0.0 for i, milestone in enumerate(self.milestones): if milestone.immediate_payout and i != 0: raise ValidationException("Only the first milestone can have an immediate payout") if len(milestone.title) > 60: raise ValidationException("Milestone title cannot be longer than 60 chars") if len(milestone.content) > 200: raise ValidationException("Milestone content cannot be longer than 200 chars") try: p = float(milestone.payout_amount) if not p.is_integer(): raise ValidationException("Milestone payout must be whole numbers, no decimals") if p <= 0: raise ValidationException("Milestone payout must be greater than zero") except ValueError: raise ValidationException("Milestone payout percent must be a number") payout_total += p if payout_total != float(self.target): raise ValidationException("Payout of milestones must add up to proposal target")
def validate_publishable(self): self.validate_publishable_milestones() # Require certain fields required_fields = ['title', 'content', 'brief', 'category', 'target'] for field in required_fields: if not hasattr(self, field): raise ValidationException("Proposal must have a {}".format(field)) # Stricter limits on certain fields if len(self.title) > 60: raise ValidationException("Proposal title cannot be longer than 60 characters") if len(self.brief) > 140: raise ValidationException("Brief cannot be longer than 140 characters") if len(self.content) > 250000: raise ValidationException("Content cannot be longer than 250,000 characters") # Then run through regular validation Proposal.simple_validate(vars(self))
def email_subscriptions_to_bits(subs: dict): validate_email_subscriptions(subs) settings = 0 for s in EmailSubscription: sk = s.value['key'] si = s.value['bit'] if sk not in subs: raise ValidationException('Missing email_subscriptions key: {}'.format(sk)) settings = set_bitmap_state(settings, si, subs[sk]) return settings
def submit_for_approval(self): self.validate_publishable() self.validate_milestone_dates() allowed_statuses = [ProposalStatus.DRAFT, ProposalStatus.REJECTED] # specific validation if self.status not in allowed_statuses: raise ValidationException(f"Proposal status must be draft or rejected to submit for approval") self.send_admin_email('admin_approval') self.status = ProposalStatus.PENDING db.session.add(self) db.session.flush()
def cancel(self): if self.status != ProposalStatus.LIVE: raise ValidationException("Cannot cancel a proposal until it's live") self.stage = ProposalStage.CANCELED db.session.add(self) db.session.flush() # Send emails to team & contributors for u in self.team: send_email(u.email_address, 'proposal_canceled', { 'proposal': self, 'support_url': make_url('/contact'), })
def update_ccr(ccr_id, **kwargs): try: if g.current_ccr.status not in [CCRStatus.DRAFT, CCRStatus.REJECTED]: raise ValidationException( f"CCR with status: {g.current_ccr.status} are not authorized for updates" ) g.current_ccr.update(**kwargs) except ValidationException as e: return {"message": "{}".format(str(e))}, 400 db.session.add(g.current_ccr) # Commit db.session.commit() return ccr_schema.dump(g.current_ccr), 200
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 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 validate_email_subscriptions(subs: dict): for k in subs: if not is_email_sub_key(k): raise ValidationException('Invalid email_subcriptions key: {}'.format(k))
def validate_milestone_dates(self): present = datetime.datetime.today().replace(day=1, hour=0, minute=0, second=0, microsecond=0) for milestone in self.milestones: if present > milestone.date_estimated: raise ValidationException("Milestone date estimate must be in the future ")