Skip to content

Commit

Permalink
Add precondition counting
Browse files Browse the repository at this point in the history
  • Loading branch information
SupraSummus committed Dec 8, 2024
1 parent bb10953 commit 51d324b
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 2 deletions.
6 changes: 5 additions & 1 deletion django_goals/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ def has_delete_permission(self, request, obj=None):
@admin.register(Goal)
class GoalAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = (
'id', 'state', 'handler', 'precondition_date', 'created_at',
'id', 'state', 'handler',
'precondition_date',
'waiting_for_count',
'created_at',
'progress_count',
)
list_filter = ('state', 'precondition_date')
Expand All @@ -74,6 +77,7 @@ class GoalAdmin(DjangoObjectActions, admin.ModelAdmin):
'handler',
'instructions_pre',
'precondition_date',
'waiting_for_count',
'deadline',
'created_at',
'related_objects',
Expand Down
18 changes: 18 additions & 0 deletions django_goals/migrations/0004_goal_waiting_for_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2024-12-08 00:41

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('django_goals', '0003_remove_goal_goals_waiting_for_worker_idx_and_more'),
]

operations = [
migrations.AddField(
model_name='goal',
name='waiting_for_count',
field=models.IntegerField(default=0, help_text='Number of precondition goals we are still waiting for.'),
),
]
31 changes: 30 additions & 1 deletion django_goals/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class Goal(models.Model):
through='GoalDependency',
blank=True,
)
waiting_for_count = models.IntegerField(
default=0,
help_text=_('Number of precondition goals we are still waiting for.'),
)
deadline = models.DateTimeField(
default=timezone.now,
help_text=_('Goals having deadline sooner will be pursued first.'),
Expand Down Expand Up @@ -381,6 +385,14 @@ def handle_waiting_for_worker():

time_taken = time.monotonic() - start_time

# decrease waiting-for counter in dependent goals
if goal.state == GoalState.ACHIEVED:
Goal.objects.filter(
precondition_goals=goal,
).update(
waiting_for_count=models.F('waiting_for_count') - 1,
)

progress = goal.progress.create(
success=success,
created_at=now,
Expand Down Expand Up @@ -539,7 +551,24 @@ def schedule(


def add_precondition_goals(goal, precondition_goals):
goal.precondition_goals.add(*precondition_goals)
if not precondition_goals:
return
# get a list of new preconditions, and make sure nobody is working on them
new_precondition_goals = list(Goal.objects.filter(
id__in=[g.id for g in precondition_goals],
).exclude(
dependent_goals=goal,
).select_for_update(no_key=True))
if not new_precondition_goals:
return
# add to our preconditions
goal.precondition_goals.add(*new_precondition_goals)
# update waiting-for counter
for precondition_goal in new_precondition_goals:
if precondition_goal.state != GoalState.ACHIEVED:
goal.waiting_for_count += 1
goal.save(update_fields=['waiting_for_count'])
# move deadline earlier for preconditions, if needed
update_goals_deadline(goal.precondition_goals.all(), goal.deadline)


Expand Down
16 changes: 16 additions & 0 deletions django_goals/models_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ def test_handle_waiting_for_worker_guarded_updates_dependent_goals(goal):
next_goal = GoalFactory(
state=GoalState.WAITING_FOR_PRECONDITIONS,
precondition_goals=[goal],
waiting_for_count=1,
)
handle_waiting_for_worker_guarded()
next_goal.refresh_from_db()
assert next_goal.state == GoalState.WAITING_FOR_WORKER
assert next_goal.waiting_for_count == 0


@pytest.mark.django_db
Expand All @@ -71,3 +73,17 @@ def test_schedule_updates_deadline():
)
goal_a.refresh_from_db()
assert goal_a.deadline == now - datetime.timedelta(minutes=1)


@pytest.mark.django_db
@pytest.mark.parametrize(
('goal', 'expected_waiting_for'),
[
({'state': GoalState.WAITING_FOR_WORKER}, 1),
({'state': GoalState.ACHIEVED}, 0),
],
indirect=['goal'],
)
def test_schedule_updates_waiting_for_count(goal, expected_waiting_for):
next_goal = schedule(noop, precondition_goals=[goal])
assert next_goal.waiting_for_count == expected_waiting_for

0 comments on commit 51d324b

Please sign in to comment.