Skip to content
This repository has been archived by the owner on Aug 20, 2018. It is now read-only.

Commit

Permalink
Bug 1033808 - Automatically make bugzilla-import task unavailable bas…
Browse files Browse the repository at this point in the history
…ed on criteria
  • Loading branch information
bobsilverberg committed Oct 2, 2014
1 parent 5cd2b68 commit 6213623
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 8 deletions.
6 changes: 4 additions & 2 deletions oneanddone/tasks/bugzilla_utils.py
Expand Up @@ -48,9 +48,11 @@ def request_bugcount(self, request_params):
bug_count = response.get('bug_count', '0')
return int(bug_count)

def request_bug(self, bug_id, fields=['id', 'summary']):
def request_bug(self, bug_id, fields=None):
""" Returns bug with id `bug_id` from Buzgilla@Mozilla, if any """
params = {'include_fields': ','.join(fields)}
params = {}
if fields:
params['include_fields'] = ','.join(fields)
url = ''.join([self.baseurl, '/', str(bug_id)])
bugs = self._request_json(url, params).get('bugs')
if bugs:
Expand Down
Expand Up @@ -3,13 +3,15 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from django.core.management.base import BaseCommand

from oneanddone.tasks.models import TaskAttempt
from oneanddone.tasks.models import Task, TaskAttempt


class Command(BaseCommand):
help = 'Cleans up status of task attempts based on task data'
help = 'Cleans up status of tasks and attempts based on task data'

def handle(self, *args, **options):
invalidated = Task.invalidate_tasks()
self.stdout.write('%s tasks were invalidated via bug data\n' % invalidated)
closed = TaskAttempt.close_stale_onetime_attempts()
self.stdout.write('%s stale one-time attempts were closed\n' % closed)
closed = TaskAttempt.close_expired_task_attempts()
Expand Down
45 changes: 45 additions & 0 deletions oneanddone/tasks/models.py
Expand Up @@ -19,6 +19,7 @@
from tower import ugettext as _

from oneanddone.base.models import CachedModel, CreatedByModel, CreatedModifiedModel
from oneanddone.tasks.bugzilla_utils import BugzillaUtils


class TaskInvalidationCriterion(CreatedModifiedModel, CreatedByModel):
Expand All @@ -44,6 +45,15 @@ def __unicode__(self):
self.choices[self.relation],
self.field_value])

def passes(self, bug):
sought_value = self.field_value.lower()
actual_value = bug[self.field_name.lower()].lower()
matches = sought_value == actual_value
if ((self.relation == self.EQUAL and matches) or
(self.relation == self.NOT_EQUAL and not matches)):
return True
return False

field_name.help_text = """
Name of field recognized by Bugzilla@Mozilla REST API. Examples:
status, resolution, component.
Expand Down Expand Up @@ -196,6 +206,22 @@ def _yield_html(self, field):
attributes=settings.INSTRUCTIONS_ALLOWED_ATTRIBUTES)
return jinja2.Markup(cleaned_html)

@property
def has_bugzilla_bug(self):
return isinstance(self.imported_item, BugzillaBug)

@property
def bugzilla_bug(self):
if self.has_bugzilla_bug:
return BugzillaUtils().request_bug(self.imported_item.bugzilla_id)
return None

@property
def invalidation_criteria(self):
if self.batch:
return self.batch.taskinvalidationcriterion_set.all()
return None

@property
def keywords_list(self):
return ', '.join([keyword.name for keyword in self.keyword_set.all()])
Expand Down Expand Up @@ -256,6 +282,25 @@ def get_edit_url(self):
def __unicode__(self):
return self.name

@classmethod
def invalidate_tasks(self):
"""
Invalidate any tasks for which invalidation criteria is met
"""
bugzillabug_type = ContentType.objects.get(model="BugzillaBug")
tasks = self.objects.filter(
is_invalid=False,
content_type=bugzillabug_type)
invalidated = 0
for task in tasks:
bug = task.bugzilla_bug
for criterion in task.invalidation_criteria:
if criterion.passes(bug):
task.is_invalid = True
task.save()
invalidated += 1
return invalidated

@classmethod
def is_available_filter(self, now=None, allow_expired=False, prefix=''):
"""
Expand Down
25 changes: 25 additions & 0 deletions oneanddone/tasks/tests/__init__.py
Expand Up @@ -25,6 +25,31 @@ class TaskTypeFactory(DjangoModelFactory):
creator = SubFactory(UserFactory)


