Merge pull request #402 from mouse-reeve/alt-text

Federate cover alt text
This commit is contained in:
Mouse Reeve 2020-12-17 13:22:09 -08:00 committed by GitHub
commit d109ac0626
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 78 additions and 57 deletions

View file

@ -52,12 +52,29 @@ class Book(ActivitypubMixin, BookWyrmModel):
authors = fields.ManyToManyField('Author') authors = fields.ManyToManyField('Author')
# preformatted authorship string for search and easier display # preformatted authorship string for search and easier display
author_text = models.CharField(max_length=255, blank=True, null=True) author_text = models.CharField(max_length=255, blank=True, null=True)
cover = fields.ImageField(upload_to='covers/', blank=True, null=True) cover = fields.ImageField(
upload_to='covers/', blank=True, null=True, alt_field='alt_text')
first_published_date = fields.DateTimeField(blank=True, null=True) first_published_date = fields.DateTimeField(blank=True, null=True)
published_date = fields.DateTimeField(blank=True, null=True) published_date = fields.DateTimeField(blank=True, null=True)
objects = InheritanceManager() objects = InheritanceManager()
@property
def edition_info(self):
''' properties of this edition, as a string '''
items = [
self.physical_format,
self.languages[0] + ' language' if self.languages and \
self.languages[0] != 'English' else None,
str(self.published_date.year) if self.published_date else None,
]
return ', '.join(i for i in items if i)
@property
def alt_text(self):
''' image alt test '''
return '%s cover (%s)' % (self.title, self.edition_info)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
''' can't be abstract for query reasons, but you shouldn't USE it ''' ''' can't be abstract for query reasons, but you shouldn't USE it '''
if not isinstance(self, Edition) and not isinstance(self, Work): if not isinstance(self, Edition) and not isinstance(self, Work):

View file

@ -305,18 +305,22 @@ class TagField(ManyToManyField):
return items return items
def image_serializer(value): def image_serializer(value, alt):
''' helper for serializing images ''' ''' helper for serializing images '''
if value and hasattr(value, 'url'): if value and hasattr(value, 'url'):
url = value.url url = value.url
else: else:
return None return None
url = 'https://%s%s' % (DOMAIN, url) url = 'https://%s%s' % (DOMAIN, url)
return activitypub.Image(url=url) return activitypub.Image(url=url, name=alt)
class ImageField(ActivitypubFieldMixin, models.ImageField): class ImageField(ActivitypubFieldMixin, models.ImageField):
''' activitypub-aware image field ''' ''' activitypub-aware image field '''
def __init__(self, *args, alt_field=None, **kwargs):
self.alt_field = alt_field
super().__init__(*args, **kwargs)
# pylint: disable=arguments-differ # pylint: disable=arguments-differ
def set_field_from_activity(self, instance, data, save=True): def set_field_from_activity(self, instance, data, save=True):
''' helper function for assinging a value to the field ''' ''' helper function for assinging a value to the field '''
@ -326,9 +330,19 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
return return
getattr(instance, self.name).save(*formatted, save=save) getattr(instance, self.name).save(*formatted, save=save)
def set_activity_from_field(self, activity, instance):
value = getattr(instance, self.name)
if value is None:
return
alt_text = getattr(instance, self.alt_field)
formatted = self.field_to_activity(value, alt_text)
def field_to_activity(self, value): key = self.get_activitypub_field()
return image_serializer(value) activity[key] = formatted
def field_to_activity(self, value, alt=None):
return image_serializer(value, alt)
def field_from_activity(self, value): def field_from_activity(self, value):

View file

@ -86,11 +86,11 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
activity['name'] = self.pure_name activity['name'] = self.pure_name
activity['type'] = self.pure_type activity['type'] = self.pure_type
activity['attachment'] = [ activity['attachment'] = [
image_serializer(b.cover) for b in self.mention_books.all() \ image_serializer(b.cover, b.alt_text) \
if b.cover] for b in self.mention_books.all()[:4] if b.cover]
if hasattr(self, 'book'): if hasattr(self, 'book'):
activity['attachment'].append( activity['attachment'].append(
image_serializer(self.book.cover) image_serializer(self.book.cover, self.book.alt_text)
) )
return activity return activity

View file

