✨(backend) reconciliation requests update comments
Adds management of threads, comments and reactions in the reconciliation requests.
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
from django.contrib import admin, messages
|
||||
from django.contrib.auth import admin as auth_admin
|
||||
from django.db import transaction
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@@ -119,7 +118,6 @@ def process_reconciliation(_modeladmin, _request, queryset):
|
||||
"""
|
||||
Admin action to process selected user reconciliations.
|
||||
The action will process only entries that are ready and have both emails checked.
|
||||
|
||||
"""
|
||||
processable_entries = queryset.filter(
|
||||
status="ready", active_email_checked=True, inactive_email_checked=True
|
||||
|
||||
@@ -386,6 +386,8 @@ class UserReconciliation(BaseModel):
|
||||
- Transfer document accesses from inactive to active user, updating roles as needed.
|
||||
- Transfer document favorites from inactive to active user.
|
||||
- Transfer link traces from inactive to active user.
|
||||
- Transfer comment-related content from inactive to active user
|
||||
(threads, comments and reactions)
|
||||
- Activate the active user and deactivate the inactive user.
|
||||
- Update the reconciliation entry itself.
|
||||
"""
|
||||
@@ -398,6 +400,9 @@ class UserReconciliation(BaseModel):
|
||||
update_favorites, removed_favorites = (
|
||||
self.prepare_document_favorite_reconciliation()
|
||||
)
|
||||
updated_threads = self.prepare_thread_reconciliation()
|
||||
updated_comments = self.prepare_comment_reconciliation()
|
||||
updated_reactions, removed_reactions = self.prepare_reaction_reconciliation()
|
||||
|
||||
self.active_user.is_active = True
|
||||
self.inactive_user.is_active = False
|
||||
@@ -419,6 +424,28 @@ class UserReconciliation(BaseModel):
|
||||
ids_to_delete = [entry.id for entry in removed_linktraces]
|
||||
LinkTrace.objects.filter(id__in=ids_to_delete).delete()
|
||||
|
||||
Thread.objects.bulk_update(updated_threads, ["creator"])
|
||||
Comment.objects.bulk_update(updated_comments, ["user"])
|
||||
|
||||
# pylint: disable=C0103
|
||||
ReactionThroughModel = Reaction.users.through
|
||||
reactions_to_create = []
|
||||
for updated_reaction in updated_reactions:
|
||||
reactions_to_create.append(
|
||||
ReactionThroughModel(
|
||||
user_id=self.active_user.pk, reaction_id=updated_reaction.pk
|
||||
)
|
||||
)
|
||||
|
||||
if reactions_to_create:
|
||||
ReactionThroughModel.objects.bulk_create(reactions_to_create)
|
||||
|
||||
if removed_reactions:
|
||||
ids_to_delete = [entry.id for entry in removed_reactions]
|
||||
ReactionThroughModel.objects.filter(
|
||||
reaction_id__in=ids_to_delete, user_id=self.inactive_user.pk
|
||||
).delete()
|
||||
|
||||
User.objects.bulk_update([self.active_user, self.inactive_user], ["is_active"])
|
||||
|
||||
# Wrap up the reconciliation entry
|
||||
@@ -507,6 +534,48 @@ class UserReconciliation(BaseModel):
|
||||
|
||||
return updated_linktraces, removed_linktraces
|
||||
|
||||
def prepare_thread_reconciliation(self):
|
||||
"""
|
||||
Prepare the reconciliation by transferring threads from the inactive user
|
||||
to the active user.
|
||||
"""
|
||||
updated_threads = []
|
||||
|
||||
inactive_threads = Thread.objects.filter(creator=self.inactive_user)
|
||||
|
||||
for entry in inactive_threads:
|
||||
entry.creator = self.active_user
|
||||
updated_threads.append(entry)
|
||||
|
||||
return updated_threads
|
||||
|
||||
def prepare_comment_reconciliation(self):
|
||||
"""
|
||||
Prepare the reconciliation by transferring comments from the inactive user
|
||||
to the active user.
|
||||
"""
|
||||
updated_comments = []
|
||||
|
||||
inactive_comments = Comment.objects.filter(user=self.inactive_user)
|
||||
|
||||
for entry in inactive_comments:
|
||||
entry.user = self.active_user
|
||||
updated_comments.append(entry)
|
||||
|
||||
return updated_comments
|
||||
|
||||
def prepare_reaction_reconciliation(self):
|
||||
"""
|
||||
Prepare the reconciliation by creating missing reactions for the active user
|
||||
(ie, the ones that exist for the inactive user but not the active user)
|
||||
and then deleting all reactions of the inactive user.
|
||||
"""
|
||||
|
||||
inactive_reactions = Reaction.objects.filter(users=self.inactive_user)
|
||||
updated_reactions = inactive_reactions.exclude(users=self.active_user)
|
||||
|
||||
return updated_reactions, inactive_reactions
|
||||
|
||||
def send_reconciliation_confirm_email(
|
||||
self, user, user_type, confirmation_id, language=None
|
||||
):
|
||||
|
||||
@@ -466,8 +466,6 @@ def test_process_reconciliation_updates_linktraces(
|
||||
|
||||
u1_2 = userdocs_u1[2]
|
||||
u1_5 = userdocs_u1[5]
|
||||
u2doc1 = userdocs_u2[1].document
|
||||
u2doc5 = userdocs_u2[5].document
|
||||
|
||||
doc_both = u1_2.document
|
||||
models.LinkTrace.objects.create(document=doc_both, user=user_1)
|
||||
@@ -530,6 +528,71 @@ def test_process_reconciliation_updates_linktraces(
|
||||
).exists()
|
||||
|
||||
|
||||
def test_process_reconciliation_updates_threads_comments_reactions(
|
||||
user_reconciliation_users_and_docs,
|
||||
):
|
||||
"""Test that threads, comments and reactions are transferred/deduplicated
|
||||
on reconciliation."""
|
||||
user_1, user_2, _userdocs_u1, userdocs_u2 = user_reconciliation_users_and_docs
|
||||
|
||||
# Use a document from the inactive user's set
|
||||
document = userdocs_u2[0].document
|
||||
|
||||
# Thread and comment created by inactive user -> should be moved to active
|
||||
thread = factories.ThreadFactory(document=document, creator=user_2)
|
||||
comment = factories.CommentFactory(thread=thread, user=user_2)
|
||||
|
||||
# Reaction where only inactive user reacted -> should be moved to active user
|
||||
reaction_inactive_only = factories.ReactionFactory(comment=comment, users=[user_2])
|
||||
|
||||
# Reaction where both users reacted -> inactive user's participation should be removed
|
||||
thread2 = factories.ThreadFactory(document=document, creator=user_1)
|
||||
comment2 = factories.CommentFactory(thread=thread2, user=user_1)
|
||||
reaction_both = factories.ReactionFactory(comment=comment2, users=[user_1, user_2])
|
||||
|
||||
# Reaction where only active user reacted -> unchanged
|
||||
thread3 = factories.ThreadFactory(document=document, creator=user_1)
|
||||
comment3 = factories.CommentFactory(thread=thread3, user=user_1)
|
||||
reaction_active_only = factories.ReactionFactory(comment=comment3, users=[user_1])
|
||||
|
||||
rec = models.UserReconciliation.objects.create(
|
||||
active_email=user_1.email,
|
||||
inactive_email=user_2.email,
|
||||
active_user=user_1,
|
||||
inactive_user=user_2,
|
||||
active_email_checked=True,
|
||||
inactive_email_checked=True,
|
||||
status="ready",
|
||||
)
|
||||
|
||||
qs = models.UserReconciliation.objects.filter(id=rec.id)
|
||||
process_reconciliation(None, None, qs)
|
||||
|
||||
# Refresh objects
|
||||
thread.refresh_from_db()
|
||||
comment.refresh_from_db()
|
||||
reaction_inactive_only.refresh_from_db()
|
||||
reaction_both.refresh_from_db()
|
||||
reaction_active_only.refresh_from_db()
|
||||
|
||||
# Thread and comment creator should now be the active user
|
||||
assert thread.creator == user_1
|
||||
assert comment.user == user_1
|
||||
|
||||
# reaction_inactive_only: inactive user's participation should be removed and
|
||||
# active user's participation added
|
||||
reaction_inactive_only.refresh_from_db()
|
||||
assert not reaction_inactive_only.users.filter(pk=user_2.pk).exists()
|
||||
assert reaction_inactive_only.users.filter(pk=user_1.pk).exists()
|
||||
|
||||
# reaction_both: should end up with only active user's participation
|
||||
assert reaction_both.users.filter(pk=user_2.pk).exists() is False
|
||||
assert reaction_both.users.filter(pk=user_1.pk).exists() is True
|
||||
|
||||
# reaction_active_only should still have active user's participation
|
||||
assert reaction_active_only.users.filter(pk=user_1.pk).exists()
|
||||
|
||||
|
||||
def test_process_reconciliation_updates_favorites(
|
||||
user_reconciliation_users_and_docs,
|
||||
):
|
||||
@@ -538,8 +601,6 @@ def test_process_reconciliation_updates_favorites(
|
||||
|
||||
u1_2 = userdocs_u1[2]
|
||||
u1_5 = userdocs_u1[5]
|
||||
u2doc1 = userdocs_u2[1].document
|
||||
u2doc5 = userdocs_u2[5].document
|
||||
|
||||
doc_both = u1_2.document
|
||||
models.DocumentFavorite.objects.create(document=doc_both, user=user_1)
|
||||
@@ -592,9 +653,15 @@ def test_process_reconciliation_updates_favorites(
|
||||
)
|
||||
|
||||
# doc_inactive_only should now be linked to active user
|
||||
df = models.DocumentFavorite.objects.filter(
|
||||
assert (
|
||||
models.DocumentFavorite.objects.filter(
|
||||
user=user_2, document=doc_inactive_only
|
||||
).count()
|
||||
== 0
|
||||
)
|
||||
assert models.DocumentFavorite.objects.filter(
|
||||
user=user_1, document=doc_inactive_only
|
||||
).first()
|
||||
).exists()
|
||||
|
||||
# doc_active_only should still belong to active user
|
||||
assert models.DocumentFavorite.objects.filter(
|
||||
|
||||
Reference in New Issue
Block a user