class BugzillaBugFactory(DjangoModelFactory):
FACTORY_FOR = models.BugzillaBug

summary = Sequence(lambda n: 'test{0}'.format(n))
bugzilla_id = Sequence(lambda n: n)


class TaskImportBatchFactory(DjangoModelFactory):
FACTORY_FOR = models.TaskImportBatch

description = Sequence(lambda n: 'test{0}'.format(n))
query = Sequence(lambda n: 'test{0}'.format(n))
source = models.TaskImportBatch.BUGZILLA
creator = SubFactory(UserFactory)


class TaskInvalidationCriterionFactory(DjangoModelFactory):
FACTORY_FOR = models.TaskInvalidationCriterion

field_name = Sequence(lambda n: 'test{0}'.format(n))
relation = models.TaskInvalidationCriterion.EQUAL
field_value = Sequence(lambda n: 'test{0}'.format(n))
creator = SubFactory(UserFactory)


class TaskFactory(DjangoModelFactory):
FACTORY_FOR = models.Task

Expand Down
146 changes: 143 additions & 3 deletions oneanddone/tasks/tests/test_models.py
Expand Up @@ -9,9 +9,12 @@
from nose.tools import eq_, ok_

from oneanddone.base.tests import TestCase
from oneanddone.tasks.models import Task, TaskKeyword, TaskAttempt
from oneanddone.tasks.tests import TaskFactory, TaskKeywordFactory, TaskAttemptFactory
from oneanddone.tasks.tests import FeedbackFactory
from oneanddone.tasks.models import (Task, TaskInvalidationCriterion, TaskKeyword,
TaskAttempt)
from oneanddone.tasks.tests import (BugzillaBugFactory, FeedbackFactory,
TaskFactory, TaskImportBatchFactory,
TaskInvalidationCriterionFactory, TaskKeywordFactory,
TaskAttemptFactory)
from oneanddone.users.tests import UserFactory


Expand Down Expand Up @@ -342,6 +345,63 @@ def test_close_expired_task_attempts(self):
eq_(TaskAttempt.objects.filter(task=self.task_no_draft,
state=TaskAttempt.STARTED).count(), 1)

def test_invalidate_tasks_equals_criterion(self):
"""
The invalidate_tasks routine should invalidate tasks which match the
invalidation criteria.
This tests an equals criterion.
"""
bug_to_become_invalid, bug_to_stay_valid = BugzillaBugFactory.create_batch(2)
batch = TaskImportBatchFactory.create()
criterion = TaskInvalidationCriterionFactory.create(
field_name='name',
relation=TaskInvalidationCriterion.EQUAL,
field_value='value')
criterion.batches.add(batch)
criterion.save()
task1, task2, task3 = TaskFactory.create_batch(3,
batch=batch,
imported_item=bug_to_become_invalid,
is_invalid=False)
task3.imported_item = bug_to_stay_valid
task3.save()
with patch('oneanddone.tasks.models.BugzillaUtils.request_bug') as request_bug:
request_bug.side_effect = lambda x: {
bug_to_become_invalid.bugzilla_id: {'name': 'value'},
bug_to_stay_valid.bugzilla_id: {'name': 'not value'}}[x]
eq_(Task.invalidate_tasks(), 2)
eq_(Task.objects.get(pk=task1.pk).is_invalid, True)
eq_(Task.objects.get(pk=task2.pk).is_invalid, True)
eq_(Task.objects.get(pk=task3.pk).is_invalid, False)

def test_invalidate_tasks_not_equals_criterion(self):
"""
The invalidate_tasks routine should invalidate tasks which match the
invalidation criteria.
This tests a not equals criterion.
"""
bug_to_become_invalid, bug_to_stay_valid = BugzillaBugFactory.create_batch(2)
batch = TaskImportBatchFactory.create()
criterion = TaskInvalidationCriterionFactory.create(
field_name='name',
relation=TaskInvalidationCriterion.NOT_EQUAL,
field_value='value')
criterion.batches.add(batch)
criterion.save()
task1, task2 = TaskFactory.create_batch(2,
batch=batch,
imported_item=bug_to_become_invalid,
is_invalid=False)
task2.imported_item = bug_to_stay_valid
task2.save()
with patch('oneanddone.tasks.models.BugzillaUtils.request_bug') as request_bug:
request_bug.side_effect = lambda x: {
bug_to_become_invalid.bugzilla_id: {'name': 'value'},
bug_to_stay_valid.bugzilla_id: {'name': 'not value'}}[x]
eq_(Task.invalidate_tasks(), 1)
eq_(Task.objects.get(pk=task1.pk).is_invalid, False)
eq_(Task.objects.get(pk=task2.pk).is_invalid, True)

