Status.content and Status.spoiler_text are now Status.content_source and Status.spoiler_source.

HTML renderings of each one are cached. You can access them at Status.content_as_html
and Status.spoiler_as_html.
merge-requests/2/head
Marnanel Thurman 2021-02-16 22:58:42 +00:00
rodzic 061ce40101
commit 37d53b2e4e
14 zmienionych plików z 213 dodań i 50 usunięć

Wyświetl plik

@ -231,9 +231,9 @@ def on_note(fields, address):
remote_url = fields['id'],
account = poster,
in_reply_to = in_reply_to,
content = fields['content'],
content_source = fields['content'],
sensitive = is_sensitive,
spoiler_text = spoiler_text,
spoiler_source = spoiler_text,
visibility = visibility,
language = language,
)

Wyświetl plik

@ -33,7 +33,7 @@ class StatusObjectSerializer(serializers.ModelSerializer):
'id': status.url,
'url': status.url,
'type': 'Note',
'summary': status.spoiler_text_as_html,
'summary': status.spoiler_as_html,
'inReplyTo': status.in_reply_to,
'published': status.created_at,
'attributedTo': status.account.url,
@ -43,7 +43,7 @@ class StatusObjectSerializer(serializers.ModelSerializer):
'conversation': status.conversation,
'content': status.content_as_html,
'contentMap': {
status.language: status.content,
status.language: status.content_source,
},
'attachment': status.media_attachments,
'tag': status.tags,

Wyświetl plik

@ -125,7 +125,7 @@ class Tests(TestCase):
)
self.assertEqual(
original_status.content,
original_status.content_source,
'Hello world',
msg = 'the status was reblogged at the end',
)

Wyświetl plik

@ -69,7 +69,7 @@ class Tests(Create_TestCase):
import kepi.trilby_api.models as trilby_models
result = trilby_models.Status.objects.filter(
content = content,
content_source = content,
)
if result:

Wyświetl plik

@ -82,8 +82,22 @@ class Tests(TestCase):
result = trilby_models.Status(
account = self._alice,
visibility = trilby_utils.VISIBILITY_PUBLIC,
content = "<p>Victoria Wood parodying Peter Skellern. I laughed so much at this, though you might have to know both singers&apos; work in order to find it quite as funny.</p><p>- love song<br />- self-doubt<br />- refs to northern England<br />- preamble<br />- piano solo<br />- brass band<br />- choir backing<br />- love is cosy<br />- heavy rhotic vowels</p><p><a href=\"https://youtu.be/782hqdmnq7g\" rel=\"nofollow noopener\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">youtu.be/782hqdmnq7g</span><span class=\"invisible\"></span></a></p>",
)
content_source = """Victoria Wood parodying Peter Skellern.
I laughed so much at this, though you might have to know both singers' work
in order to find it quite as funny:
- love song
- self-doubt
- refs to northern England
- preamble
- piano solo
- brass band
- choir backing
- love is cosy
- heavy rhotic vowels
https://youtu.be/782hqdmnq7g""",
)
result.save()
return result

Wyświetl plik

@ -59,7 +59,7 @@ def on_posted(sender, **kwargs):
"object": {
"type": "Note",
"id": sender.url,
"content": sender.content,
"content": sender.content_as_html,
}
},
sender = sender.account,

Wyświetl plik

@ -0,0 +1,38 @@
# Generated by Django 3.0.9 on 2021-02-16 19:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('trilby_api', '0028_mention'),
]
operations = [
migrations.RenameField(
model_name='status',
old_name='spoiler_text',
new_name='spoiler_source',
),
migrations.RemoveField(
model_name='status',
name='content',
),
migrations.AddField(
model_name='status',
name='content_as_html_denormed',
field=models.TextField(default=None, editable=False, help_text='HTML rendering of content_source. Do not edit!', null=True),
),
migrations.AddField(
model_name='status',
name='content_source',
field=models.TextField(default='', help_text='Text of the status, as entered'),
preserve_default=False,
),
migrations.AddField(
model_name='status',
name='spoiler_as_html_denormed',
field=models.CharField(default=None, editable=False, max_length=255, null=True),
),
]

Wyświetl plik