@ -53,7 +53,8 @@ class User(OrderedCollectionPageMixin, AbstractUser):
# name is your display name, which you can change at will # name is your display name, which you can change at will
name = fields.CharField(max_length=100, default='') name = fields.CharField(max_length=100, default='')
avatar = fields.ImageField( avatar = fields.ImageField(
upload_to='avatars/', blank=True, null=True, activitypub_field='icon') upload_to='avatars/', blank=True, null=True,
activitypub_field='icon', alt_field='alt_text')
followers = fields.ManyToManyField( followers = fields.ManyToManyField(
'self', 'self',
link_only=True, link_only=True,
@ -90,6 +91,11 @@ class User(OrderedCollectionPageMixin, AbstractUser):
last_active_date = models.DateTimeField(auto_now=True) last_active_date = models.DateTimeField(auto_now=True)
manually_approves_followers = fields.BooleanField(default=False) manually_approves_followers = fields.BooleanField(default=False)
@property
def alt_text(self):
''' alt text with username '''
return 'avatar for %s' % (self.localname or self.username)
@property @property
def display_name(self): def display_name(self):
''' show the cleanest version of the user's name possible ''' ''' show the cleanest version of the user's name possible '''

View file

@ -1,3 +1,3 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
<img class="avatar image {% if large %}is-96x96{% else %}is-32x32{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}" alt="avatar for {{ user|username }}"> <img class="avatar image {% if large %}is-96x96{% else %}is-32x32{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}" alt="{{ user.alt_text }}">

View file

@ -1,13 +1,13 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
<div class="cover-container is-{{ size }}"> <div class="cover-container is-{{ size }}">
{% if book.cover %} {% if book.cover %}
<img class="book-cover" src="/images/{{ book.cover }}" alt="{% include 'snippets/cover_alt.html' with book=book %}"> <img class="book-cover" src="/images/{{ book.cover }}" alt="{{ book.alt_text }}">
{% else %} {% else %}
<div class="no-cover book-cover"> <div class="no-cover book-cover">
<img class="book-cover" src="/static/images/no_cover.jpg" alt="No cover"> <img class="book-cover" src="/static/images/no_cover.jpg" alt="No cover">
<div> <div>
<p>{{ book.title }}</p> <p>{{ book.title }}</p>
<p>({{ book|edition_info }})</p> <p>({{ book.edition_info }})</p>
</div> </div>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,2 +0,0 @@
{% load bookwyrm_tags %}
'{{ book.title }}' Cover ({{ book|edition_info }})

View file

@ -97,20 +97,6 @@ def get_boosted(boost):
).get() ).get()
@register.filter(name='edition_info')
def get_edition_info(book):
''' paperback, French language, 1982 '''
if not book:
return ''
items = [
book.physical_format if isinstance(book, models.Edition) else None,
book.languages[0] + ' language' if book.languages and \
book.languages[0] != 'English' else None,
str(book.published_date.year) if book.published_date else None,
]
return ', '.join(i for i in items if i)
@register.filter(name='book_description') @register.filter(name='book_description')
def get_book_description(book): def get_book_description(book):
''' use the work's text if the book doesn't have it ''' ''' use the work's text if the book doesn't have it '''

View file

@ -1,5 +1,7 @@
''' testing models ''' ''' testing models '''
from dateutil.parser import parse
from django.test import TestCase from django.test import TestCase
from django.utils import timezone
from bookwyrm import models, settings from bookwyrm import models, settings
from bookwyrm.models.book import isbn_10_to_13, isbn_13_to_10 from bookwyrm.models.book import isbn_10_to_13, isbn_13_to_10
@ -56,3 +58,27 @@ class Book(TestCase):
isbn_13 = '978-1788-16167-1' isbn_13 = '978-1788-16167-1'
isbn_10 = isbn_13_to_10(isbn_13) isbn_10 = isbn_13_to_10(isbn_13)
self.assertEqual(isbn_10, '178816167X') self.assertEqual(isbn_10, '178816167X')
def test_get_edition_info(self):
''' text slug about an edition '''
book = models.Edition.objects.create(title='Test Edition')
self.assertEqual(book.edition_info, '')
book.physical_format = 'worm'
book.save()
self.assertEqual(book.edition_info, 'worm')
book.languages = ['English']
book.save()
self.assertEqual(book.edition_info, 'worm')
book.languages = ['Glorbish', 'English']
book.save()
self.assertEqual(book.edition_info, 'worm, Glorbish language')
book.published_date = timezone.make_aware(parse('2020'))
book.save()
self.assertEqual(book.edition_info, 'worm, Glorbish language, 2020')
self.assertEqual(
book.alt_text, 'Test Edition cover (worm, Glorbish language, 2020)')

View file

@ -393,17 +393,19 @@ class ActivitypubFields(TestCase):
ContentFile(output.getvalue()) ContentFile(output.getvalue())
) )
output = fields.image_serializer(user.avatar) output = fields.image_serializer(user.avatar, alt='alt text')
self.assertIsNotNone( self.assertIsNotNone(
re.match( re.match(
r'.*\.jpg', r'.*\.jpg',
output.url, output.url,
) )
) )
self.assertEqual(output.name, 'alt text')
self.assertEqual(output.type, 'Image') self.assertEqual(output.type, 'Image')
instance = fields.ImageField() instance = fields.ImageField()
output = fields.image_serializer(user.avatar, alt=None)
self.assertEqual(instance.field_to_activity(user.avatar), output) self.assertEqual(instance.field_to_activity(user.avatar), output)
responses.add( responses.add(

View file

@ -158,34 +158,6 @@ class TemplateTags(TestCase):
self.assertEqual(boosted, status) self.assertEqual(boosted, status)
def test_get_edition_info(self):
''' text slug about an edition '''
self.assertEqual(
bookwyrm_tags.get_edition_info(self.book), '')
self.book.physical_format = 'worm'
self.book.save()
self.assertEqual(
bookwyrm_tags.get_edition_info(self.book), 'worm')
self.book.languages = ['English']
self.book.save()
self.assertEqual(
bookwyrm_tags.get_edition_info(self.book), 'worm')
self.book.languages = ['Glorbish', 'English']
self.book.save()
self.assertEqual(
bookwyrm_tags.get_edition_info(self.book),
'worm, Glorbish language')
self.book.published_date = timezone.make_aware(parse('2020'))
self.book.save()
self.assertEqual(
bookwyrm_tags.get_edition_info(self.book),
'worm, Glorbish language, 2020')
def test_get_book_description(self): def test_get_book_description(self):
''' grab it from the edition or the parent ''' ''' grab it from the edition or the parent '''
work = models.Work.objects.create(title='Test Work') work = models.Work.objects.create(title='Test Work')