import re from datetime import datetime from functools import cached_property from django.conf import settings from django.db import models from django.utils import timezone from django.utils.translation import gettext as _ from markdownify import markdownify as md from markdownx.models import MarkdownxField from catalog.models import Item from takahe.utils import Takahe from users.models import APIdentity from .common import Content from .rating import Rating from .renderers import render_md, render_post_with_macro, render_rating from .shelf import ShelfManager _RE_HTML_TAG = re.compile(r"<[^>]*>") _RE_SPOILER_TAG = re.compile(r'<(div|span)\sclass="spoiler">.*') class Review(Content): url_path = "review" title = models.CharField(max_length=500, blank=False, null=False) body = MarkdownxField() @property def display_title(self): return self.title @property def brief_description(self): return self.plain_content[:155] @property def html_content(self): return render_md(self.body) @property def plain_content(self): html = render_md(self.body) return _RE_HTML_TAG.sub( " ", _RE_SPOILER_TAG.sub("***", html.replace("\n", " ")) ) @property def ap_object(self): return { "id": self.absolute_url, "type": "Review", "name": self.title, # "content": self.html_content, "content": self.body, "mediaType": "text/markdown", "published": self.created_time.isoformat(), "updated": self.edited_time.isoformat(), "attributedTo": self.owner.actor_uri, "withRegardTo": self.item.absolute_url, "href": self.absolute_url, } @classmethod def update_by_ap_object(cls, owner, item, obj, post): 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" else md(obj["content"]) ) d = { "title": obj["name"], "body": content, "local": False, "remote_id": obj["id"], "visibility": Takahe.visibility_t2n(post.visibility), "created_time": datetime.fromisoformat(obj["published"]), "edited_time": datetime.fromisoformat(obj["updated"]), } p, _ = cls.objects.update_or_create(owner=owner, item=item, defaults=d) p.link_post_id(post.id) return p def get_crosspost_postfix(self): tags = render_post_with_macro( self.owner.user.preference.mastodon_append_tag, self.item ) return "\n" + tags if tags else "" def get_crosspost_template(self): return _(ShelfManager.get_action_template("reviewed", self.item.category)) def to_crosspost_params(self): content = ( self.get_crosspost_template().format(item=self.item.display_title) + " ##rating##\n##obj##\n##obj_link_if_plain##" + self.get_crosspost_postfix() ) params = {"content": content, "obj": self, "rating": self.rating_grade} return params def to_post_params(self): item_link = f"{settings.SITE_INFO['site_url']}/~neodb~{self.item.url}" prepend_content = ( self.get_crosspost_template().format( item=f'{self.item.display_title}' ) + f'
{self.title}' ) content = f"{render_rating(self.rating_grade)}\n{self.get_crosspost_postfix()}" return { "prepend_content": prepend_content, "content": content, } @cached_property def mark(self): from .mark import Mark m = Mark(self.owner, self.item) m.review = self return m @cached_property def rating_grade(self): return Rating.get_item_rating(self.item, self.owner) @classmethod def update_item_review( cls, item: Item, owner: APIdentity, title: str | None, body: str | None, visibility=0, created_time=None, share_to_mastodon: bool = False, ): review = Review.objects.filter(owner=owner, item=item).first() delete_existing_post = False if review is not None: if title is None: review.delete() return delete_existing_post = review.visibility != visibility defaults = { "title": title, "body": body, "visibility": visibility, } if created_time: defaults["created_time"] = ( created_time if created_time < timezone.now() else timezone.now() ) review, created = cls.objects.update_or_create( item=item, owner=owner, defaults=defaults ) update_mode = 1 if delete_existing_post else 0 review.sync_to_timeline(update_mode) if share_to_mastodon: review.sync_to_social_accounts(update_mode) return review