def test_save_closes_task_attempts(self):
"""
When a saved task is unavailable,
Expand Down Expand Up @@ -399,6 +459,39 @@ def test_default_sort_order(self):
eq_(tasks[4], t5)
eq_(tasks[5], t6)

def test_has_bugzilla_bug_true(self):
bug = BugzillaBugFactory.create()
task = TaskFactory.create(imported_item=bug)
ok_(task.has_bugzilla_bug)

def test_has_bugzilla_bug_false(self):
task = TaskFactory.create()
ok_(not task.has_bugzilla_bug)

def test_bugzilla_bug_exists(self):
bug = BugzillaBugFactory.create()
task = TaskFactory.create(imported_item=bug)
with patch('oneanddone.tasks.models.BugzillaUtils.request_bug') as request_bug:
request_bug.return_value = bug
eq_(bug, task.bugzilla_bug)
request_bug.assert_called_with(bug.bugzilla_id)

def test_bugzilla_bug_not_exists(self):
task = TaskFactory.create()
eq_(None, task.bugzilla_bug)

def test_invalidation_criteria_exists(self):
batch = TaskImportBatchFactory.create()
criterion = TaskInvalidationCriterionFactory.create()
criterion.batches.add(batch)
criterion.save()
task = TaskFactory.create(batch=batch)
eq_(criterion, task.invalidation_criteria[0])

def test_invalidation_criteria_does_not_exist(self):
task = TaskFactory.create()
eq_(None, task.invalidation_criteria)


class TaskAttemptTests(TestCase):
def setUp(self):
Expand Down Expand Up @@ -441,3 +534,50 @@ def test_attempt_length_in_minutes(self):
self.attempt.created = aware_datetime(2014, 1, 1)
self.attempt.modified = aware_datetime(2014, 1, 2)
eq_(self.attempt.attempt_length_in_minutes, 1440)


class TaskInvalidationCriterionTests(TestCase):

def test_equal_passes_true(self):
"""
Return true if the criterion passes for the bug, using EQUAL.
"""
criterion = TaskInvalidationCriterionFactory.create(
field_name='name',
relation=TaskInvalidationCriterion.EQUAL,
field_value='value')
bug = {'name': 'value'}
ok_(criterion.passes(bug))

def test_equal_passes_false(self):
"""
Return false if the criterion does not pass for the bug, using EQUAL.
"""
criterion = TaskInvalidationCriterionFactory.create(
field_name='name',
relation=TaskInvalidationCriterion.EQUAL,
field_value='value')
bug = {'name': 'not value'}
ok_(not criterion.passes(bug))

def test_not_equal_passes_true(self):
"""
Return true if the criterion passes for the bug, using NOT_EQUAL.
"""
criterion = TaskInvalidationCriterionFactory.create(
field_name='name',
relation=TaskInvalidationCriterion.NOT_EQUAL,
field_value='not value')
bug = {'name': 'value'}
ok_(criterion.passes(bug))

def test_not_equal_passes_false(self):
"""
Return false if the criterion does not pass for the bug, using NOT_EQUAL.
"""
criterion = TaskInvalidationCriterionFactory.create(
field_name='name',
relation=TaskInvalidationCriterion.NOT_EQUAL,
field_value='value')
bug = {'name': 'value'}
ok_(not criterion.passes(bug))
2 changes: 1 addition & 1 deletion stackato.yml
Expand Up @@ -60,4 +60,4 @@ ignores:
- "mine/*"
- "stackato-*"
cron:
- "00 * * * * python manage.py taskattemptcleanup >>$HOME/cron-stdout.log 2>>$HOME/cron-stderr.log"
- "00 */6 * * * python manage.py taskcleanup >>$HOME/cron-stdout.log 2>>$HOME/cron-stderr.log"

0 comments on commit 6213623

Please sign in to comment.