From c02bbfb3f3ac4f100a76ec1e39808723989e1dfe Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Mon, 22 Aug 2022 14:44:55 +0200 Subject: [PATCH 01/14] Only count upcoming activities for segments --- bluebottle/segments/serializers.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/bluebottle/segments/serializers.py b/bluebottle/segments/serializers.py index eb4ddac112..bd551f0a6e 100644 --- a/bluebottle/segments/serializers.py +++ b/bluebottle/segments/serializers.py @@ -63,16 +63,11 @@ def get_initiatives_count(self, obj): return len(Initiative.objects.filter(status='approved', activities__segments=obj).distinct()) def get_activities_count(self, obj): - return len( - Activity.objects.filter( - segments=obj - ).exclude( - status__in=( - 'draft', 'needs_work', 'submitted', 'deleted', - 'closed', 'cancelled', 'rejected' - ) - ) - ) + total = Activity.objects.filter( + segments=obj, + status__in=['open', 'full'] + ).count() + return total def get_stats(self, obj): return get_stats_for_activities(obj.activities.all()) From b0de02546f9a17105601a8ffabdb7adec9b8b805 Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Tue, 6 Sep 2022 07:28:07 +0200 Subject: [PATCH 02/14] FIx test --- bluebottle/segments/tests/test_api.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bluebottle/segments/tests/test_api.py b/bluebottle/segments/tests/test_api.py index b28b0bc149..59fd6ca5c0 100644 --- a/bluebottle/segments/tests/test_api.py +++ b/bluebottle/segments/tests/test_api.py @@ -238,8 +238,8 @@ def test_get_stats(self): funding = FundingFactory.create( initiative=initiative, - deadline=now() - datetime.timedelta(weeks=1), - status='succeeded' + deadline=now() + datetime.timedelta(weeks=1), + status='open' ) funding.segments.set([self.model]) for donor in DonorFactory.create_batch(3, activity=funding, user=None, amount=Money(10, 'USD')): @@ -249,9 +249,9 @@ def test_get_stats(self): deed_activity = DeedFactory.create( initiative=initiative, - status='succeeded', + status='open', start=datetime.date.today() - datetime.timedelta(days=10), - end=datetime.date.today() - datetime.timedelta(days=5) + end=datetime.date.today() + datetime.timedelta(days=5) ) deed_activity.segments.set([self.model]) @@ -262,8 +262,8 @@ def test_get_stats(self): collect_activity = CollectActivityFactory.create( initiative=initiative, - status='succeeded', - start=datetime.date.today() - datetime.timedelta(weeks=2), + status='open', + start=datetime.date.today() + datetime.timedelta(weeks=2), ) collect_activity.segments.set([self.model]) collect_activity.realized = 100 @@ -272,9 +272,9 @@ def test_get_stats(self): unrelated_activity = PeriodActivityFactory.create( initiative=initiative, - status='succeeded', + status='open', start=datetime.date.today() - datetime.timedelta(weeks=2), - deadline=datetime.date.today() - datetime.timedelta(weeks=1), + deadline=datetime.date.today() + datetime.timedelta(weeks=1), registration_deadline=datetime.date.today() - datetime.timedelta(weeks=3) ) PeriodParticipantFactory.create_batch(3, activity=unrelated_activity) @@ -285,7 +285,7 @@ def test_get_stats(self): self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()['data']['meta']['activities-count'], 5) + self.assertEqual(response.json()['data']['meta']['activities-count'], 4) self.assertEqual(response.json()['data']['meta']['initiatives-count'], 1) stats = response.json()['data']['meta']['stats'] From e5047b51d7434ee3ac8223453c84655f8769c5b7 Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Fri, 16 Sep 2022 11:34:31 +0200 Subject: [PATCH 03/14] Add status filter on related contributor lists Just add `filter[status]=` to the url and the contributors will be filtered correctly. --- bluebottle/activities/views.py | 4 ++++ bluebottle/time_based/tests/test_api.py | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/bluebottle/activities/views.py b/bluebottle/activities/views.py index 26c04fac19..1babdb5ea7 100644 --- a/bluebottle/activities/views.py +++ b/bluebottle/activities/views.py @@ -330,6 +330,10 @@ def get_queryset(self): status__in=('accepted', 'succeeded',) ) + status = self.request.query_params.get('filter[status]') + if status: + queryset = queryset.filter(status=status) + return queryset.filter( activity_id=self.kwargs['activity_id'] ) diff --git a/bluebottle/time_based/tests/test_api.py b/bluebottle/time_based/tests/test_api.py index 7ba49b7bf6..103bb0286e 100644 --- a/bluebottle/time_based/tests/test_api.py +++ b/bluebottle/time_based/tests/test_api.py @@ -2573,6 +2573,31 @@ def test_get_removed_participant(self): included_documents = self.included_by_type(self.response, 'private-documents') self.assertEqual(len(included_documents), 1) + def test_get_filter_new(self): + participant = self.participants[1] + + participant.status = 'new' + participant.save() + self.response = self.client.get( + self.url + '?filter[status]=new', user=self.activity.owner + ) + self.assertEqual(self.response.status_code, status.HTTP_200_OK) + + self.assertTotal(1) + self.assertEqual(self.response.json()['data'][0]['id'], str(participant.pk)) + + def test_get_filter_new_other_user(self): + participant = self.participants[1] + + participant.status = 'new' + participant.save() + self.response = self.client.get( + self.url + '?filter[status]=new', user=BlueBottleUserFactory.create() + ) + self.assertEqual(self.response.status_code, status.HTTP_200_OK) + + self.assertTotal(0) + def test_get_closed_site(self): MemberPlatformSettings.objects.update(closed=True) group = Group.objects.get(name='Anonymous') From d66f0fb008bcf1c934464e1c445ee1811f3d21dc Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Mon, 19 Sep 2022 14:07:27 +0200 Subject: [PATCH 04/14] Add download link to private document fields in the admin. --- bluebottle/files/fields.py | 24 +++++++++++++++++-- .../files/templates/widgets/document.html | 17 ++++--------- bluebottle/files/widgets.py | 10 ++++++-- bluebottle/funding/models.py | 4 +++- bluebottle/time_based/admin.py | 1 + bluebottle/time_based/models.py | 4 ++-- bluebottle/time_based/tests/test_admin.py | 16 +++++++++++++ bluebottle/time_based/urls/api.py | 1 + 8 files changed, 57 insertions(+), 20 deletions(-) diff --git a/bluebottle/files/fields.py b/bluebottle/files/fields.py index 9755beb12c..724c3baea2 100644 --- a/bluebottle/files/fields.py +++ b/bluebottle/files/fields.py @@ -59,14 +59,32 @@ def formfield(self, **kwargs): return super(DocumentField, self).formfield(**defaults) +class PrivateDocumentModelChoiceField(ModelChoiceField): + def __init__(self, related_field=None, view_name=None, *args, **kwargs): + self.related_field = related_field + self.view_name = view_name + super().__init__(*args, **kwargs) + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + + attrs['related_field'] = self.related_field + attrs['view_name'] = self.view_name + + return attrs + + class PrivateDocumentField(ForeignKey): def __init__(self, to=None, on_delete=models.CASCADE, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, to_field=None, - db_constraint=True, **kwargs): + db_constraint=True, view_name=None, **kwargs): if not to: from bluebottle.files.models import PrivateDocument to = PrivateDocument + + self.view_name = view_name + super(PrivateDocumentField, self).__init__( to, on_delete, related_name, related_query_name, limit_choices_to, parent_link, to_field, @@ -78,7 +96,9 @@ def formfield(self, **kwargs): queryset = self.remote_field.model._default_manager.using(db) defaults = { 'widget': PrivateDocumentWidget, - 'form_class': ModelChoiceField, + 'related_field': self.related_query_name(), + 'view_name': self.view_name, + 'form_class': PrivateDocumentModelChoiceField, 'queryset': queryset, 'to_field_name': self.remote_field.field_name, } diff --git a/bluebottle/files/templates/widgets/document.html b/bluebottle/files/templates/widgets/document.html index b3a7de53d2..b57ef09d3e 100644 --- a/bluebottle/files/templates/widgets/document.html +++ b/bluebottle/files/templates/widgets/document.html @@ -1,13 +1,4 @@ - - -
+{% load i18n %} +{% if download_link %} + {% trans "Download" %} +{% endif %} diff --git a/bluebottle/files/widgets.py b/bluebottle/files/widgets.py index 7294d80432..bb9153feb2 100644 --- a/bluebottle/files/widgets.py +++ b/bluebottle/files/widgets.py @@ -1,4 +1,5 @@ from django.forms import Select +from bluebottle.utils.utils import reverse_signed class ImageWidget(Select): @@ -32,9 +33,14 @@ class PrivateDocumentWidget(Select): def get_context(self, name, value, attrs): context = super(PrivateDocumentWidget, self).get_context(name, value, attrs) + if value: from bluebottle.files.models import PrivateDocument - context['file'] = PrivateDocument.objects.get(pk=value).file + document = PrivateDocument.objects.get(pk=value) + context['download_link'] = reverse_signed( + self.attrs['view_name'], + args=(getattr(document, f"{self.attrs['related_field']}_set").first().pk, ) + ) else: - context['file'] = None + context['download_link'] = None return context diff --git a/bluebottle/funding/models.py b/bluebottle/funding/models.py index 5f96acf1d8..99d513dc88 100644 --- a/bluebottle/funding/models.py +++ b/bluebottle/funding/models.py @@ -640,7 +640,9 @@ def __str__(self): class PlainPayoutAccount(PayoutAccount): - document = PrivateDocumentField(blank=True, null=True, on_delete=models.deletion.SET_NULL) + document = PrivateDocumentField( + blank=True, null=True, on_delete=models.deletion.SET_NULL, view_name='kyc-document' + ) ip_address = models.GenericIPAddressField(_('IP address'), blank=True, null=True, default=None) diff --git a/bluebottle/time_based/admin.py b/bluebottle/time_based/admin.py index b0c6b87fdd..214afd5d69 100644 --- a/bluebottle/time_based/admin.py +++ b/bluebottle/time_based/admin.py @@ -805,6 +805,7 @@ class PeriodParticipantAdmin(ContributorChildAdmin): inlines = ContributorChildAdmin.inlines + [TimeContributionInlineAdmin] readonly_fields = ContributorChildAdmin.readonly_fields + ['total'] fields = ContributorChildAdmin.fields + ['total', 'motivation', 'current_period', 'document'] + raw_id_fields = ContributorChildAdmin.raw_id_fields + ('document', ) list_display = ['__str__', 'activity_link', 'status'] def total(self, obj): diff --git a/bluebottle/time_based/models.py b/bluebottle/time_based/models.py index 7e5922e5d9..62357a102f 100644 --- a/bluebottle/time_based/models.py +++ b/bluebottle/time_based/models.py @@ -583,7 +583,7 @@ class Meta: class DateParticipant(Participant): motivation = models.TextField(blank=True, null=True) - document = PrivateDocumentField(blank=True, null=True) + document = PrivateDocumentField(blank=True, null=True, view_name='date-participant-document') class Meta(): verbose_name = _("Participant on a date") @@ -606,7 +606,7 @@ class JSONAPIMeta: class PeriodParticipant(Participant, Contributor): motivation = models.TextField(blank=True, null=True) - document = PrivateDocumentField(blank=True, null=True) + document = PrivateDocumentField(blank=True, null=True, view_name='period-participant-document') current_period = models.DateField(null=True, blank=True) diff --git a/bluebottle/time_based/tests/test_admin.py b/bluebottle/time_based/tests/test_admin.py index d39ccbbbd5..04f3b33ef2 100644 --- a/bluebottle/time_based/tests/test_admin.py +++ b/bluebottle/time_based/tests/test_admin.py @@ -6,6 +6,7 @@ from django.utils.timezone import now from pytz import UTC +from bluebottle.files.tests.factories import PrivateDocumentFactory from bluebottle.initiatives.models import InitiativePlatformSettings from bluebottle.initiatives.tests.factories import InitiativeFactory from bluebottle.offices.tests.factories import LocationFactory @@ -219,6 +220,21 @@ def test_adjusting_contribution(self): self.assertEqual(page.status, '200 OK') self.assertTrue('This field is required.' in page.text) + def test_document(self): + self.participant.document = PrivateDocumentFactory.create() + self.participant.save() + + self.url = reverse('admin:time_based_dateparticipant_change', args=(self.participant.id,)) + page = self.app.get(self.url) + self.assertEqual(page.status, '200 OK') + + link = page.html.find("a", {'class': 'private-document-link'}) + self.assertTrue( + link.attrs['href'].startswith( + reverse('date-participant-document', args=(self.participant.pk, )) + ) + ) + class TestSkillAdmin(BluebottleAdminTestCase): diff --git a/bluebottle/time_based/urls/api.py b/bluebottle/time_based/urls/api.py index 7790f9008b..def5c8d3e6 100644 --- a/bluebottle/time_based/urls/api.py +++ b/bluebottle/time_based/urls/api.py @@ -92,6 +92,7 @@ url(r'^/participants/date/transitions$', DateParticipantTransitionList.as_view(), name='date-participant-transition-list'), + url(r'^/participants/date/(?P\d+)/document$', DateParticipantDocumentDetail.as_view(), name='date-participant-document'), From 368b612913cc847944bb3d7499a62703278039b6 Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Mon, 19 Sep 2022 17:12:12 +0200 Subject: [PATCH 05/14] Add unreview-contributors to serializer --- bluebottle/time_based/serializers.py | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/bluebottle/time_based/serializers.py b/bluebottle/time_based/serializers.py index 7f3f634d61..07983d06cd 100644 --- a/bluebottle/time_based/serializers.py +++ b/bluebottle/time_based/serializers.py @@ -43,6 +43,18 @@ def get_url(self, name, view_name, kwargs, request): return f"{self.reverse('team-list')}?filter[activity_id]={kwargs['pk']}" +class UnreviewedContributorsField(SerializerMethodHyperlinkedRelatedField): + def __init__(self, *args, **kwargs): + self.model = kwargs.pop('model') + super().__init__(*args, **kwargs) + + def get_url(self, *args, **kwargs): + url = super().get_url(*args, **kwargs) + + if url: + return f"{url}?filter[status]=new" + + class TimeBasedBaseSerializer(BaseActivitySerializer): review = serializers.BooleanField(required=False) is_online = serializers.BooleanField(required=False, allow_null=True) @@ -56,6 +68,7 @@ class Meta(BaseActivitySerializer.Meta): 'expertise', 'review', 'contributors', + 'unreviewed_contributors', 'my_contributor', 'teams', ) @@ -362,11 +375,19 @@ class DateActivitySerializer(DateActivitySlotInfoMixin, TimeBasedBaseSerializer) contributors = SerializerMethodHyperlinkedRelatedField( model=DateParticipant, + read_only=True, many=True, related_link_view_name='date-participants', related_link_url_kwarg='activity_id' ) + unreviewed_contributors = UnreviewedContributorsField( + read_only=True, + related_link_view_name='date-participants', + related_link_url_kwarg='activity_id', + model=DateParticipant + ) + def get_slot_count(self, instance): return len(instance.slots.all()) @@ -385,6 +406,22 @@ def get_contributors(self, instance): ) ] + def get_unreviewed_contributors(self, instance): + user = self.context['request'].user + unreviewed_participants = instance.contributors.instance_of( + DateParticipant + ).filter( + status=ParticipantStateMachine.new.value + ) + + if ( + user not in (instance.owner, instance.initiative.owner) and + user not in instance.activity_managers.all() + ): + unreviewed_participants = unreviewed_participants.filter(user=user) + + return unreviewed_participants + participants_export_url = PrivateFileSerializer( 'date-participant-export', url_args=('pk', ), @@ -467,6 +504,13 @@ class PeriodActivitySerializer(TimeBasedBaseSerializer): contributors = ParticipantsField() + unreviewed_contributors = UnreviewedContributorsField( + read_only=True, + related_link_view_name='period-participants', + related_link_url_kwarg='activity_id', + model=PeriodParticipant + ) + participants_export_url = PrivateFileSerializer( 'period-participant-export', url_args=('pk', ), @@ -475,6 +519,22 @@ class PeriodActivitySerializer(TimeBasedBaseSerializer): read_only=True ) + def get_unreviewed_contributors(self, instance): + user = self.context['request'].user + unreviewed_participants = instance.contributors.instance_of( + PeriodParticipant + ).filter( + status=ParticipantStateMachine.new.value + ) + + if ( + user not in (instance.owner, instance.initiative.owner) and + user not in instance.activity_managers.all() + ): + unreviewed_participants = unreviewed_participants.filter(user=user) + + return unreviewed_participants + def get_my_contributor(self, instance): user = self.context['request'].user if user.is_authenticated: From 4e5e1f8c96fa58bb61c94f049c8ed48a49843988 Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Tue, 20 Sep 2022 09:58:18 +0200 Subject: [PATCH 06/14] Add test --- bluebottle/time_based/tests/test_api.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bluebottle/time_based/tests/test_api.py b/bluebottle/time_based/tests/test_api.py index 103bb0286e..60748250d4 100644 --- a/bluebottle/time_based/tests/test_api.py +++ b/bluebottle/time_based/tests/test_api.py @@ -341,6 +341,18 @@ def test_get_owner(self): self.assertEqual(data['meta']['matching-properties']['theme'], None) self.assertEqual(data['meta']['matching-properties']['location'], None) + contributor_url = reverse(f'{self.type}-participants', args=(self.activity.pk, )) + self.assertTrue( + data['relationships']['unreviewed-contributors']['links']['related'].endswith( + f"{contributor_url}?filter[status]=new" + ) + ) + self.assertTrue( + data['relationships']['contributors']['links']['related'].endswith( + contributor_url + ) + ) + def test_matching_theme(self): self.activity.initiative.states.submit(save=True) self.activity.initiative.states.approve(save=True) From bb41e556995add0261ff47405fcda34ff269cf06 Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Wed, 21 Sep 2022 17:23:37 +0200 Subject: [PATCH 07/14] Fix user location missing position --- bluebottle/activities/serializers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bluebottle/activities/serializers.py b/bluebottle/activities/serializers.py index 054f789981..9e2dc2167c 100644 --- a/bluebottle/activities/serializers.py +++ b/bluebottle/activities/serializers.py @@ -177,7 +177,11 @@ def get_matching_properties(self, obj): self.context['themes'] = [theme.pk for theme in user.favourite_themes.all()] if 'location' not in self.context: - self.context['location'] = user.location or user.place + if user.location and user.location.position: + self.context['location'] = user.location + + if user.place and user.place.position: + self.context['location'] = user.place matching = {'location': False} matching['skill'] = obj.expertise[0].id in self.context['skills'] if obj.expertise else False From b94df6d7919b5027c8694d1a97d4fd76d32354fd Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Thu, 22 Sep 2022 11:04:45 +0200 Subject: [PATCH 08/14] Remove obsolete fields on activity block --- bluebottle/cms/content_plugins.py | 1 - .../cms/migrations/0072_auto_20220922_1104.py | 21 +++++++++++++++++++ bluebottle/cms/models.py | 6 ------ .../admin/cms/preview/activities.html | 9 +++----- 4 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 bluebottle/cms/migrations/0072_auto_20220922_1104.py diff --git a/bluebottle/cms/content_plugins.py b/bluebottle/cms/content_plugins.py index 29ba904c07..63e8f7d9d4 100644 --- a/bluebottle/cms/content_plugins.py +++ b/bluebottle/cms/content_plugins.py @@ -64,7 +64,6 @@ class HomepageStatisticsBlockPlugin(CMSContentPlugin): @plugin_pool.register class ActivitiesBlockPlugin(CMSContentPlugin): model = ActivitiesContent - raw_id_fields = ('activities', ) category = _('Activities') diff --git a/bluebottle/cms/migrations/0072_auto_20220922_1104.py b/bluebottle/cms/migrations/0072_auto_20220922_1104.py new file mode 100644 index 0000000000..ad9cc4fdc7 --- /dev/null +++ b/bluebottle/cms/migrations/0072_auto_20220922_1104.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.24 on 2022-09-22 09:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0071_merge_20220906_0730'), + ] + + operations = [ + migrations.RemoveField( + model_name='activitiescontent', + name='activities', + ), + migrations.RemoveField( + model_name='activitiescontent', + name='highlighted', + ), + ] diff --git a/bluebottle/cms/models.py b/bluebottle/cms/models.py index 154fb167e4..06681de3b8 100644 --- a/bluebottle/cms/models.py +++ b/bluebottle/cms/models.py @@ -8,7 +8,6 @@ from parler.models import TranslatableModel, TranslatedFields from solo.models import SingletonModel -from bluebottle.activities.models import Activity from bluebottle.categories.models import Category from bluebottle.geo.models import Location from bluebottle.utils.fields import ImageField @@ -253,11 +252,6 @@ class ActivitiesContent(TitledContent): action_link = models.CharField(max_length=100, default="/initiatives/activities/list", blank=True, null=True) - activities = models.ManyToManyField( - Activity, blank=True, db_table='cms_activitycontent_activities' - ) - highlighted = models.BooleanField(default=False) - preview_template = 'admin/cms/preview/activities.html' class Meta: diff --git a/bluebottle/cms/templates/admin/cms/preview/activities.html b/bluebottle/cms/templates/admin/cms/preview/activities.html index 0596950b61..16d806dd50 100644 --- a/bluebottle/cms/templates/admin/cms/preview/activities.html +++ b/bluebottle/cms/templates/admin/cms/preview/activities.html @@ -1,7 +1,4 @@ -
    -{% for activity in instance.activities.all %} -
  • {{ activity.title }}
  • -{% endfor %} -
+{% load i18n%} -{{ instance.action }} +{% trans "Toggle 'highlight' on activities to show them in this block." %} +{{ instance.action }} \ No newline at end of file From 5fb3a33c17c04f90792b2aad7cd278d95fa47feb Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Thu, 22 Sep 2022 12:18:26 +0200 Subject: [PATCH 09/14] Don't invlude activity in participant serializer --- bluebottle/time_based/serializers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bluebottle/time_based/serializers.py b/bluebottle/time_based/serializers.py index 07983d06cd..b4fc366868 100644 --- a/bluebottle/time_based/serializers.py +++ b/bluebottle/time_based/serializers.py @@ -849,7 +849,6 @@ class Meta(ParticipantSerializer.Meta): class JSONAPIMeta(ParticipantSerializer.JSONAPIMeta): included_resources = ParticipantSerializer.JSONAPIMeta.included_resources + [ 'slots', - 'activity', ] resource_name = 'contributors/time-based/date-participants' @@ -859,7 +858,6 @@ class JSONAPIMeta(ParticipantSerializer.JSONAPIMeta): 'user': 'bluebottle.initiatives.serializers.MemberSerializer', 'document': 'bluebottle.time_based.serializers.DateParticipantDocumentSerializer', 'slots': 'bluebottle.time_based.serializers.SlotParticipantSerializer', - 'activity': 'bluebottle.time_based.serializers.DateActivitySerializer', } ) @@ -906,7 +904,6 @@ class JSONAPIMeta(ParticipantSerializer.JSONAPIMeta): 'document': 'bluebottle.time_based.serializers.PeriodParticipantDocumentSerializer', 'contributions': 'bluebottle.time_based.serializers.TimeContributionSerializer', 'team.slot': 'bluebottle.time_based.serializers.TeamSlotSerializer', - 'activity': 'bluebottle.time_based.serializers.PeriodActivitySerializer', } ) From 3b77b672e3c22a7cad113c72c946835881a90fa1 Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Thu, 22 Sep 2022 13:01:24 +0200 Subject: [PATCH 10/14] More fixes --- bluebottle/time_based/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bluebottle/time_based/serializers.py b/bluebottle/time_based/serializers.py index b4fc366868..ca075768ba 100644 --- a/bluebottle/time_based/serializers.py +++ b/bluebottle/time_based/serializers.py @@ -842,7 +842,7 @@ class Meta(ParticipantSerializer.Meta): validators = [ UniqueTogetherValidator( queryset=DateParticipant.objects.all(), - fields=('activity', 'user') + fields=('activity', 'user',) ) ] @@ -858,6 +858,7 @@ class JSONAPIMeta(ParticipantSerializer.JSONAPIMeta): 'user': 'bluebottle.initiatives.serializers.MemberSerializer', 'document': 'bluebottle.time_based.serializers.DateParticipantDocumentSerializer', 'slots': 'bluebottle.time_based.serializers.SlotParticipantSerializer', + 'activity': 'bluebottle.time_based.serializers.DateActivitySerializer', } ) @@ -904,6 +905,7 @@ class JSONAPIMeta(ParticipantSerializer.JSONAPIMeta): 'document': 'bluebottle.time_based.serializers.PeriodParticipantDocumentSerializer', 'contributions': 'bluebottle.time_based.serializers.TimeContributionSerializer', 'team.slot': 'bluebottle.time_based.serializers.TeamSlotSerializer', + 'activity': 'bluebottle.time_based.serializers.PeriodActivitySerializer', } ) From 517c238b424dc2c5885bae754c3b6ace456729e7 Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Fri, 23 Sep 2022 11:43:57 +0200 Subject: [PATCH 11/14] Paginate the related initiatives with a high page number, so that we show all activities --- bluebottle/initiatives/serializers.py | 2 +- bluebottle/initiatives/tests/test_api.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bluebottle/initiatives/serializers.py b/bluebottle/initiatives/serializers.py index b392a14061..47923272f3 100644 --- a/bluebottle/initiatives/serializers.py +++ b/bluebottle/initiatives/serializers.py @@ -157,7 +157,7 @@ def __init__(self, many=True, read_only=True, *args, **kwargs): super().__init__(Activity, many=many, read_only=read_only, *args, **kwargs) def get_url(self, name, view_name, kwargs, request): - return f"{self.reverse('activity-preview-list')}?filter[initiative.id]={kwargs['pk']}" + return f"{self.reverse('activity-preview-list')}?filter[initiative.id]={kwargs['pk']}&page[size]=100" class InitiativeSerializer(NoCommitMixin, ModelSerializer): diff --git a/bluebottle/initiatives/tests/test_api.py b/bluebottle/initiatives/tests/test_api.py index 0617c33053..9459f078ae 100644 --- a/bluebottle/initiatives/tests/test_api.py +++ b/bluebottle/initiatives/tests/test_api.py @@ -411,6 +411,10 @@ def test_get_owner(self): self.assertTrue( '/data/attributes/title' in (error['source']['pointer'] for error in data['meta']['required']) ) + self.assertEqual( + data['relationships']['activities']['links']['related'], + f'/api/activities/search?filter[initiative.id]={self.initiative.id}&page[size]=100' + ) def test_get_image_used_twice(self): InitiativeFactory.create(image=self.initiative.image) From 2c26b24159ee12d605fe934fda790582746586a3 Mon Sep 17 00:00:00 2001 From: Ernst Odolphi Date: Fri, 23 Sep 2022 13:55:22 +0200 Subject: [PATCH 12/14] Fix error when user location is not set --- bluebottle/activities/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bluebottle/activities/serializers.py b/bluebottle/activities/serializers.py index 9e2dc2167c..6460c4350f 100644 --- a/bluebottle/activities/serializers.py +++ b/bluebottle/activities/serializers.py @@ -189,7 +189,7 @@ def get_matching_properties(self, obj): if obj.is_online: matching['location'] = True - elif self.context['location'] and obj.position: + elif 'location' in self.context and obj.position: positions = [obj.position] if 'lat' in obj.position else obj.position dist = min( From 01534fc159a35512e913602de97abd050939cc1d Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Mon, 26 Sep 2022 10:57:37 +0200 Subject: [PATCH 13/14] Don't make higghlight list editable. --- bluebottle/activities/admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bluebottle/activities/admin.py b/bluebottle/activities/admin.py index fd0d08c815..b3580a5945 100644 --- a/bluebottle/activities/admin.py +++ b/bluebottle/activities/admin.py @@ -583,8 +583,6 @@ def get_list_filter(self, request): return filters - list_editable = ('highlight',) - list_display = ['__str__', 'created', 'type', 'state_name', 'link', 'highlight'] From 46eeec3a55df41fa18f7b4938e7ab707e6285591 Mon Sep 17 00:00:00 2001 From: Loek van Gent Date: Mon, 26 Sep 2022 11:06:39 +0200 Subject: [PATCH 14/14] Fix tests --- bluebottle/cms/tests/test_api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bluebottle/cms/tests/test_api.py b/bluebottle/cms/tests/test_api.py index cd72b55e09..088ada2ffc 100644 --- a/bluebottle/cms/tests/test_api.py +++ b/bluebottle/cms/tests/test_api.py @@ -150,9 +150,8 @@ def test_results_quotes(self): self.assertEqual(quotes['quotes'][0]['quote'], self.quote.quote) def test_results_activities(self): - activity = DateActivityFactory.create(status='open') - block = ActivitiesContent.objects.create_for_placeholder(self.placeholder) - block.activities.add(activity) + DateActivityFactory.create(status='open', highlight=True) + ActivitiesContent.objects.create_for_placeholder(self.placeholder) response = self.client.get(self.url) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -290,7 +289,7 @@ def test_homepage(self): def test_activities_from_homepage(self): DateActivityFactory.create_batch(10, status='open', highlight=True) - ActivitiesContent.objects.create_for_placeholder(self.placeholder, highlighted=True) + ActivitiesContent.objects.create_for_placeholder(self.placeholder) response = self.client.get(self.url) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['blocks'][0]['type'], 'activities')