Skip to content
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

Ticket/bb 25015 change team triggers #6024

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
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
4 changes: 4 additions & 0 deletions bluebottle/test/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ def included_by_type(self, response, type):
included = response.json()['included']
return [include for include in included if include['type'] == type]

def assertStatus(self, obj, status):
obj.refresh_from_db()
return self.assertEqual(obj.status, status)

@classmethod
def setUpClass(cls):
super(BluebottleTestCase, cls).setUpClass()
Expand Down
2 changes: 2 additions & 0 deletions bluebottle/time_based/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,8 @@ class TeamAdmin(PolymorphicInlineSupportMixin, StateMachineAdmin):
raw_id_fields = ('user', 'registration', 'activity')
inlines = [TeamMemberAdminInline]

list_filter = [StateMachineFilter]

def get_inlines(self, request, obj):
inlines = super().get_inlines(request, obj)
if obj and obj.id and obj.activity and isinstance(obj.activity, ScheduleActivity):
Expand Down
14 changes: 14 additions & 0 deletions bluebottle/time_based/effects/slots.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.utils.timezone import now
from django.utils.translation import gettext as _

from bluebottle.fsm.effects import Effect
Expand All @@ -14,3 +15,16 @@ def post_save(self, **kwargs):
user=team_member.user,
activity=slot.activity,
)


class SetContributionsStartEffect(Effect):
title = _('Set contributions start date')
template = 'admin/time_based/set_contributions_start.html'

def is_valid(self):
return not self.instance.start

def post_save(self, **kwargs):
if not self.instance.start:
for participant in self.instance.participants.all():
participant.contributions.update(start=now())
6 changes: 6 additions & 0 deletions bluebottle/time_based/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,12 @@ class ScheduleActivity(RegistrationActivity):
def accepted_participants(self):
return self.registrations.filter(status__in=["accepted", "succeeded", "scheduled"])

@property
def unscheduled_slots(self):
if self.team_activity == 'teams':
return self.team_slots.filter(status='new')
return self.slots.filter(status='new')

class Meta:
verbose_name = _("Schedule activity")
verbose_name_plural = _("Schedule activities")
Expand Down
9 changes: 9 additions & 0 deletions bluebottle/time_based/states/teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ def is_owner(self, user):
automatic=True,
)

unschedule = Transition(
[scheduled, succeeded],
accepted,
name=_("Schedule"),
passed_label=_("scheduled"),
description=_("Assign a slot to this activity"),
automatic=True,
)

remove = Transition(
[accepted, scheduled],
removed,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% load i18n %}
<h3>{%trans "Update time contributions" %}</h3>

<p>
{% blocktrans with slot=effects.0.instance activity=effects.0.instance.activity %}
Update the start date of related contributions.
{% endblocktrans %}

{% if effects|length > 1%}
{% blocktrans with count=effects|length|add:"-1" %}
and {{count}} others
{% endblocktrans %}
{% endif %}
79 changes: 78 additions & 1 deletion bluebottle/time_based/tests/test_activity_triggers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import date, timedelta

from django.core import mail
from django.utils.timezone import now

from bluebottle.activities.models import Organizer
from bluebottle.initiatives.tests.factories import (
Expand All @@ -12,7 +13,7 @@
from bluebottle.time_based.tests.factories import (
DeadlineActivityFactory,
DeadlineRegistrationFactory,
PeriodicActivityFactory,
PeriodicActivityFactory, ScheduleActivityFactory, TeamScheduleRegistrationFactory,
)


Expand Down Expand Up @@ -183,3 +184,79 @@ def test_initial(self):
def test_publish(self):
self.publish()
self.assertEqual(len(self.activity.slots.all()), 1)


class ScheduleActivityTriggerTestCase(ActivityTriggerTestCase, BluebottleTestCase):
factory = ScheduleActivityFactory

def setUp(self):
super().setUp()
self.activity.team_activity = 'teams'
self.activity.save()

def register_team(self):
self.registration = TeamScheduleRegistrationFactory.create(activity=self.activity, user=self.user)
self.team = self.registration.team
self.team_member = self.team.team_members.first()
self.slot = self.team.slots.first()
self.participant = self.slot.participants.first()
self.contribution = self.participant.contributions.first()
self.registration.states.accept(save=True)

def test_succeed_manually(self):
self.publish()
self.register_team()
self.assertEqual(len(self.activity.team_slots.all()), 1)
self.assertStatus(self.activity, "open")
self.assertStatus(self.registration, "accepted")
self.assertStatus(self.team, "accepted")
self.assertStatus(self.team_member, "active")
self.assertStatus(self.slot, "new")
self.assertStatus(self.participant, "accepted")
self.assertStatus(self.contribution, "new")

self.activity.states.succeed_manually(save=True)

self.assertStatus(self.activity, "succeeded")
self.assertStatus(self.team, "succeeded")
self.assertStatus(self.team_member, "active")
self.assertStatus(self.slot, "finished")
self.assertStatus(self.participant, "succeeded")
self.assertStatus(self.contribution, "succeeded")

def test_change_end_date(self):
self.publish()
self.register_team()

self.activity.deadline = date.today() - timedelta(days=10)
self.activity.save()
self.assertStatus(self.activity, "succeeded")
self.assertStatus(self.team, "succeeded")
self.assertStatus(self.team_member, "active")
self.assertStatus(self.slot, "finished")
self.assertStatus(self.participant, "succeeded")
self.assertStatus(self.contribution, "succeeded")

def test_schedule_team(self):
self.publish()
self.register_team()

self.slot.start = now() + timedelta(days=1)
self.slot.save()
self.assertStatus(self.team, "scheduled")
self.assertStatus(self.team_member, "active")
self.assertStatus(self.slot, "scheduled")
self.assertStatus(self.participant, "scheduled")
self.assertStatus(self.contribution, "new")

def test_schedule_team_past(self):
self.publish()
self.register_team()

self.slot.start = now() - timedelta(days=1)
self.slot.save()
self.assertStatus(self.team, "succeeded")
self.assertStatus(self.team_member, "active")
self.assertStatus(self.slot, "finished")
self.assertStatus(self.participant, "succeeded")
self.assertStatus(self.contribution, "succeeded")
15 changes: 15 additions & 0 deletions bluebottle/time_based/triggers/activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,21 @@ class ScheduleActivityTriggers(RegistrationActivityTriggers):
RelatedTransitionEffect("teams", TeamStateMachine.restore),
],
),

