Repository: bookwyrm-social/bookwyrm

Issue:
Set last edited by field for federated book data updates



Discussion:

Resolving patch:
diff --git a/bookwyrm/activitypub/book.py b/bookwyrm/activitypub/book.py
--- a/bookwyrm/activitypub/book.py
+++ b/bookwyrm/activitypub/book.py
@@ -11,6 +11,7 @@ class Book(ActivityObject):
     """ serializes an edition or work, abstract """
 
     title: str
+    lastEditedBy: str = None
     sortTitle: str = ""
     subtitle: str = ""
     description: str = ""
@@ -64,6 +65,7 @@ class Author(ActivityObject):
     """ author of a book """
 
     name: str
+    lastEditedBy: str = None
     born: str = None
     died: str = None
     aliases: List[str] = field(default_factory=lambda: [])
diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py
--- a/bookwyrm/models/activitypub_mixin.py
+++ b/bookwyrm/models/activitypub_mixin.py
@@ -148,13 +148,17 @@ def get_recipients(self, software=None):
         mentions = self.recipients if hasattr(self, "recipients") else []
 
         # we always send activities to explicitly mentioned users' inboxes
-        recipients = [u.inbox for u in mentions or []]
+        recipients = [u.inbox for u in mentions or [] if not u.local]
 
         # unless it's a dm, all the followers should receive the activity
         if privacy != "direct":
             # we will send this out to a subset of all remote users
-            queryset = user_model.viewer_aware_objects(user).filter(
-                local=False,
+            queryset = (
+                user_model.viewer_aware_objects(user)
+                .filter(
+                    local=False,
+                )
+                .distinct()
             )
             # filter users first by whether they're using the desired software
             # this lets us send book updates only to other bw servers
@@ -175,7 +179,7 @@ def get_recipients(self, software=None):
                 "inbox", flat=True
             )
             recipients += list(shared_inboxes) + list(inboxes)
-        return recipients
+        return list(set(recipients))
 
     def to_activity_dataclass(self):
         """ convert from a model to an activity """
diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py
--- a/bookwyrm/models/book.py
+++ b/bookwyrm/models/book.py
@@ -26,7 +26,11 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
         max_length=255, blank=True, null=True, deduplication_field=True
     )
 
-    last_edited_by = models.ForeignKey("User", on_delete=models.PROTECT, null=True)
+    last_edited_by = fields.ForeignKey(
+        "User",
+        on_delete=models.PROTECT,
+        null=True,
+    )
 
     class Meta:
         """ can't initialize this model, that wouldn't make sense """


Test patch:
diff --git a/bookwyrm/tests/data/bw_edition.json b/bookwyrm/tests/data/bw_edition.json
--- a/bookwyrm/tests/data/bw_edition.json
+++ b/bookwyrm/tests/data/bw_edition.json
@@ -1,5 +1,6 @@
 {
     "id": "https://bookwyrm.social/book/5989",
+    "lastEditedBy": "https://example.com/users/rat",
     "type": "Edition",
     "authors": [
         "https://bookwyrm.social/author/417"
diff --git a/bookwyrm/tests/views/inbox/test_inbox_update.py b/bookwyrm/tests/views/inbox/test_inbox_update.py
--- a/bookwyrm/tests/views/inbox/test_inbox_update.py
+++ b/bookwyrm/tests/views/inbox/test_inbox_update.py
@@ -23,6 +23,16 @@ def setUp(self):
         )
         self.local_user.remote_id = "https://example.com/user/mouse"
         self.local_user.save(broadcast=False)
+        with patch("bookwyrm.models.user.set_remote_server.delay"):
+            self.remote_user = models.User.objects.create_user(
+                "rat",
+                "rat@rat.com",
+                "ratword",
+                local=False,
+                remote_id="https://example.com/users/rat",
+                inbox="https://example.com/users/rat/inbox",
+                outbox="https://example.com/users/rat/outbox",
+            )
 
         self.create_json = {
             "id": "hi",
@@ -34,7 +44,7 @@ def setUp(self):
         }
         models.SiteSettings.objects.create()
 
-    def test_handle_update_list(self):
+    def test_update_list(self):
         """ a new list """
         with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
             book_list = models.List.objects.create(
@@ -68,7 +78,7 @@ def test_handle_update_list(self):
         self.assertEqual(book_list.description, "summary text")
         self.assertEqual(book_list.remote_id, "https://example.com/list/22")
 
-    def test_handle_update_user(self):
+    def test_update_user(self):
         """ update an existing user """
         # we only do this with remote users
         self.local_user.local = False
@@ -94,7 +104,7 @@ def test_handle_update_user(self):
         self.assertEqual(user.localname, "mouse")
         self.assertTrue(user.discoverable)
 
-    def test_handle_update_edition(self):
+    def test_update_edition(self):
         """ update an existing edition """
         datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_edition.json")
         bookdata = json.loads(datafile.read_bytes())
@@ -122,8 +132,9 @@ def test_handle_update_edition(self):
             )
         book = models.Edition.objects.get(id=book.id)
         self.assertEqual(book.title, "Piranesi")
+        self.assertEqual(book.last_edited_by, self.remote_user)
 
-    def test_handle_update_work(self):
+    def test_update_work(self):
         """ update an existing edition """
         datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_work.json")
         bookdata = json.loads(datafile.read_bytes())