@ -59,7 +59,15 @@ class Status(PolymorphicModel):
blank = True,
)
content = models.TextField(
content_source = models.TextField(
help_text = 'Text of the status, as entered',
)
content_as_html_denormed = models.TextField(
help_text = 'HTML rendering of content_source. Do not edit!',
editable = False,
null = True,
default = None,
)
created_at = models.DateTimeField(
@ -72,13 +80,20 @@ class Status(PolymorphicModel):
default = False,
)
spoiler_text = models.CharField(
spoiler_source = models.CharField(
max_length = 255,
null = True,
blank = True,
default = '',
)
spoiler_as_html_denormed = models.CharField(
max_length = 255,
null = True,
editable = False,
default = None,
)
visibility = models.CharField(
max_length = 1,
default = trilby_utils.VISIBILITY_PUBLIC,
@ -107,6 +122,44 @@ class Status(PolymorphicModel):
default = None,
)
@property
def content_as_html(self):
"""
Returns an HTML rendition of content_source.
The return value will be cached.
Saving the record will clear this cache.
"""
if self.content_as_html_denormed is not None:
return self.content_as_html_denormed
if self.content_source is None:
result = '<p></p>'
else:
result = markdown.markdown(self.content_source)
self.content_as_html_denormed = result
return result
@property
def spoiler_as_html(self):
"""
Returns an HTML rendition of spoiler_source.
The return value will be cached.
Saving the record will clear this cache.
"""
if self.spoiler_as_html_denormed is not None:
return self.spoiler_as_html_denormed
if self.spoiler_source is None:
result = '<p></p>'
else:
result = markdown.markdown(self.spoiler_source)
self.spoiler_as_html_denormed = result
return result
@property
def emojis(self):
return [] # TODO
@ -268,6 +321,19 @@ class Status(PolymorphicModel):
if self.in_reply_to == self:
raise ValueError("Status can't be a reply to itself")
if not newly_made:
old = self.__class__.objects.get(pk=self.pk)
if self.content_source != old.content_source:
logger.debug("%s: content changed; flushing HTML cache",
self)
self.content_as_html_denormed = None
if self.spoiler_source != old.spoiler_source:
logger.debug("%s: spoiler changed; flushing HTML cache",
self)
self.spoiler_as_html_denormed = None
super().save(*args, **kwargs)
if send_signal and newly_made:
@ -280,7 +346,7 @@ class Status(PolymorphicModel):
def __str__(self):
return '%s: %s' % (
self.id,
self.content,
self.content_source,
)
@classmethod
@ -350,20 +416,8 @@ class Status(PolymorphicModel):
# HTML and one is plain text. But the docs don't
# seem to be forthcoming on this point, so we'll
# just have to wait until we find out.
return self.content
return self.content_source
@property
def is_local(self):
return self.remote_url is None
@property
def content_as_html(self):
if not self.content:
return '<p></p>'
return markdown.markdown(self.content)
@property
def spoiler_text_as_html(self):
if not self.spoiler_text:
return '<p></p>'
return markdown.markdown(self.spoiler_text)

Wyświetl plik

@ -208,26 +208,24 @@ class StatusSerializer(serializers.ModelSerializer):
# "content" is read-only for HTML;
# "status" is write-only for text (or Markdown)
content = serializers.SerializerMethodField(
content = serializers.CharField(
source='content_as_html',
read_only = True)
status = serializers.CharField(
source='source_text',
source='content_source',
write_only = True)
def get_content(self, status):
result = markdown.markdown(status.content)
return result
created_at = serializers.DateTimeField(
required = False,
read_only = True)
# TODO Media
# TODO Media
sensitive = serializers.BooleanField(
required = False)
spoiler_text = serializers.CharField(
source='spoiler_source',
allow_blank = True,
required = False)

Wyświetl plik

@ -1,9 +1,18 @@
# trilby_api/tests/__init__.py
#
# Part of kepi.
# Copyright (c) 2018-2021 Marnanel Thurman.
# Licensed under the GNU Public License v2.
from django.test import TestCase, Client
from rest_framework.test import force_authenticate, APIClient
from kepi.trilby_api.models import *
from django.conf import settings
import json
import logging
logger = logging.getLogger(name='kepi')
ACCOUNT_EXPECTED = {
'username': 'alice',
'acct': 'alice',
@ -154,7 +163,7 @@ def create_local_status(
result = Status(
remote_url = None,
account = posted_by,
content = content,
content_source = content,
**kwargs,
)

Wyświetl plik

@ -23,7 +23,7 @@ class TestReblog(TestCase):
)
reblog = create_local_status(
content = original.content,
content = original.content_source,
posted_by = bob,
reblog_of = original,
)
@ -48,7 +48,7 @@ class TestReblog(TestCase):
)
reblog = create_local_status(
content = original.content,
content = original.content_source,
posted_by = bob,
reblog_of = original,
)
@ -87,7 +87,7 @@ class TestReblog(TestCase):
for i in range(1, 20):
reblog = create_local_status(
content = original.content,
content = original.content_source,
posted_by = bob,
reblog_of = original,
)

Wyświetl plik

@ -685,7 +685,7 @@ class TestGetStatus(TrilbyTestCase):
remote_url = "https://example.org/people/bob/status/100",
account = self._bob,
in_reply_to = self._alice_status,
content = "Buttercups our gold.",
content_source = "Buttercups our gold.",
)
self._bob_status.save()

Wyświetl plik

@ -24,7 +24,7 @@ class TimelineTestCase(TrilbyTestCase):
def add_status(self, source, visibility, content):
status = Status(
account = source,
content = content,
content_source = content,
visibility = visibility,
)
status.save()
@ -39,12 +39,22 @@ class TimelineTestCase(TrilbyTestCase):
as_user = None,
):
details = sorted([x['content'] \
for x in self.get(
logger.info("Timeline contents for %s as %s...",
path,
as_user)
found = self.get(
path = path,
data = data,
as_user = as_user,
)])
)
logger.info(" -- retrieved")
details = sorted([x['content'] for x in found])
logger.debug(" -- sorted as %s",
details)
result = ''
for detail in details:
@ -54,10 +64,7 @@ class TimelineTestCase(TrilbyTestCase):
result += detail
logger.info("Timeline contents for %s as %s...",
path,
as_user)
logger.info(" ...are %s",
logger.info(" -- contents are %s",
result)
return result
@ -449,6 +456,49 @@ class TestHomeTimeline(TimelineTestCase):
msg = 'default is 20',
)
def temp_general_test_limit(self, count):
# XXX temp
self.alice = create_local_person("alice")
self.bob = create_local_person("bob")
self.carol = create_local_person("carol")
Follow(
follower=self.alice,
following=self.bob,
offer=None).save()
for i in range(100):
self.add_status(
source=self.bob,
content=str(i),
visibility='A',
)
self.add_status(
source=self.carol,
content=str(i),
visibility='A',
)
LOOP_COUNT = 50
for j in range(LOOP_COUNT):
logger.info("----------- Loop: %d of %d", j, LOOP_COUNT)
for i in [count]:
self.assertIsNotNone(
self.timeline_contents(
path = '/api/v1/timelines/home',
data = {'limit': i},
as_user = self.alice,
),
)
def xxx_test_1(self):
self.temp_general_test_limit(1)
def xxx_test_100(self):
self.temp_general_test_limit(100)
@httpretty.activate()
def test_local(self):

Wyświetl plik

@ -135,18 +135,18 @@ class Reblog(DoSomethingWithStatus):
# https://github.com/tootsuite/mastodon/issues/13479
# For now, I'm assuming that you can.
content = 'RT {}'.format(the_status.content)
content_source = 'RT {}'.format(the_status.content_source)
new_status = trilby_models.Status(
# Fields which are different in a reblog:
account = request.user.localperson,
content = content,
content_source = content_source,
reblog_of = the_status,
# Fields which are just copied in:
sensitive = the_status.sensitive,
spoiler_text = the_status.spoiler_text,
spoiler_source = the_status.spoiler_source,
visibility = the_status.visibility,
language = the_status.language,
in_reply_to = the_status.in_reply_to,
@ -397,9 +397,9 @@ class Statuses(generics.ListCreateAPIView,
status = trilby_models.Status(
account = request.user.localperson,
content = data.get('status', ''),
content_source = data.get('status', ''),
sensitive = data.get('sensitive', False),
spoiler_text = data.get('spoiler_text', ''),
spoiler_source = data.get('spoiler_text', ''),
visibility = data.get('visibility', 'public'),
language = data.get('language',
settings.KEPI['LANGUAGES'][0]),