diff --git a/journal/models/comment.py b/journal/models/comment.py index 29453dc8..8f488fbe 100644 --- a/journal/models/comment.py +++ b/journal/models/comment.py @@ -34,6 +34,9 @@ def ap_object(self): @classmethod def update_by_ap_object(cls, owner, item, obj, post_id, visibility): + p = cls.objects.filter(owner=owner, item=item).first() + if p and p.edited_time >= datetime.fromisoformat(obj["updated"]): + return p # incoming ap object is older than what we have, no update needed content = obj.get("content", "").strip() if obj else "" if not content: cls.objects.filter(owner=owner, item=item).delete() diff --git a/journal/models/common.py b/journal/models/common.py index 53b4b5ed..8d01aae0 100644 --- a/journal/models/common.py +++ b/journal/models/common.py @@ -198,10 +198,15 @@ def clear_post_ids(self): PiecePost.objects.filter(piece=self).delete() @cached_property - def latest_post(self): - # local post id is ordered by their created time + def latest_post_id(self): + # post id is ordered by their created time pp = PiecePost.objects.filter(piece=self).order_by("-post_id").first() - return Takahe.get_post(pp.post_id) if pp else None # type: ignore + return pp.post_id if pp else None + + @cached_property + def latest_post(self): + pk = self.latest_post_id + return Takahe.get_post(pk) if pk else None @cached_property def all_post_ids(self): @@ -212,6 +217,7 @@ def all_post_ids(self): class PiecePost(models.Model): + post_id: int piece = models.ForeignKey(Piece, on_delete=models.CASCADE) post = models.ForeignKey( "takahe.Post", db_constraint=False, db_index=True, on_delete=models.CASCADE diff --git a/journal/models/rating.py b/journal/models/rating.py index bd7505ec..fe8b114e 100644 --- a/journal/models/rating.py +++ b/journal/models/rating.py @@ -39,6 +39,9 @@ def ap_object(self): @classmethod def update_by_ap_object(cls, owner, item, obj, post_id, visibility): + p = cls.objects.filter(owner=owner, item=item).first() + if p and p.edited_time >= datetime.fromisoformat(obj["updated"]): + return p # incoming ap object is older than what we have, no update needed value = obj.get("value", 0) if obj else 0 if not value: cls.objects.filter(owner=owner, item=item).delete() diff --git a/journal/models/review.py b/journal/models/review.py index aa268a6b..0a2afaa7 100644 --- a/journal/models/review.py +++ b/journal/models/review.py @@ -53,6 +53,9 @@ def ap_object(self): @classmethod def update_by_ap_object(cls, owner, item, obj, post_id, visibility): + p = cls.objects.filter(owner=owner, item=item).first() + if p and p.edited_time >= datetime.fromisoformat(obj["updated"]): + return p # incoming ap object is older than what we have, no update needed content = ( obj["content"] if obj.get("mediaType") == "text/markdown" diff --git a/journal/models/shelf.py b/journal/models/shelf.py index 35318412..58d41ba1 100644 --- a/journal/models/shelf.py +++ b/journal/models/shelf.py @@ -80,10 +80,9 @@ def ap_object(self): def update_by_ap_object( cls, owner: APIdentity, item: Identity, obj: dict, post_id: int, visibility: int ): - # TODO check timestamp? (update may come in with inconsistent sequence) - if not obj: - cls.objects.filter(owner=owner, item=item).delete() - return + p = cls.objects.filter(owner=owner, item=item).first() + if p and p.edited_time >= datetime.fromisoformat(obj["updated"]): + return p # incoming ap object is older than what we have, no update needed shelf = owner.shelf_manager.get_shelf(obj["status"]) if not shelf: logger.warning(f"unable to locate shelf for {owner}, {obj}") @@ -147,7 +146,8 @@ def log_and_delete(self): self.delete() def link_post_id(self, post_id: int): - self.ensure_log_entry().link_post_id(post_id) + if self.local: + self.ensure_log_entry().link_post_id(post_id) return super().link_post_id(post_id) diff --git a/journal/templatetags/user_actions.py b/journal/templatetags/user_actions.py index 7483d6c2..cc817319 100644 --- a/journal/templatetags/user_actions.py +++ b/journal/templatetags/user_actions.py @@ -25,7 +25,7 @@ def like_piece_action(context, piece): action = {} if user and user.is_authenticated and piece and piece.latest_post: action = { - "taken": Takahe.post_liked_by(piece.latest_post.pk, user), + "taken": Takahe.post_liked_by(piece.latest_post.pk, user.identity.pk), "url": reverse("journal:like", args=[piece.uuid]), } return action diff --git a/neodb-takahe b/neodb-takahe index a8f8f9d5..bb145fa4 160000 --- a/neodb-takahe +++ b/neodb-takahe @@ -1 +1 @@ -Subproject commit a8f8f9d5931b062d1b8bb9455db7e7f0ad79ff41 +Subproject commit bb145fa4ae8d68810ee1963e9b24b3e7623a7069 diff --git a/social/templates/activity/mark_item.html b/social/templates/activity/mark_item.html index 8a52f79a..c9c5abc3 100644 --- a/social/templates/activity/mark_item.html +++ b/social/templates/activity/mark_item.html @@ -11,12 +11,10 @@ {% load duration %} {% wish_item_action activity.action_object.item as action %} - {% if activity.action_object.mark.comment_text %} - - {% liked_piece activity.action_object.mark.comment as liked %} - {% include 'like_stats.html' with liked=liked piece=activity.action_object.mark.comment %} - - {% endif %} + + {% liked_piece activity.action_object as liked %} + {% include 'like_stats.html' with liked=liked piece=activity.action_object %} + {% comment %} diff --git a/social/templates/feed_data.html b/social/templates/feed_data.html index 94dfedb2..5c458205 100644 --- a/social/templates/feed_data.html +++ b/social/templates/feed_data.html @@ -24,7 +24,7 @@ {{ activity.owner.display_name }} - @{{ activity.owner.handler }} + {{ activity.owner.handler }} {% with "activity/"|add:activity.template|add:".html" as template %} diff --git a/takahe/ap_handlers.py b/takahe/ap_handlers.py index 029a6118..3c55b229 100644 --- a/takahe/ap_handlers.py +++ b/takahe/ap_handlers.py @@ -61,12 +61,16 @@ def _get_or_create_item(item_obj): if typ in ["TVEpisode", "PodcastEpisode"]: # TODO support episode item # match and fetch parent item first + logger.debug(f"{typ}:{url} not supported yet") return None site = SiteManager.get_site_by_url(url) if not site: + logger.warning(f"Site not found for {url}") return None site.get_resource_ready() item = site.get_item() + if not item: + logger.warning(f"Item not fetched for {url}") return item @@ -100,12 +104,21 @@ def post_fetched(pk, obj): logger.warning(f"Post {post} has no local item matched or created") return for p in pieces: - cls = _supported_ap_journal_types[p["type"]] + cls = _supported_ap_journal_types.get(p["type"]) + if not cls: + logger.warning(f'Unknown link type {p["type"]}') + continue cls.update_by_ap_object(owner, item, p, pk, _get_visibility(post.visibility)) def post_deleted(pk, obj): - Piece.objects.filter(posts__id=pk, local=False).delete() + for piece in Piece.objects.filter(posts__id=pk, local=False): + # delete piece if the deleted post is the most recent one for the piece + if piece.latest_post_id == pk: + logger.debug(f"Deleting remote piece {piece}") + piece.delete() + else: + logger.debug(f"Matched remote piece {piece} has newer posts, not deleting") def identity_fetched(pk):