Skip to content

Commit

Permalink
Merge 7224e8a into 3be6da3
Browse files Browse the repository at this point in the history
  • Loading branch information
gannetson committed Aug 9, 2022
2 parents 3be6da3 + 7224e8a commit 05693da
Show file tree
Hide file tree
Showing 53 changed files with 2,000 additions and 1,284 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ notifications:
secure: TOveMBh9HePYKWuGTrWF+hTXzxGZvbVsa3KU0sB1yv6qkcixb5/ggvmkTeRddYEd/zyWyMenicFsrXVBgsP0SmbNgke6kq5+EN0U5oJWse998lvCVCpwmJQMdwDHvYsOtbFEOppQrbRK4vmH8qibx3x2YVg+u+61ePHvWYF9z6U=
after_success:
- bash post_travis.sh
- python -m coverage combine; python -m coverage report --omit **/migrations/**; coveralls
- python -m coverage combine; python -m coverage report --omit "**/migrations/**"; coveralls
36 changes: 31 additions & 5 deletions bluebottle/activities/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pytz import timezone

from django import forms
from django.conf.urls import url
from django.contrib import admin
Expand Down Expand Up @@ -230,20 +232,41 @@ def __init__(self, *args, **kwargs):
class TeamInline(admin.TabularInline):
model = Team
raw_id_fields = ('owner',)
readonly_fields = ('team_link', 'created', 'status')
readonly_fields = ('team_link', 'slot_link', 'created', 'status')
fields = readonly_fields + ('owner',)

extra = 0
ordering = ['slot__start']

def team_link(self, obj):
return format_html(
'<a href="{}">{}</a>',
reverse('admin:activities_team_change', args=(obj.id,)),
obj
)

team_link.short_description = _('Edit')

def slot_link(self, obj):
if getattr(obj, 'slot', None):
if obj.slot.location:
return format_html(
'<a href="{}#/tab/inline_1/">{}</a>',
reverse('admin:activities_team_change', args=(obj.id,)),
obj.slot.start.astimezone(timezone(obj.slot.location.timezone)).strftime('%c')
)
else:
return format_html(
'<a href="{}#/tab/inline_1/">{}</a>',
reverse('admin:activities_team_change', args=(obj.id,)),
obj.slot.start.strftime('%c')
)
return format_html(
'<a href="{}#/tab/inline_1/">{}</a>',
reverse('admin:activities_team_change', args=(obj.id,)),
_('Add time slot')
)
slot_link.short_description = _('Time slot')


class ActivityChildAdmin(PolymorphicChildModelAdmin, StateMachineAdmin):
base_model = Activity
Expand Down Expand Up @@ -589,6 +612,8 @@ def link(self, obj):
ordering = ('-created',)

def type(self, obj):
if obj.team_activity == 'teams':
return _('Team activity')
return obj.get_real_instance_class()._meta.verbose_name


Expand Down Expand Up @@ -675,16 +700,17 @@ class PaginationFormSet(PaginationFormSetBase, formset_class):
class TeamAdmin(StateMachineAdmin):
raw_id_fields = ['owner', 'activity']
readonly_fields = ['created', 'activity_link', 'invite_link']
fields = ['activity', 'invite_link', 'created', 'owner', 'states']
fields = ['activity', 'invite_link', 'created', 'owner', 'status', 'states']
superadmin_fields = ['force_status']
list_display = ['__str__', 'activity_link', 'status']

def get_inline_instances(self, request, obj=None):
self.inlines = []
if isinstance(obj.activity, PeriodActivity):
from bluebottle.time_based.admin import PeriodParticipantAdminInline
from bluebottle.time_based.admin import PeriodParticipantAdminInline, TeamSlotInline
self.inlines = [
PeriodParticipantAdminInline
PeriodParticipantAdminInline,
TeamSlotInline
]
return super(TeamAdmin, self).get_inline_instances(request, obj)

Expand Down
8 changes: 4 additions & 4 deletions bluebottle/activities/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def post_save(self, **kwargs):
if not self.instance.team:
self.instance.team = Team.objects.create(
owner=self.instance.user,
activity=self.instance.activity
activity=self.instance.activity,
)
self.instance.save()

Expand Down Expand Up @@ -128,19 +128,19 @@ def is_valid(self):
)

def pre_save(self, effects):
self.transitioned_conributions = []
self.transitioned_contributions = []
for contribution in self.contributions:
effect = TransitionEffect(self.transition)(contribution)

if effect.is_valid:
self.transitioned_conributions.append(contribution)
self.transitioned_contributions.append(contribution)
effect.pre_save(effects=effects)
effects.append(effect)

contribution.execute_triggers(effects=effects)

def post_save(self):
for contribution in self.transitioned_conributions:
for contribution in self.transitioned_contributions:
try:
contribution.contributor.refresh_from_db()
contribution.save()
Expand Down
4 changes: 2 additions & 2 deletions bluebottle/activities/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def action_link(self):
action_title = pgettext('email', 'View activity')

def get_recipients(self):
"""acitvity mananager"""
"""activity mananager"""
return [self.obj.activity.owner]


Expand Down Expand Up @@ -391,7 +391,7 @@ def get_recipients(self):


class TeamMemberWithdrewMessage(ActivityNotification):
subject = pgettext('email', "Withdrawal for '{title}'")
subject = pgettext('email', 'A participant has withdrawn from your team for "{title}"')
template = 'messages/team_member_withdrew'

context = {
Expand Down
18 changes: 18 additions & 0 deletions bluebottle/activities/migrations/0058_auto_20220622_1050.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.24 on 2022-06-22 08:50

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('activities', '0057_auto_20220608_1513'),
]

operations = [
migrations.AlterField(
model_name='activity',
name='team_activity',
field=models.CharField(blank=True, choices=[('teams', 'Teams'), ('individuals', 'Individuals')], default='individuals', help_text='Is this activity open for individuals or can only teams sign up?', max_length=100, verbose_name='participation'),
),
]
19 changes: 19 additions & 0 deletions bluebottle/activities/migrations/0059_auto_20220804_1214.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 2.2.24 on 2022-08-04 10:14

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('activities', '0058_auto_20220622_1050'),
]

operations = [
migrations.AlterField(
model_name='contributor',
name='team',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='members', to='activities.Team', verbose_name='team'),
),
]
10 changes: 9 additions & 1 deletion bluebottle/activities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class Contributor(TriggerMixin, AnonymizationMixin, PolymorphicModel):

team = models.ForeignKey(
'activities.Team', verbose_name=_('team'),
null=True, blank=True, related_name='members', on_delete=models.SET_NULL
null=True, blank=True, related_name='members', on_delete=models.CASCADE
)
user = models.ForeignKey(
'members.Member', verbose_name=_('user'),
Expand Down Expand Up @@ -290,6 +290,14 @@ class Team(TriggerMixin, models.Model):
'members.Member', related_name='teams', null=True, on_delete=models.SET_NULL
)

@property
def accepted_participants(self):
return self.members.filter(status='accepted')

@property
def accepted_participants_count(self):
return len(self.accepted_participants)

class Meta(object):
ordering = ('-created',)
verbose_name = _("Team")
Expand Down
2 changes: 1 addition & 1 deletion bluebottle/activities/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Meta(object):
'matching_properties',
)

class JSONAPIMeta(object):
class JSONAPIMeta:
included_resources = [
'owner',
'initiative',
Expand Down
37 changes: 35 additions & 2 deletions bluebottle/activities/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,18 @@ class TeamStateMachine(ModelStateMachine):
_('The team is cancelled. Contributors can no longer register')
)

running = State(
_('running'),
'running',
_('The team is currently running the activity.')
)

finished = State(
_('finished'),
'finished',
_('The team has completed the activity.')
)

def is_team_captain(self, user):
return user == self.instance.owner

Expand Down Expand Up @@ -428,7 +440,10 @@ def is_activity_owner(self, user):
)

cancel = Transition(
open,
[
open,
new
],
cancelled,
automatic=False,
permission=is_activity_owner,
Expand All @@ -437,10 +452,28 @@ def is_activity_owner(self, user):
)

reopen = Transition(
cancelled,
[cancelled, running, finished],
open,
automatic=False,
permission=is_activity_owner,
name=_('accept'),
description=_('The team is reopened. Contributors can apply again')
)

start = Transition(
[open, finished],
running,
name=_("Start"),
description=_(
"The slot is currently taking place."
)
)
finish = Transition(
[open, running],
finished,
name=_("Finish"),
description=_(
"The slot has ended. "
"Triggered when slot has ended."
)
)
52 changes: 45 additions & 7 deletions bluebottle/activities/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from bluebottle.time_based.serializers import PeriodParticipantSerializer
from bluebottle.time_based.tests.factories import (
DateActivityFactory, PeriodActivityFactory, DateParticipantFactory, PeriodParticipantFactory,
DateActivitySlotFactory, SkillFactory
DateActivitySlotFactory, SkillFactory, TeamSlotFactory
)
from bluebottle.initiatives.tests.factories import InitiativeFactory
from bluebottle.initiatives.models import InitiativePlatformSettings
Expand Down Expand Up @@ -1726,7 +1726,7 @@ def test_participants_over_max_age(self):
)


class RelatedTeamListViewAPITestCase(APITestCase):
class TeamListViewAPITestCase(APITestCase):
serializer = TeamSerializer

def setUp(self):
Expand All @@ -1737,6 +1737,14 @@ def setUp(self):
self.approved_teams = TeamFactory.create_batch(5, activity=self.activity)
for team in self.approved_teams:
PeriodParticipantFactory.create(activity=self.activity, team=team, user=team.owner)

for team in self.approved_teams[:2]:
TeamSlotFactory.create(activity=self.activity, team=team, start=now() + timedelta(days=5))

TeamSlotFactory.create(
activity=self.activity, team=self.approved_teams[2], start=now() - timedelta(days=5)
)

self.cancelled_teams = TeamFactory.create_batch(
5, activity=self.activity, status='cancelled'
)
Expand All @@ -1760,7 +1768,6 @@ def test_get_activity_owner(self):
self.assertStatus(status.HTTP_200_OK)
self.assertTotal(len(self.approved_teams) + len(self.cancelled_teams))
self.assertObjectList(self.approved_teams)
self.assertRelationship('activity', [self.activity])
self.assertRelationship('owner')

self.assertMeta('status')
Expand All @@ -1774,6 +1781,41 @@ def test_get_activity_owner(self):
'We should have a unique list of team ids'
)

def test_get_filtered_status(self):
new_teams = TeamFactory.create_batch(2, activity=self.activity, status='new')
self.perform_get(user=self.activity.owner, query={'filter[status]': 'new'})

self.assertStatus(status.HTTP_200_OK)
for resource in self.response.json()['data']:
self.assertTrue(
resource['id'] in [str(team.pk) for team in new_teams]
)

def test_get_filtered_has_no_slot(self):
self.perform_get(user=self.activity.owner, query={'filter[has_slot]': 'false'})

self.assertStatus(status.HTTP_200_OK)
for resource in self.response.json()['data']:
self.assertIsNone(resource['relationships']['slot']['data'])

def test_get_filtered_future(self):
self.perform_get(user=self.activity.owner, query={'filter[start]': 'future'})

self.assertStatus(status.HTTP_200_OK)
for resource in self.response.json()['data']:
self.assertTrue(
resource['id'] in [str(team.pk) for team in self.approved_teams[:2]]
)

def test_get_filtered_passed(self):
self.perform_get(user=self.activity.owner, query={'filter[start]': 'passed'})

self.assertStatus(status.HTTP_200_OK)
for resource in self.response.json()['data']:
self.assertEqual(
resource['id'], str(self.approved_teams[2].pk)
)

def test_get_cancelled_team_captain(self):
team = self.cancelled_teams[0]
self.perform_get(user=team.owner)
Expand All @@ -1782,7 +1824,6 @@ def test_get_cancelled_team_captain(self):

self.assertTotal(len(self.approved_teams) + 1)
self.assertObjectList(self.approved_teams + [team])
self.assertRelationship('activity', [self.activity])
self.assertRelationship('owner')

self.assertEqual(
Expand All @@ -1797,7 +1838,6 @@ def test_get_team_captain(self):
self.assertStatus(status.HTTP_200_OK)
self.assertTotal(len(self.approved_teams))
self.assertObjectList(self.approved_teams)
self.assertRelationship('activity', [self.activity])
self.assertRelationship('owner')

self.assertEqual(
Expand All @@ -1817,7 +1857,6 @@ def test_get_anonymous(self):
self.assertStatus(status.HTTP_200_OK)
self.assertTotal(len(self.approved_teams))
self.assertObjectList(self.approved_teams)
self.assertRelationship('activity', [self.activity])
self.assertRelationship('owner')
for resource in self.response.json()['data']:
self.assertTrue(resource['meta']['participants-export-url'] is None)
Expand All @@ -1838,7 +1877,6 @@ def test_other_user_anonymous(self):
self.assertStatus(status.HTTP_200_OK)
self.assertTotal(len(self.approved_teams))
self.assertObjectList(self.approved_teams)
self.assertRelationship('activity', [self.activity])
self.assertRelationship('owner')

def test_get_anonymous_closed_site(self):
Expand Down
2 changes: 1 addition & 1 deletion bluebottle/activities/tests/test_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def test_team_member_withdrew_notification(self):
self.message_class = TeamMemberWithdrewMessage
self.create()
self.assertRecipients([self.captain])
self.assertSubject("Withdrawal for 'Save the world!'")
self.assertSubject('A participant has withdrawn from your team for "Save the world!"')
self.assertHtmlBodyContains(
f"{self.obj.user.full_name} has withdrawn from your team for the activity ‘Save the world!’."
)
Expand Down
Loading

0 comments on commit 05693da

Please sign in to comment.