Skip to content

Add move method to automation rule #5998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,58 @@ def apply_action(self, version, match_result):
raise NotImplementedError
action(version, match_result, self.action_arg)

def move(self, steps):
"""
Move the rule n steps.

:param steps: Number of steps to be moved
:returns: True if the priority was changed
"""
total = self.project.automation_rules.count()
current_priority = self.priority
new_priority = (current_priority + steps) % total

if current_priority == new_priority:
return False

# Avoid integrity errors
self.priority = total + 99
self.save()

# Move other's priority
if new_priority > current_priority:
# It was moved down
rules = (
self.project.automation_rules
.filter(priority__gt=current_priority, priority__lte=new_priority)
# We sort the queryset in asc order
# to avoid hitting the unique constraint (project, priority).
.order_by('priority')
)
for rule in rules:
rule.priority -= 1
rule.save()
else:
# It was moved up
rules = (
self.project.automation_rules
.filter(priority__lt=current_priority, priority__gte=new_priority)
# We sort the queryset in desc order
# to avoid hitting the unique constraint (project, priority).
.order_by('-priority')
)
for rule in rules:
rule.priority += 1
rule.save()
self.priority = new_priority
self.save()
return True

def get_description(self):
if self.description:
return self.description
return f'{self.get_action_display()}'

def __str__(self):
class_name = self.__class__.__name__
return (
Expand Down
154 changes: 154 additions & 0 deletions readthedocs/rtd_tests/tests/test_automation_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,157 @@ def test_action_set_default_version(self):
assert self.project.get_default_version() == LATEST
assert rule.run(version) is True
assert self.project.get_default_version() == version.slug


@pytest.mark.django_db
class TestAutomationRuleManager:

@pytest.fixture(autouse=True)
def setup_method(self):
self.project = get(Project)

def test_append_rule_regex(self):
assert not self.project.automation_rules.all()

rule = RegexAutomationRule.objects.append_rule(
project=self.project,
description='First rule',
match_arg='.*',
version_type=TAG,
action=VersionAutomationRule.ACTIVATE_VERSION_ACTION,
)

# First rule gets added with priority 0
assert self.project.automation_rules.count() == 1
assert rule.priority == 0

# Adding a second rule
rule = RegexAutomationRule.objects.append_rule(
project=self.project,
description='Second rule',
match_arg='.*',
version_type=BRANCH,
action=VersionAutomationRule.ACTIVATE_VERSION_ACTION,
)
assert self.project.automation_rules.count() == 2
assert rule.priority == 1

# Adding a rule with a not secuencial priority
rule = get(
RegexAutomationRule,
description='Third rule',
project=self.project,
priority=9,
match_arg='.*',
version_type=TAG,
action=VersionAutomationRule.ACTIVATE_VERSION_ACTION,
)
assert self.project.automation_rules.count() == 3
assert rule.priority == 9

# Adding a new rule
rule = RegexAutomationRule.objects.append_rule(
project=self.project,
description='Fourth rule',
match_arg='.*',
version_type=BRANCH,
action=VersionAutomationRule.ACTIVATE_VERSION_ACTION,
)
assert self.project.automation_rules.count() == 4
assert rule.priority == 10


@pytest.mark.django_db
class TestAutomationRuleMove:

@pytest.fixture(autouse=True)
def setup_method(self):
self.project = get(Project)
self.rule_0 = self._append_rule('Zero')
self.rule_1 = self._append_rule('One')
self.rule_2 = self._append_rule('Two')
self.rule_3 = self._append_rule('Three')
self.rule_4 = self._append_rule('Four')
self.rule_5 = self._append_rule('Five')
assert self.project.automation_rules.count() == 6

def _append_rule(self, description):
rule = RegexAutomationRule.objects.append_rule(
project=self.project,
description=description,
match_arg='.*',
version_type=BRANCH,
action=VersionAutomationRule.ACTIVATE_VERSION_ACTION,
)
return rule

def test_move_rule_one_step(self):
self.rule_0.move(1)
new_order = [
self.rule_1,
self.rule_0,
self.rule_2,
self.rule_3,
self.rule_4,
self.rule_5,
]

for priority, rule in enumerate(self.project.automation_rules.all()):
assert rule == new_order[priority]
assert rule.priority == priority

def test_move_rule_positive_steps(self):
self.rule_1.move(1)
self.rule_1.move(2)

new_order = [
self.rule_0,
self.rule_2,
self.rule_3,
self.rule_4,
self.rule_1,
self.rule_5,
]

for priority, rule in enumerate(self.project.automation_rules.all()):
assert rule == new_order[priority]
assert rule.priority == priority

def test_move_rule_positive_steps_overflow(self):
self.rule_2.move(3)
self.rule_2.move(2)

new_order = [
self.rule_0,
self.rule_2,
self.rule_1,
self.rule_3,
self.rule_4,
self.rule_5,
]

for priority, rule in enumerate(self.project.automation_rules.all()):
assert rule == new_order[priority]
assert rule.priority == priority

def test_move_rules_positive_steps(self):
self.rule_2.move(2)
self.rule_0.refresh_from_db()
self.rule_0.move(7)
self.rule_4.refresh_from_db()
self.rule_4.move(4)
self.rule_1.refresh_from_db()
self.rule_1.move(1)

new_order = [
self.rule_4,
self.rule_1,
self.rule_0,
self.rule_3,
self.rule_2,
self.rule_5,
]

for priority, rule in enumerate(self.project.automation_rules.all()):
assert rule == new_order[priority]
assert rule.priority == priority