Implement reaction aggregation.

This commit is contained in:
Jamie Bliss 2023-11-09 14:51:09 -05:00
parent 42b0b5831a
commit b3e67ffe3a
No known key found for this signature in database
2 changed files with 90 additions and 1 deletions

View file

@ -610,12 +610,24 @@ class Post(StatorModel):
"likes": self.interactions.filter( "likes": self.interactions.filter(
type=PostInteraction.Types.like, type=PostInteraction.Types.like,
state__in=PostInteractionStates.group_active(), state__in=PostInteractionStates.group_active(),
).count(), )
.values("identity")
.distinct()
.count(), # This counts each user that's had any likes/reactions
"boosts": self.interactions.filter( "boosts": self.interactions.filter(
type=PostInteraction.Types.boost, type=PostInteraction.Types.boost,
state__in=PostInteractionStates.group_active(), state__in=PostInteractionStates.group_active(),
).count(), ).count(),
"replies": Post.objects.filter(in_reply_to=self.object_uri).count(), "replies": Post.objects.filter(in_reply_to=self.object_uri).count(),
"reactions": {
row["value"]: row["count"]
for row in self.interactions.filter(
type=PostInteraction.Types.like,
state__in=PostInteractionStates.group_active(),
)
.values("value")
.annotate(count=models.Count("identity"))
},
} }
if save: if save:
self.save() self.save()

View file

@ -230,6 +230,83 @@ def test_react_undo_mismatched(
assert len(events) == 1 assert len(events) == 1
@pytest.mark.django_db
@pytest.mark.parametrize("local", [True, False])
@pytest.mark.parametrize("reaction", ["\U0001F607"])
def test_react_stats(
identity: Identity,
other_identity: Identity,
remote_identity: Identity,
stator,
local: bool,
reaction: str,
):
"""
Checks basic post stats generation
"""
post = Post.create_local(author=identity, content="I love birds!")
if local:
PostService(post).like_as(other_identity, reaction)
else:
message = {
"id": "test",
"type": "Like",
"actor": remote_identity.actor_uri,
"object": post.object_uri,
"content": reaction,
}
InboxMessage.objects.create(message=message)
# Run stator thrice - to receive the post, make fanouts and then process them
stator.run_single_cycle()
stator.run_single_cycle()
stator.run_single_cycle()
post.refresh_from_db()
assert "reactions" in post.stats
assert post.stats["reactions"] == {reaction: 1}
@pytest.mark.django_db
@pytest.mark.parametrize("local", [True, False])
def test_react_stats_multiple(
identity: Identity,
other_identity: Identity,
remote_identity: Identity,
stator,
local: bool,
):
"""
Ensures that multiple reactions get aggregated correctly.
Basically, if the same person leaves multiple reactions, aggregate all of them into one Like.
"""
post = Post.create_local(author=identity, content="I love birds!")
for i, reaction in enumerate("abc"):
if local:
PostService(post).like_as(other_identity, reaction)
else:
message = {
"id": f"test{i}",
"type": "Like",
"actor": remote_identity.actor_uri,
"object": post.object_uri,
"content": reaction,
}
InboxMessage.objects.create(message=message)
# Run stator thrice - to receive the post, make fanouts and then process them
stator.run_single_cycle()
stator.run_single_cycle()
stator.run_single_cycle()
post.refresh_from_db()
assert post.stats["reactions"] == {"a": 1, "b": 1, "c": 1}
assert post.stats["likes"] == 1
# TODO: Test that multiple reactions can be added and deleted correctly # TODO: Test that multiple reactions can be added and deleted correctly
# TODO: How should plain likes and reactions from the same source be handled? # TODO: How should plain likes and reactions from the same source be handled?