TransitionTrigger(
RegistrationActivityStateMachine.succeed,
effects=[
RelatedTransitionEffect("unscheduled_slots", ScheduleSlotStateMachine.finish),
],
),

TransitionTrigger(
RegistrationActivityStateMachine.succeed_manually,
effects=[
RelatedTransitionEffect("unscheduled_slots", ScheduleSlotStateMachine.finish),
],
),

]


Expand Down
9 changes: 9 additions & 0 deletions bluebottle/time_based/triggers/participants.py
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,15 @@ def team_is_accepted(effect):
),
],
),
TransitionTrigger(
TeamScheduleParticipantStateMachine.unschedule,
effects=[
RelatedTransitionEffect(
"contributions",
ContributionStateMachine.reset,
),
],
),
TransitionTrigger(
TeamScheduleParticipantStateMachine.withdraw,
effects=[
Expand Down
11 changes: 10 additions & 1 deletion bluebottle/time_based/triggers/slots.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
RescheduleScheduleSlotContributions,
)
from bluebottle.time_based.effects.slots import (
CreateTeamSlotParticipantsEffect
CreateTeamSlotParticipantsEffect, SetContributionsStartEffect
)
from bluebottle.time_based.models import PeriodicSlot, ScheduleSlot, TeamScheduleSlot
from bluebottle.time_based.notifications.teams import UserTeamDetailsChangedNotification
Expand Down Expand Up @@ -103,6 +103,7 @@ class ScheduleSlotTriggers(TriggerManager):
TransitionTrigger(
ScheduleSlotStateMachine.finish,
effects=[
SetContributionsStartEffect,
RelatedTransitionEffect(
"participants",
ScheduleParticipantStateMachine.succeed,
Expand Down Expand Up @@ -139,6 +140,10 @@ class ScheduleSlotTriggers(TriggerManager):
"participants",
ScheduleParticipantStateMachine.schedule,
),
RelatedTransitionEffect(
"team",
TeamStateMachine.schedule,
),
],
),
TransitionTrigger(
Expand All @@ -148,6 +153,10 @@ class ScheduleSlotTriggers(TriggerManager):
"participants",
ScheduleParticipantStateMachine.unschedule,
),
RelatedTransitionEffect(
"team",
TeamStateMachine.unschedule,
),
],
),
]
Expand Down
35 changes: 35 additions & 0 deletions scripts/fix_team_status2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from bluebottle.clients.models import Client
from bluebottle.clients.utils import LocalTenant
from bluebottle.time_based.models import Team, TeamMember, TeamScheduleParticipant


def run(*args):
fix = 'fix' in args
for client in Client.objects.all():
with (LocalTenant(client)):
new_teams = Team.objects.filter(status='new', registration__status='accepted')
if new_teams.count():
print(f"{client.client_name}: {new_teams.count()} inconsistent teams.")
print(f"Team ID: {new_teams.first().id}")
if fix:
new_teams.update(status='accepted')
print(f"Fixed {new_teams.count()} teams.")

team_members = TeamMember.objects.filter(status__in=['accepted', 'new'])
if team_members.count():
print(f"{client.client_name}: {team_members.count()} inconsistent team members.")
if fix:
team_members.update(status='active')
print(f"Fixed {new_teams.count()} team members.")

participants = TeamScheduleParticipant.objects.filter(status='succeeded', slot__status='new')
if participants.count():
print(f"{client.client_name}: {participants.count()} inconsistent team slot participants.")
if fix:
participants.update(status='accepted')
for participant in participants:
participant.contributions.update(status='new')
print(f"Fixed {new_teams.count()} team team slot participants.")

if not fix:
print("☝️ Add '--script-args=fix' to the command to actually fix the statuses.")