Prune Empty Drafts (#54)
* prune empty drafts after 72 hours * add additional noops, update tests
This commit is contained in:
parent
494303883a
commit
c66be86c54
|
@ -14,7 +14,7 @@ from grant.milestone.models import Milestone
|
|||
from grant.parser import body, query, paginated_fields
|
||||
from grant.rfp.models import RFP
|
||||
from grant.settings import PROPOSAL_STAKING_AMOUNT
|
||||
from grant.task.jobs import ProposalDeadline
|
||||
from grant.task.jobs import ProposalDeadline, PruneDraft
|
||||
from grant.user.models import User
|
||||
from grant.utils import pagination
|
||||
from grant.utils.auth import (
|
||||
|
@ -196,6 +196,9 @@ def make_proposal_draft(rfp_id):
|
|||
rfp.proposals.append(proposal)
|
||||
db.session.add(rfp)
|
||||
|
||||
task = PruneDraft(proposal)
|
||||
task.make_task()
|
||||
|
||||
db.session.add(proposal)
|
||||
db.session.commit()
|
||||
return proposal_schema.dump(proposal), 201
|
||||
|
|
|
@ -2,7 +2,7 @@ from datetime import datetime, timedelta
|
|||
|
||||
from grant.extensions import db
|
||||
from grant.email.send import send_email
|
||||
from grant.utils.enums import ProposalStage, ContributionStatus
|
||||
from grant.utils.enums import ProposalStage, ContributionStatus, ProposalStatus
|
||||
from grant.utils.misc import make_url
|
||||
from flask import current_app
|
||||
|
||||
|
@ -126,8 +126,53 @@ class ContributionExpired:
|
|||
})
|
||||
|
||||
|
||||
class PruneDraft:
|
||||
JOB_TYPE = 4
|
||||
PRUNE_TIME = 259200 # 72 hours in seconds
|
||||
|
||||
def __init__(self, proposal):
|
||||
self.proposal = proposal
|
||||
|
||||
def blobify(self):
|
||||
return {
|
||||
"proposal_id": self.proposal.id,
|
||||
}
|
||||
|
||||
def make_task(self):
|
||||
from .models import Task
|
||||
|
||||
task = Task(
|
||||
job_type=self.JOB_TYPE,
|
||||
blob=self.blobify(),
|
||||
execute_after=self.proposal.date_created + timedelta(seconds=self.PRUNE_TIME),
|
||||
)
|
||||
db.session.add(task)
|
||||
db.session.commit()
|
||||
|
||||
@staticmethod
|
||||
def process_task(task):
|
||||
from grant.proposal.models import Proposal
|
||||
proposal = Proposal.query.filter_by(id=task.blob["proposal_id"]).first()
|
||||
|
||||
# If it was deleted or moved out of a draft, noop out
|
||||
if not proposal or proposal.status != ProposalStatus.DRAFT:
|
||||
return
|
||||
|
||||
# If any of the proposal fields are filled, noop out
|
||||
if proposal.title or proposal.brief or proposal.content or proposal.category or proposal.target != "0":
|
||||
return
|
||||
|
||||
if proposal.payout_address or proposal.milestones:
|
||||
return
|
||||
|
||||
# Otherwise, delete the empty proposal
|
||||
db.session.delete(proposal)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
JOBS = {
|
||||
1: ProposalReminder.process_task,
|
||||
2: ProposalDeadline.process_task,
|
||||
3: ContributionExpired.process_task,
|
||||
4: PruneDraft.process_task
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from grant.task.models import Task
|
||||
from grant.task.models import Task, db
|
||||
from grant.task.jobs import PruneDraft
|
||||
from grant.milestone.models import Milestone
|
||||
from grant.proposal.models import Proposal
|
||||
from grant.utils.enums import ProposalStatus, Category
|
||||
|
||||
from mock import patch, Mock
|
||||
|
||||
from ..config import BaseProposalCreatorConfig
|
||||
|
||||
|
@ -22,3 +28,108 @@ class TestTaskAPI(BaseProposalCreatorConfig):
|
|||
tasks = Task.query.filter(Task.execute_after <= datetime.now()).filter_by(completed=False).all()
|
||||
self.assertEqual(tasks, [])
|
||||
|
||||
@patch('grant.task.views.datetime')
|
||||
def test_proposal_pruning(self, mock_datetime):
|
||||
self.login_default_user()
|
||||
resp = self.app.post(
|
||||
"/api/v1/proposals/drafts",
|
||||
)
|
||||
proposal_id = resp.json['proposalId']
|
||||
|
||||
# make sure proposal was created
|
||||
proposal = Proposal.query.get(proposal_id)
|
||||
self.assertIsNotNone(proposal)
|
||||
|
||||
# make sure the task was created
|
||||
self.assertStatus(resp, 201)
|
||||
tasks = Task.query.all()
|
||||
self.assertEqual(len(tasks), 1)
|
||||
task = tasks[0]
|
||||
self.assertEqual(resp.json['proposalId'], task.blob['proposal_id'])
|
||||
self.assertFalse(task.completed)
|
||||
|
||||
# mock time so task will run when called
|
||||
after_time = datetime.now() + timedelta(seconds=PruneDraft.PRUNE_TIME + 100)
|
||||
mock_datetime.now = Mock(return_value=after_time)
|
||||
|
||||
# run task
|
||||
resp = self.app.get("/api/v1/task")
|
||||
self.assert200(resp)
|
||||
|
||||
# make sure task ran successfully
|
||||
tasks = Task.query.all()
|
||||
self.assertEqual(len(tasks), 1)
|
||||
task = tasks[0]
|
||||
self.assertTrue(task.completed)
|
||||
proposal = Proposal.query.get(proposal_id)
|
||||
self.assertIsNone(proposal)
|
||||
|
||||
def test_proposal_pruning_noops(self):
|
||||
# ensure all proposal noop states work as expected
|
||||
|
||||
def status(p):
|
||||
p.status = ProposalStatus.LIVE
|
||||
|
||||
def title(p):
|
||||
p.title = 'title'
|
||||
|
||||
def brief(p):
|
||||
p.brief = 'brief'
|
||||
|
||||
def content(p):
|
||||
p.content = 'content'
|
||||
|
||||
def category(p):
|
||||
p.category = Category.DEV_TOOL
|
||||
|
||||
def target(p):
|
||||
p.target = '100'
|
||||
|
||||
def payout_address(p):
|
||||
p.payout_address = 'address'
|
||||
|
||||
def milestones(p):
|
||||
milestones_data = [
|
||||
{
|
||||
"title": "All the money straightaway",
|
||||
"content": "cool stuff with it",
|
||||
"date_estimated": 1549505307,
|
||||
"payout_percent": "100",
|
||||
"immediate_payout": False
|
||||
}
|
||||
]
|
||||
Milestone.make(milestones_data, p)
|
||||
|
||||
modifiers = [
|
||||
status,
|
||||
title,
|
||||
brief,
|
||||
content,
|
||||
category,
|
||||
target,
|
||||
payout_address,
|
||||
milestones
|
||||
]
|
||||
|
||||
for modifier in modifiers:
|
||||
proposal = Proposal.create(status=ProposalStatus.DRAFT)
|
||||
proposal_id = proposal.id
|
||||
modifier(proposal)
|
||||
|
||||
db.session.add(proposal)
|
||||
db.session.commit()
|
||||
|
||||
blob = {
|
||||
"proposal_id": proposal_id,
|
||||
}
|
||||
|
||||
task = Task(
|
||||
job_type=PruneDraft.JOB_TYPE,
|
||||
blob=blob,
|
||||
execute_after=datetime.now()
|
||||
)
|
||||
|
||||
PruneDraft.process_task(task)
|
||||
|
||||
proposal = Proposal.query.get(proposal_id)
|
||||
self.assertIsNotNone(proposal)
|
||||
|
|
Loading…
Reference in New Issue