Skip to content

Commit

Permalink
Merge be57e61 into fb28404
Browse files Browse the repository at this point in the history
  • Loading branch information
gannetson committed Sep 6, 2022
2 parents fb28404 + be57e61 commit 2114eb3
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 41 deletions.
16 changes: 16 additions & 0 deletions bluebottle/initiatives/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ class InitiativeDocument(Document):
}
)

has_public_activities = fields.BooleanField()
has_closed_activities = fields.BooleanField()

activities = fields.NestedField(properties={
'id': fields.LongField(),
'title': fields.KeywordField(),
Expand Down Expand Up @@ -156,6 +159,7 @@ def prepare_segments(self, instance):
{
'id': segment.id,
'name': segment.name,
'closed': segment.closed,
'segment_type': segment.segment_type.slug
}
for segment in activity.segments.all()
Expand Down Expand Up @@ -192,3 +196,15 @@ def prepare_country(self, instance):
if instance.location and instance.location.country_id:
countries += [instance.location.country_id]
return countries

def prepare_has_public_activities(self, instance):
hidden_statuses = ['draft', 'needs_work', 'submitted', 'rejected', 'deleted']
return instance.activities.\
exclude(segments__closed=True).\
exclude(status__in=hidden_statuses).exists()

def prepare_has_closed_activities(self, instance):
hidden_statuses = ['draft', 'needs_work', 'submitted', 'rejected', 'deleted']
return instance.activities.\
filter(segments__closed=True).\
exclude(status__in=hidden_statuses).exists()
103 changes: 69 additions & 34 deletions bluebottle/initiatives/filters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import re
from elasticsearch_dsl import Q
from elasticsearch_dsl.query import Term, Nested
from elasticsearch_dsl.query import Term, Nested, Terms

from bluebottle.utils.filters import ElasticSearchFilter
from bluebottle.initiatives.documents import InitiativeDocument
Expand All @@ -10,24 +10,26 @@ class InitiativeSearchFilter(ElasticSearchFilter):
document = InitiativeDocument

sort_fields = {
'date': ('-created', ),
'activity_date': ({
'activities.status_score': {
'order': 'desc',
'mode': 'max',
'nested': {
'path': 'activities'
'date': ('-created',),
'activity_date': (
{
'activities.status_score': {
'order': 'desc',
'mode': 'max',
'nested': {
'path': 'activities'
}
},
'activities.activity_date': {
'order': 'desc',
'mode': 'max',
'nested': {
'path': 'activities'
}
}
},
'activities.activity_date': {
'order': 'desc',
'mode': 'max',
'nested': {
'path': 'activities'
}
}
}, ),
'alphabetical': ('title_keyword', ),
),
'alphabetical': ('title_keyword',),
}
default_sort_field = 'date'

Expand Down Expand Up @@ -56,24 +58,57 @@ def get_default_filters(self, request):

permission = 'initiatives.api_read_initiative'

if not request.user.has_perm(permission):
filters = [Term(owner_id=request.user.id)]

if 'owner.id' not in fields:
filters.append(Term(status='approved'))

return filters
elif 'owner.id' in fields and request.user.is_authenticated:
value = request.user.pk
return [
Nested(path='owner', query=Term(owner__id=value)) |
Nested(path='promoter', query=Term(promoter__id=value)) |
Nested(path='activity_managers', query=Term(activity_managers__id=value)) |
Nested(path='activity_owners', query=Term(activity_owners__id=value)) |
Term(status='approved')
]
user_id = None
if request.user.is_authenticated:
user_id = request.user.pk

public_filter = Term(status='approved') & (
Nested(path='owner', query=Term(owner__id=user_id)) |
Nested(path='promoter', query=Term(promoter__id=user_id)) |
Nested(path='activity_managers', query=Term(activity_managers__id=user_id)) |
Nested(path='activity_owners', query=Term(activity_owners__id=user_id)) |
(
Term(has_public_activities=True) |
Term(has_closed_activities=False) |
Nested(
path='segments',
query=(
Terms(
segments__id=[
segment.id for segment in request.user.segments.filter(closed=True)
] if request.user.is_authenticated else []
)
)
)
)
)

owned_filter = ~Term(status='deleted') & (
Nested(path='owner', query=Term(owner__id=user_id)) |
Nested(path='promoter', query=Term(promoter__id=user_id)) |
Nested(path='activity_managers', query=Term(activity_managers__id=user_id)) |
Nested(path='activity_owners', query=Term(activity_owners__id=user_id))
)

if not request.user.has_perm(permission) and user_id:
# Not allowed to read initiatives through API unless owned.
filters = [owned_filter]
elif 'owner.id' in fields and user_id:
# Filter on user id.
# If owned then show also initiatives that are not approved.
filters = [owned_filter | public_filter]
elif user_id:
# Approved & owner or no closed segments or user has that segment
filters = [public_filter]
else:
return [Term(status='approved')]
# Guest user. Just approved projects without closed activities
filters = [
Term(status='approved') & (
Term(has_public_activities=True) |
Term(has_closed_activities=False)
)
]
return filters

def get_filters(self, request):
filters = super(InitiativeSearchFilter, self).get_filters(request)
Expand Down
125 changes: 118 additions & 7 deletions bluebottle/initiatives/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class InitiativeAPITestCase(TestCase):
"""

def setUp(self):
super().setUp()
self.client = JSONAPITestClient()
self.owner = BlueBottleUserFactory.create()
self.visitor = BlueBottleUserFactory.create()
Expand Down Expand Up @@ -809,7 +810,7 @@ def test_only_owner_permission_owner(self):
)

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
HTTP_AUTHORIZATION="JWT {0}".format(self.owner.get_jwt_token())
)
data = json.loads(response.content)
Expand Down Expand Up @@ -859,6 +860,116 @@ def test_filter_segment(self):
self.assertEqual(data['meta']['pagination']['count'], 1)
self.assertEqual(data['data'][0]['id'], str(first.pk))

def _create_closed_segments_initiatives(self):
self.closed_segment = SegmentFactory.create(
closed=True
)
self.open_segment = SegmentFactory.create(
closed=False
)
self.another_closed_segment = SegmentFactory.create(
closed=True
)

self.first = InitiativeFactory.create(
status='approved',
owner=self.owner
)
activity = DateActivityFactory.create(
status='open',
title='hup',
initiative=self.first,
)
activity.segments.add(self.closed_segment)
activity.segments.add(self.another_closed_segment)
activity.segments.add(self.open_segment)

activity = DateActivityFactory.create(
status='open',
initiative=self.first,
)
activity.segments.add(self.closed_segment)

self.second = InitiativeFactory.create(
owner=self.owner,
status='approved'
)
activity = DateActivityFactory.create(
status='open',
initiative=self.second,
)
activity.segments.add(self.closed_segment)
activity.segments.add(self.open_segment)

activity = DateActivityFactory.create(
status='open',
initiative=self.second,
)
activity.segments.add(self.open_segment)

self.third = InitiativeFactory.create(
owner=self.owner,
status='draft'
)
activity = DateActivityFactory.create(
status='draft',
initiative=self.third,
)
activity.segments.add(self.closed_segment)
activity.segments.add(self.open_segment)

self.fourth = InitiativeFactory.create(
owner=self.owner,
status='approved'
)

def test_filter_closed_segment_owner(self):
self._create_closed_segments_initiatives()
response = self.client.get(
self.url,
user=self.owner
)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 3)
response = self.client.get(
self.url + f'?filter[owner.id]={self.owner.id}',
user=self.owner
)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 4)

def test_filter_closed_segment_user_without_segment(self):
self._create_closed_segments_initiatives()
user = BlueBottleUserFactory.create()
user.segments.add(self.open_segment)

response = self.client.get(
self.url,
user=user
)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 2)

def test_filter_closed_segment_user_with_segment(self):
self._create_closed_segments_initiatives()
user = BlueBottleUserFactory.create()
user.segments.add(self.closed_segment)

response = self.client.get(
self.url,
user=user
)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 3)

def test_filter_closed_segment_guest(self):
self._create_closed_segments_initiatives()
response = self.client.get(
self.url
)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 2)

def test_filter_owner(self):
owned_initiatives = InitiativeFactory.create_batch(
2, status='submitted', owner=self.owner
Expand All @@ -870,7 +981,7 @@ def test_filter_owner(self):
InitiativeFactory.create_batch(4, status='submitted')

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
HTTP_AUTHORIZATION="JWT {0}".format(self.owner.get_jwt_token())
)

Expand All @@ -891,7 +1002,7 @@ def test_filter_owner_activity(self):
activity = DateActivityFactory.create(owner=self.owner, initiative=with_activity)

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
HTTP_AUTHORIZATION="JWT {0}".format(self.owner.get_jwt_token())
)

Expand Down Expand Up @@ -958,7 +1069,7 @@ def test_filter_not_owner(self):
InitiativeFactory.create_batch(3, status='approved')

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
user=self.visitor
)

Expand All @@ -975,7 +1086,7 @@ def test_filter_activity_manager(self):
InitiativeFactory.create_batch(4, status='approved')

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
HTTP_AUTHORIZATION="JWT {0}".format(self.owner.get_jwt_token())
)

Expand All @@ -992,7 +1103,7 @@ def test_filter_promoter(self):
InitiativeFactory.create_batch(4, status='approved')

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
HTTP_AUTHORIZATION="JWT {0}".format(self.owner.get_jwt_token())
)

Expand All @@ -1009,7 +1120,7 @@ def test_filter_owner_and_activity_manager(self):
InitiativeFactory.create_batch(4, status='approved')

response = self.client.get(
self.url + '?filter[owner.id]={}'.format(self.owner.pk),
self.url + f'?filter[owner.id]={self.owner.pk}',
HTTP_AUTHORIZATION="JWT {0}".format(self.owner.get_jwt_token())
)

Expand Down

0 comments on commit 2114eb3

Please sign in to comment.