Skip to content

Commit 539e4ea

Browse files
authored
Merge pull request #3217 from ResearchHub/financial-activity-scope
Add financial scope to activity feed
2 parents d3acac0 + 4f209a8 commit 539e4ea

File tree

2 files changed

+145
-14
lines changed

2 files changed

+145
-14
lines changed

src/feed/tests/views/test_activity_feed_view.py

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import uuid
1+
from decimal import Decimal
22

33
from django.contrib.auth import get_user_model
44
from django.contrib.contenttypes.models import ContentType
@@ -9,8 +9,13 @@
99
from rest_framework.test import APIClient
1010

1111
from feed.models import FeedEntry
12+
from purchase.models import Fundraise
1213
from purchase.related_models.grant_application_model import GrantApplication
1314
from purchase.related_models.grant_model import Grant
15+
from purchase.related_models.purchase_model import Purchase
16+
from purchase.related_models.usd_fundraise_contribution_model import (
17+
UsdFundraiseContribution,
18+
)
1419
from researchhub_comment.constants.rh_comment_thread_types import (
1520
COMMUNITY_REVIEW,
1621
GENERIC_COMMENT,
@@ -28,19 +33,12 @@
2833
from researchhub_document.related_models.researchhub_unified_document_model import (
2934
ResearchhubUnifiedDocument,
3035
)
31-
from utils.test_helpers import AWSMockTestCase
36+
from utils.test_helpers import AWSMockTestCase, create_test_user
3237

3338
User = get_user_model()
3439
ACTIVITY_LIST_URL = reverse("activity_feed-list")
3540

3641

37-
def _make_user(username=None):
38-
return User.objects.create_user(
39-
username=username or uuid.uuid4().hex,
40-
password=uuid.uuid4().hex,
41-
)
42-
43-
4442
def _make_feed_entry(
4543
content_type_model,
4644
object_id,
@@ -66,7 +64,7 @@ class ActivityFeedBaseTests(AWSMockTestCase):
6664

6765
def setUp(self):
6866
super().setUp()
69-
self.user = _make_user("activity_user")
67+
self.user = create_test_user("activity_user")
7068
self.client = APIClient()
7169

7270
self.prereg_doc = ResearchhubUnifiedDocument.objects.create(
@@ -194,7 +192,7 @@ class ActivityFeedGrantFilterTests(AWSMockTestCase):
194192

195193
def setUp(self):
196194
super().setUp()
197-
self.user = _make_user()
195+
self.user = create_test_user()
198196
self.client = APIClient()
199197

200198
# Grant document + post + Grant object
@@ -421,7 +419,7 @@ class ActivityFeedScopeGrantsTests(AWSMockTestCase):
421419

422420
def setUp(self):
423421
super().setUp()
424-
self.user = _make_user()
422+
self.user = create_test_user()
425423
self.client = APIClient()
426424

427425
# Grant A with an applied preregistration
@@ -590,7 +588,7 @@ class ActivityFeedActionDateOrderingTests(AWSMockTestCase):
590588

591589
def setUp(self):
592590
super().setUp()
593-
self.user = _make_user()
591+
self.user = create_test_user()
594592
self.client = APIClient()
595593

596594
def test_action_date_determines_order_not_created_date(self):
@@ -654,7 +652,7 @@ class ActivityFeedPeerReviewFilterTests(AWSMockTestCase):
654652

655653
def setUp(self):
656654
super().setUp()
657-
self.user = _make_user()
655+
self.user = create_test_user()
658656
self.client = APIClient()
659657

660658
self.doc = ResearchhubUnifiedDocument.objects.create(
@@ -787,3 +785,106 @@ def test_scope_peer_reviews_empty_when_none_exist(self):
787785
# Assert
788786
self.assertEqual(resp.status_code, status.HTTP_200_OK)
789787
self.assertEqual(len(resp.data["results"]), 0)
788+
789+
790+
class ActivityFeedFinancialScopeTests(AWSMockTestCase):
791+
"""
792+
Test financial scope returns fundraise contribution activities.
793+
"""
794+
795+
def setUp(self):
796+
super().setUp()
797+
self.user = create_test_user()
798+
self.client = APIClient()
799+
800+
self.proposal_doc = ResearchhubUnifiedDocument.objects.create(
801+
document_type=PREREGISTRATION,
802+
)
803+
self.proposal_post = ResearchhubPost.objects.create(
804+
title="Funding Proposal",
805+
created_by=self.user,
806+
document_type=PREREGISTRATION,
807+
unified_document=self.proposal_doc,
808+
)
809+
self.fundraise = Fundraise.objects.create(
810+
unified_document=self.proposal_doc,
811+
created_by=self.user,
812+
goal_amount=Decimal("1000.00"),
813+
goal_currency="USD",
814+
status=Fundraise.OPEN,
815+
)
816+
817+
fundraise_ct = ContentType.objects.get_for_model(Fundraise)
818+
self.rsc_contribution = Purchase.objects.create(
819+
user=self.user,
820+
content_type=fundraise_ct,
821+
object_id=self.fundraise.id,
822+
purchase_type=Purchase.FUNDRAISE_CONTRIBUTION,
823+
purchase_method=Purchase.OFF_CHAIN,
824+
amount="100",
825+
)
826+
self.usd_contribution = UsdFundraiseContribution.objects.create(
827+
user=self.user,
828+
fundraise=self.fundraise,
829+
amount_cents=5500,
830+
fee_cents=495,
831+
origin_fund_id="test-origin",
832+
destination_org_id="test-destination",
833+
)
834+
835+
self.rsc_entry = _make_feed_entry(
836+
Purchase,
837+
self.rsc_contribution.id,
838+
self.proposal_doc,
839+
user=self.user,
840+
)
841+
self.usd_entry = _make_feed_entry(
842+
UsdFundraiseContribution,
843+
self.usd_contribution.id,
844+
self.proposal_doc,
845+
user=self.user,
846+
)
847+
848+
self.unrelated_doc = ResearchhubUnifiedDocument.objects.create(
849+
document_type="DISCUSSION",
850+
)
851+
self.unrelated_post = ResearchhubPost.objects.create(
852+
title="Unrelated Discussion",
853+
created_by=self.user,
854+
document_type="DISCUSSION",
855+
unified_document=self.unrelated_doc,
856+
)
857+
self.unrelated_entry = _make_feed_entry(
858+
ResearchhubPost,
859+
self.unrelated_post.id,
860+
self.unrelated_doc,
861+
user=self.user,
862+
)
863+
864+
post_ct = ContentType.objects.get_for_model(ResearchhubPost)
865+
self.boost_purchase = Purchase.objects.create(
866+
user=self.user,
867+
content_type=post_ct,
868+
object_id=self.unrelated_post.id,
869+
purchase_type=Purchase.BOOST,
870+
purchase_method=Purchase.OFF_CHAIN,
871+
amount="10",
872+
)
873+
self.boost_entry = _make_feed_entry(
874+
Purchase,
875+
self.boost_purchase.id,
876+
self.unrelated_doc,
877+
user=self.user,
878+
)
879+
880+
def test_scope_financial_includes_rsc_and_usd_contributions(self):
881+
# Act
882+
resp = self.client.get(ACTIVITY_LIST_URL, {"scope": "financial"})
883+
884+
# Assert
885+
self.assertEqual(resp.status_code, status.HTTP_200_OK)
886+
ids = {entry["id"] for entry in resp.data["results"]}
887+
self.assertIn(self.rsc_entry.id, ids)
888+
self.assertIn(self.usd_entry.id, ids)
889+
self.assertNotIn(self.unrelated_entry.id, ids)
890+
self.assertNotIn(self.boost_entry.id, ids)

src/feed/views/activity_feed_view.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.contrib.contenttypes.models import ContentType
2+
from django.db.models import Q
23
from rest_framework.viewsets import ModelViewSet
34

45
from feed.models import FeedEntry
@@ -7,6 +8,10 @@
78
from feed.views.feed_view_mixin import FeedViewMixin
89
from purchase.related_models.grant_application_model import GrantApplication
910
from purchase.related_models.grant_model import Grant
11+
from purchase.related_models.purchase_model import Purchase
12+
from purchase.related_models.usd_fundraise_contribution_model import (
13+
UsdFundraiseContribution,
14+
)
1015
from researchhub_comment.constants.rh_comment_thread_types import (
1116
COMMUNITY_REVIEW,
1217
PEER_REVIEW,
@@ -23,6 +28,8 @@ class ActivityFeedViewSet(FeedViewMixin, ModelViewSet):
2328
- scope: "grants" returns all activity across every grant and
2429
every preregistration that applied to any grant.
2530
"peer_reviews" returns only peer review comments.
31+
"financial" returns only fundraise contribution activity
32+
across RSC and USD contributions.
2633
- document_type: PREREGISTRATION, GRANT, etc.
2734
- grant_id: all activity on a grant and its applied preregistrations
2835
- content_type: RHCOMMENTMODEL, RESEARCHHUBPOST, PAPER, etc.
@@ -67,6 +74,8 @@ def get_queryset(self):
6774
queryset = self._filter_all_grants(queryset)
6875
elif scope == "peer_reviews":
6976
queryset = self._filter_peer_reviews(queryset)
77+
elif scope == "financial":
78+
queryset = self._filter_financial_activities(queryset)
7079
else:
7180
document_type = self.request.query_params.get("document_type")
7281
if document_type:
@@ -138,6 +147,27 @@ def _filter_peer_reviews(queryset):
138147
object_id__in=peer_review_ids,
139148
)
140149

150+
@staticmethod
151+
def _filter_financial_activities(queryset):
152+
"""
153+
Return feed entries for fundraise contributions.
154+
"""
155+
purchase_type = ContentType.objects.get_for_model(Purchase)
156+
usd_contribution_type = ContentType.objects.get_for_model(
157+
UsdFundraiseContribution
158+
)
159+
contribution_purchase_ids = Purchase.objects.filter(
160+
purchase_type=Purchase.FUNDRAISE_CONTRIBUTION
161+
).values_list("id", flat=True)
162+
163+
return queryset.filter(
164+
Q(
165+
content_type=purchase_type,
166+
object_id__in=contribution_purchase_ids,
167+
)
168+
| Q(content_type=usd_contribution_type)
169+
)
170+
141171
@staticmethod
142172
def _filter_by_content_type(queryset, content_type_name):
143173
"""Filter feed entries by the model name of their content_type."""

0 commit comments

Comments
 (0)