mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-04 15:26:48 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
fd0e4c6e13
22 changed files with 188 additions and 130 deletions
|
@ -201,6 +201,19 @@ def add_status_on_create(sender, instance, created, *args, **kwargs):
|
|||
for stream in streams.values():
|
||||
stream.add_status(instance)
|
||||
|
||||
if sender != models.Boost:
|
||||
return
|
||||
# remove the original post and other, earlier boosts
|
||||
boosted = instance.boost.boosted_status
|
||||
old_versions = models.Boost.objects.filter(
|
||||
boosted_status__id=boosted.id,
|
||||
created_date__lt=instance.created_date,
|
||||
)
|
||||
for stream in streams.values():
|
||||
stream.remove_object_from_related_stores(boosted)
|
||||
for status in old_versions:
|
||||
stream.remove_object_from_related_stores(status)
|
||||
|
||||
|
||||
@receiver(signals.post_delete, sender=models.Boost)
|
||||
# pylint: disable=unused-argument
|
||||
|
@ -208,7 +221,10 @@ def remove_boost_on_delete(sender, instance, *args, **kwargs):
|
|||
"""boosts are deleted"""
|
||||
# we're only interested in new statuses
|
||||
for stream in streams.values():
|
||||
# remove the boost
|
||||
stream.remove_object_from_related_stores(instance)
|
||||
# re-add the original status
|
||||
stream.add_status(instance.boosted_status)
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=models.UserFollows)
|
||||
|
|
|
@ -37,7 +37,7 @@ class AbstractMinimalConnector(ABC):
|
|||
for field in self_fields:
|
||||
setattr(self, field, getattr(info, field))
|
||||
|
||||
def search(self, query, min_confidence=None):
|
||||
def search(self, query, min_confidence=None, timeout=5):
|
||||
"""free text search"""
|
||||
params = {}
|
||||
if min_confidence:
|
||||
|
@ -46,6 +46,7 @@ class AbstractMinimalConnector(ABC):
|
|||
data = self.get_search_data(
|
||||
"%s%s" % (self.search_url, query),
|
||||
params=params,
|
||||
timeout=timeout,
|
||||
)
|
||||
results = []
|
||||
|
||||
|
@ -218,7 +219,7 @@ def dict_from_mappings(data, mappings):
|
|||
return result
|
||||
|
||||
|
||||
def get_data(url, params=None):
|
||||
def get_data(url, params=None, timeout=10):
|
||||
"""wrapper for request.get"""
|
||||
# check if the url is blocked
|
||||
if models.FederatedServer.is_blocked(url):
|
||||
|
@ -234,6 +235,7 @@ def get_data(url, params=None):
|
|||
"Accept": "application/json; charset=utf-8",
|
||||
"User-Agent": settings.USER_AGENT,
|
||||
},
|
||||
timeout=timeout,
|
||||
)
|
||||
except (RequestError, SSLError, ConnectionError) as e:
|
||||
logger.exception(e)
|
||||
|
@ -250,7 +252,7 @@ def get_data(url, params=None):
|
|||
return data
|
||||
|
||||
|
||||
def get_image(url):
|
||||
def get_image(url, timeout=10):
|
||||
"""wrapper for requesting an image"""
|
||||
try:
|
||||
resp = requests.get(
|
||||
|
@ -258,6 +260,7 @@ def get_image(url):
|
|||
headers={
|
||||
"User-Agent": settings.USER_AGENT,
|
||||
},
|
||||
timeout=timeout,
|
||||
)
|
||||
except (RequestError, SSLError) as e:
|
||||
logger.exception(e)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" interface with whatever connectors the app has """
|
||||
from datetime import datetime
|
||||
import importlib
|
||||
import logging
|
||||
import re
|
||||
|
@ -29,9 +30,11 @@ def search(query, min_confidence=0.1, return_first=False):
|
|||
isbn = re.sub(r"[\W_]", "", query)
|
||||
maybe_isbn = len(isbn) in [10, 13] # ISBN10 or ISBN13
|
||||
|
||||
timeout = 15
|
||||
start_time = datetime.now()
|
||||
for connector in get_connectors():
|
||||
result_set = None
|
||||
if maybe_isbn and connector.isbn_search_url and connector.isbn_search_url == "":
|
||||
if maybe_isbn and connector.isbn_search_url and connector.isbn_search_url != "":
|
||||
# Search on ISBN
|
||||
try:
|
||||
result_set = connector.isbn_search(isbn)
|
||||
|
@ -59,6 +62,8 @@ def search(query, min_confidence=0.1, return_first=False):
|
|||
"results": result_set,
|
||||
}
|
||||
)
|
||||
if (datetime.now() - start_time).seconds >= timeout:
|
||||
break
|
||||
|
||||
if return_first:
|
||||
return None
|
||||
|
|
|
@ -3,7 +3,7 @@ from functools import reduce
|
|||
import operator
|
||||
|
||||
from django.contrib.postgres.search import SearchRank, SearchVector
|
||||
from django.db.models import Count, OuterRef, Subquery, F, Q
|
||||
from django.db.models import OuterRef, Subquery, F, Q
|
||||
|
||||
from bookwyrm import models
|
||||
from .abstract_connector import AbstractConnector, SearchResult
|
||||
|
@ -122,6 +122,8 @@ def search_identifiers(query, *filters):
|
|||
results = models.Edition.objects.filter(
|
||||
*filters, reduce(operator.or_, (Q(**f) for f in or_filters))
|
||||
).distinct()
|
||||
if results.count() <= 1:
|
||||
return results
|
||||
|
||||
# when there are multiple editions of the same work, pick the default.
|
||||
# it would be odd for this to happen.
|
||||
|
@ -146,19 +148,15 @@ def search_title_author(query, min_confidence, *filters):
|
|||
)
|
||||
|
||||
results = (
|
||||
models.Edition.objects.annotate(search=vector)
|
||||
.annotate(rank=SearchRank(vector, query))
|
||||
models.Edition.objects.annotate(rank=SearchRank(vector, query))
|
||||
.filter(*filters, rank__gt=min_confidence)
|
||||
.order_by("-rank")
|
||||
)
|
||||
|
||||
# when there are multiple editions of the same work, pick the closest
|
||||
editions_of_work = (
|
||||
results.values("parent_work")
|
||||
.annotate(Count("parent_work"))
|
||||
.values_list("parent_work")
|
||||
)
|
||||
editions_of_work = results.values("parent_work__id").values_list("parent_work__id")
|
||||
|
||||
# filter out multiple editions of the same work
|
||||
for work_id in set(editions_of_work):
|
||||
editions = results.filter(parent_work=work_id)
|
||||
default = editions.order_by("-edition_rank").first()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
""" activitypub-aware django model fields """
|
||||
from dataclasses import MISSING
|
||||
import imghdr
|
||||
import re
|
||||
from uuid import uuid4
|
||||
|
||||
|
@ -9,7 +10,7 @@ from django.contrib.postgres.fields import ArrayField as DjangoArrayField
|
|||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import models
|
||||
from django.forms import ClearableFileInput, ImageField
|
||||
from django.forms import ClearableFileInput, ImageField as DjangoImageField
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from bookwyrm import activitypub
|
||||
|
@ -334,10 +335,14 @@ class TagField(ManyToManyField):
|
|||
|
||||
|
||||
class ClearableFileInputWithWarning(ClearableFileInput):
|
||||
"""max file size warning"""
|
||||
|
||||
template_name = "widgets/clearable_file_input_with_warning.html"
|
||||
|
||||
|
||||
class CustomImageField(ImageField):
|
||||
class CustomImageField(DjangoImageField):
|
||||
"""overwrites image field for form"""
|
||||
|
||||
widget = ClearableFileInputWithWarning
|
||||
|
||||
|
||||
|
@ -400,11 +405,12 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
|
|||
if not response:
|
||||
return None
|
||||
|
||||
image_name = str(uuid4()) + "." + url.split(".")[-1]
|
||||
image_content = ContentFile(response.content)
|
||||
image_name = str(uuid4()) + "." + imghdr.what(None, image_content.read())
|
||||
return [image_name, image_content]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
"""special case for forms"""
|
||||
return super().formfield(
|
||||
**{
|
||||
"form_class": CustomImageField,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<div class="column is-narrow">
|
||||
<a href="{{ author.local_path }}/edit">
|
||||
<span class="icon icon-pencil" title="{% trans 'Edit Author' %}" aria-hidden="True"></span>
|
||||
<span>{% trans "Edit Author" %}</span>
|
||||
<span class="is-hidden-mobile">{% trans "Edit Author" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -8,27 +8,36 @@
|
|||
<div class="block" itemscope itemtype="https://schema.org/Book">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column">
|
||||
<h1 class="title">
|
||||
<span itemprop="name">
|
||||
{{ book.title }}{% if book.subtitle %}:
|
||||
<small>{{ book.subtitle }}</small>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
{% if book.series %}
|
||||
<meta itemprop="isPartOf" content="{{ book.series }}">
|
||||
<meta itemprop="volumeNumber" content="{{ book.series_number }}">
|
||||
|
||||
<small class="has-text-grey-dark">
|
||||
({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %})
|
||||
</small>
|
||||
<br>
|
||||
{% endif %}
|
||||
<h1 class="title" itemprop="name">
|
||||
{{ book.title }}
|
||||
</h1>
|
||||
|
||||
{% if book.subtitle or book.series %}
|
||||
<p class="subtitle title is-5">
|
||||
{% if book.subtitle %}
|
||||
<meta
|
||||
itemprop="alternativeHeadline"
|
||||
content="{{ book.subtitle | escape }}"
|
||||
>
|
||||
|
||||
<span class="has-text-weight-bold">
|
||||
{{ book.subtitle }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if book.series %}
|
||||
<meta itemprop="isPartOf" content="{{ book.series | escape }}">
|
||||
<meta itemprop="volumeNumber" content="{{ book.series_number }}">
|
||||
|
||||
({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %})
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if book.authors %}
|
||||
<h2 class="subtitle">
|
||||
{% trans "by" %} {% include 'snippets/authors.html' with book=book %}
|
||||
</h2>
|
||||
<div class="subtitle">
|
||||
{% trans "by" %} {% include 'snippets/authors.html' with book=book %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -36,7 +45,7 @@
|
|||
<div class="column is-narrow">
|
||||
<a href="{{ book.id }}/edit">
|
||||
<span class="icon icon-pencil" title="{% trans "Edit Book" %}" aria-hidden=True></span>
|
||||
<span>{% trans "Edit Book" %}</span>
|
||||
<span class="is-hidden-mobile">{% trans "Edit Book" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -84,7 +93,7 @@
|
|||
|
||||
<div class="column is-three-fifths">
|
||||
<div class="block">
|
||||
<h3
|
||||
<div
|
||||
class="field is-grouped"
|
||||
itemprop="aggregateRating"
|
||||
itemscope
|
||||
|
@ -102,7 +111,7 @@
|
|||
{% plural %}
|
||||
({{ review_count }} reviews)
|
||||
{% endblocktrans %}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{% with full=book|book_description itemprop='abstract' %}
|
||||
{% include 'snippets/trimmed_text.html' %}
|
||||
|
@ -180,7 +189,7 @@
|
|||
<p>{% trans "You don't have any reading activity for this book." %}</p>
|
||||
{% endif %}
|
||||
{% for readthrough in readthroughs %}
|
||||
{% include 'snippets/readthrough.html' with readthrough=readthrough %}
|
||||
{% include 'book/readthrough.html' with readthrough=readthrough %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
<hr aria-hidden="true">
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
|
||||
<div class="column is-narrow">
|
||||
{% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %}
|
||||
{% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True right=True %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load tz %}
|
||||
<div class="content box is-shadowless has-background-white-bis">
|
||||
<div id="hide-edit-readthrough-{{ readthrough.id }}">
|
||||
<div class="content">
|
||||
<div id="hide-edit-readthrough-{{ readthrough.id }}" class="box is-shadowless has-background-white-bis">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
{% trans "Progress Updates:" %}
|
|
@ -20,8 +20,8 @@
|
|||
{% csrf_token %}
|
||||
<button class="button is-primary" type="submit">Join Directory</button>
|
||||
<p class="help">
|
||||
{% url 'settings-profile' as path %}
|
||||
{% blocktrans %}You can opt-out at any time in your <a href="{{ path }}">profile settings.</a>{% endblocktrans %}
|
||||
{% url 'prefs-profile' as path %}
|
||||
{% blocktrans with path=path %}You can opt-out at any time in your <a href="{{ path }}">profile settings.</a>{% endblocktrans %}
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<span class="icon icon-warning"></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="column is-clipped">
|
||||
<div class="block">
|
||||
<p>
|
||||
{# DESCRIPTION #}
|
||||
|
@ -137,7 +137,7 @@
|
|||
{# PREVIEW #}
|
||||
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-white{% if notification.notification_type == 'REPLY' or notification.notification_type == 'MENTION' %} has-text-black{% else %}-bis has-text-grey-dark{% endif %}{% endif %}">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="column is-clipped">
|
||||
{% include 'snippets/status_preview.html' with status=related_status %}
|
||||
</div>
|
||||
<div class="column is-narrow {% if notification.notification_type == 'REPLY' or notification.notification_type == 'MENTION' %}has-text-black{% else %}has-text-grey-dark{% endif %}">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% block edit-button %}
|
||||
<a href="{% url 'settings-import-blocklist' %}">
|
||||
<span class="icon icon-plus" title="{% trans 'Add instance' %}" aria-hidden="True"></span>
|
||||
<span>{% trans "Add instance" %}</span>
|
||||
<span class="is-hidden-mobile">{% trans "Add instance" %}</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
class="is-sr-only"
|
||||
type="radio"
|
||||
name="rating"
|
||||
value="0"
|
||||
value=""
|
||||
{% if default_rating == 0 or not default_rating %}checked{% endif %}
|
||||
>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div class="column is-narrow">
|
||||
<a href="{% url 'prefs-profile' %}">
|
||||
<span class="icon icon-pencil" title="Edit profile" aria-hidden="true"></span>
|
||||
<span>{% trans "Edit profile" %}</span>
|
||||
<span class="is-hidden-mobile">{% trans "Edit profile" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -26,7 +26,7 @@
|
|||
<h2 class="title">
|
||||
{% include 'user/shelf/books_header.html' %}
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<div class="columns is-mobile scroll-x">
|
||||
{% for shelf in shelves %}
|
||||
<div class="column is-narrow">
|
||||
<h3>{{ shelf.name }}
|
||||
|
@ -60,7 +60,7 @@
|
|||
<div class="column is-narrow">
|
||||
<a target="_blank" href="{{ user.local_path }}/rss">
|
||||
<span class="icon icon-rss" aria-hidden="true"></span>
|
||||
<span>{% trans "RSS feed" %}</span>
|
||||
<span class="is-hidden-mobile">{% trans "RSS feed" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import os
|
||||
from uuid import uuid4
|
||||
from django import template
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
@ -20,13 +21,16 @@ def get_user_identifier(user):
|
|||
|
||||
|
||||
@register.filter(name="book_title")
|
||||
def get_title(book):
|
||||
def get_title(book, too_short=5):
|
||||
"""display the subtitle if the title is short"""
|
||||
if not book:
|
||||
return ""
|
||||
title = book.title
|
||||
if len(title) < 6 and book.subtitle:
|
||||
title = "{:s}: {:s}".format(title, book.subtitle)
|
||||
if len(title) <= too_short and book.subtitle:
|
||||
title = _("%(title)s: %(subtitle)s") % {
|
||||
"title": title,
|
||||
"subtitle": book.subtitle,
|
||||
}
|
||||
return title
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ from bookwyrm import activitypub, models, settings
|
|||
# pylint: disable=too-many-public-methods
|
||||
@patch("bookwyrm.models.Status.broadcast")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
|
||||
class Status(TestCase):
|
||||
"""lotta types of statuses"""
|
||||
|
||||
|
@ -384,7 +385,8 @@ class Status(TestCase):
|
|||
user=self.local_user, notification_type="GLORB"
|
||||
)
|
||||
|
||||
def test_create_broadcast(self, _, broadcast_mock):
|
||||
# pylint: disable=unused-argument
|
||||
def test_create_broadcast(self, one, two, broadcast_mock, *_):
|
||||
"""should send out two verions of a status on create"""
|
||||
models.Comment.objects.create(
|
||||
content="hi", user=self.local_user, book=self.book
|
||||
|
|
|
@ -16,6 +16,7 @@ from bookwyrm.templatetags import (
|
|||
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
|
||||
class TemplateTags(TestCase):
|
||||
"""lotta different things here"""
|
||||
|
||||
|
@ -38,28 +39,28 @@ class TemplateTags(TestCase):
|
|||
)
|
||||
self.book = models.Edition.objects.create(title="Test Book")
|
||||
|
||||
def test_get_user_rating(self, _):
|
||||
def test_get_user_rating(self, *_):
|
||||
"""get a user's most recent rating of a book"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
models.Review.objects.create(user=self.user, book=self.book, rating=3)
|
||||
self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 3)
|
||||
|
||||
def test_get_user_rating_doesnt_exist(self, _):
|
||||
def test_get_user_rating_doesnt_exist(self, *_):
|
||||
"""there is no rating available"""
|
||||
self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 0)
|
||||
|
||||
def test_get_user_identifer_local(self, _):
|
||||
def test_get_user_identifer_local(self, *_):
|
||||
"""fall back to the simplest uid available"""
|
||||
self.assertNotEqual(self.user.username, self.user.localname)
|
||||
self.assertEqual(utilities.get_user_identifier(self.user), "mouse")
|
||||
|
||||
def test_get_user_identifer_remote(self, _):
|
||||
def test_get_user_identifer_remote(self, *_):
|
||||
"""for a remote user, should be their full username"""
|
||||
self.assertEqual(
|
||||
utilities.get_user_identifier(self.remote_user), "rat@example.com"
|
||||
)
|
||||
|
||||
def test_get_replies(self, _):
|
||||
def test_get_replies(self, *_):
|
||||
"""direct replies to a status"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
parent = models.Review.objects.create(
|
||||
|
@ -87,7 +88,7 @@ class TemplateTags(TestCase):
|
|||
self.assertTrue(second_child in replies)
|
||||
self.assertFalse(third_child in replies)
|
||||
|
||||
def test_get_parent(self, _):
|
||||
def test_get_parent(self, *_):
|
||||
"""get the reply parent of a status"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
parent = models.Review.objects.create(
|
||||
|
@ -101,7 +102,7 @@ class TemplateTags(TestCase):
|
|||
self.assertEqual(result, parent)
|
||||
self.assertIsInstance(result, models.Review)
|
||||
|
||||
def test_get_user_liked(self, _):
|
||||
def test_get_user_liked(self, *_):
|
||||
"""did a user like a status"""
|
||||
status = models.Review.objects.create(user=self.remote_user, book=self.book)
|
||||
|
||||
|
@ -110,7 +111,7 @@ class TemplateTags(TestCase):
|
|||
models.Favorite.objects.create(user=self.user, status=status)
|
||||
self.assertTrue(interaction.get_user_liked(self.user, status))
|
||||
|
||||
def test_get_user_boosted(self, _):
|
||||
def test_get_user_boosted(self, *_):
|
||||
"""did a user boost a status"""
|
||||
status = models.Review.objects.create(user=self.remote_user, book=self.book)
|
||||
|
||||
|
@ -119,7 +120,7 @@ class TemplateTags(TestCase):
|
|||
models.Boost.objects.create(user=self.user, boosted_status=status)
|
||||
self.assertTrue(interaction.get_user_boosted(self.user, status))
|
||||
|
||||
def test_get_boosted(self, _):
|
||||
def test_get_boosted(self, *_):
|
||||
"""load a boosted status"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
status = models.Review.objects.create(user=self.remote_user, book=self.book)
|
||||
|
@ -128,7 +129,7 @@ class TemplateTags(TestCase):
|
|||
self.assertIsInstance(boosted, models.Review)
|
||||
self.assertEqual(boosted, status)
|
||||
|
||||
def test_get_book_description(self, _):
|
||||
def test_get_book_description(self, *_):
|
||||
"""grab it from the edition or the parent"""
|
||||
work = models.Work.objects.create(title="Test Work")
|
||||
self.book.parent_work = work
|
||||
|
@ -144,12 +145,12 @@ class TemplateTags(TestCase):
|
|||
self.book.save()
|
||||
self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hello")
|
||||
|
||||
def test_get_uuid(self, _):
|
||||
def test_get_uuid(self, *_):
|
||||
"""uuid functionality"""
|
||||
uuid = utilities.get_uuid("hi")
|
||||
self.assertTrue(re.match(r"hi[A-Za-z0-9\-]", uuid))
|
||||
|
||||
def test_get_markdown(self, _):
|
||||
def test_get_markdown(self, *_):
|
||||
"""mardown format data"""
|
||||
result = markdown.get_markdown("_hi_")
|
||||
self.assertEqual(result, "<p><em>hi</em></p>")
|
||||
|
@ -157,13 +158,13 @@ class TemplateTags(TestCase):
|
|||
result = markdown.get_markdown("<marquee>_hi_</marquee>")
|
||||
self.assertEqual(result, "<p><em>hi</em></p>")
|
||||
|
||||
def test_get_mentions(self, _):
|
||||
def test_get_mentions(self, *_):
|
||||
"""list of people mentioned"""
|
||||
status = models.Status.objects.create(content="hi", user=self.remote_user)
|
||||
result = status_display.get_mentions(status, self.user)
|
||||
self.assertEqual(result, "@rat@example.com ")
|
||||
|
||||
def test_related_status(self, _):
|
||||
def test_related_status(self, *_):
|
||||
"""gets the subclass model for a notification status"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
status = models.Status.objects.create(content="hi", user=self.user)
|
||||
|
|
|
@ -51,7 +51,8 @@ class InboxActivities(TestCase):
|
|||
models.SiteSettings.objects.create()
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_boost(self, redis_mock):
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
|
||||
def test_boost(self, redis_mock, _):
|
||||
"""boost a status"""
|
||||
self.assertEqual(models.Notification.objects.count(), 0)
|
||||
activity = {
|
||||
|
@ -81,7 +82,8 @@ class InboxActivities(TestCase):
|
|||
|
||||
@responses.activate
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_boost_remote_status(self, redis_mock):
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
|
||||
def test_boost_remote_status(self, redis_mock, _):
|
||||
"""boost a status from a remote server"""
|
||||
work = models.Work.objects.create(title="work title")
|
||||
book = models.Edition.objects.create(
|
||||
|
@ -153,12 +155,13 @@ class InboxActivities(TestCase):
|
|||
views.inbox.activity_task(activity)
|
||||
self.assertEqual(models.Boost.objects.count(), 0)
|
||||
|
||||
def test_unboost(self):
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
|
||||
def test_unboost(self, *_):
|
||||
"""undo a boost"""
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
boost = models.Boost.objects.create(
|
||||
boosted_status=self.status, user=self.remote_user
|
||||
)
|
||||
boost = models.Boost.objects.create(
|
||||
boosted_status=self.status, user=self.remote_user
|
||||
)
|
||||
activity = {
|
||||
"type": "Undo",
|
||||
"actor": "hi",
|
||||
|
@ -175,11 +178,7 @@ class InboxActivities(TestCase):
|
|||
"published": "Mon, 25 May 2020 19:31:20 GMT",
|
||||
},
|
||||
}
|
||||
with patch(
|
||||
"bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores"
|
||||
) as redis_mock:
|
||||
views.inbox.activity_task(activity)
|
||||
self.assertTrue(redis_mock.called)
|
||||
views.inbox.activity_task(activity)
|
||||
self.assertFalse(models.Boost.objects.exists())
|
||||
|
||||
def test_unboost_unknown_boost(self):
|
||||
|
|
|
@ -8,6 +8,7 @@ from bookwyrm import models, views
|
|||
|
||||
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
|
||||
class InteractionViews(TestCase):
|
||||
"""viewing and creating statuses"""
|
||||
|
||||
|
@ -40,7 +41,7 @@ class InteractionViews(TestCase):
|
|||
parent_work=work,
|
||||
)
|
||||
|
||||
def test_favorite(self, _):
|
||||
def test_favorite(self, *_):
|
||||
"""create and broadcast faving a status"""
|
||||
view = views.Favorite.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -58,7 +59,7 @@ class InteractionViews(TestCase):
|
|||
self.assertEqual(notification.user, self.local_user)
|
||||
self.assertEqual(notification.related_user, self.remote_user)
|
||||
|
||||
def test_unfavorite(self, _):
|
||||
def test_unfavorite(self, *_):
|
||||
"""unfav a status"""
|
||||
view = views.Unfavorite.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -75,7 +76,7 @@ class InteractionViews(TestCase):
|
|||
self.assertEqual(models.Favorite.objects.count(), 0)
|
||||
self.assertEqual(models.Notification.objects.count(), 0)
|
||||
|
||||
def test_boost(self, _):
|
||||
def test_boost(self, *_):
|
||||
"""boost a status"""
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -97,7 +98,7 @@ class InteractionViews(TestCase):
|
|||
self.assertEqual(notification.related_user, self.remote_user)
|
||||
self.assertEqual(notification.related_status, status)
|
||||
|
||||
def test_self_boost(self, _):
|
||||
def test_self_boost(self, *_):
|
||||
"""boost your own status"""
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -121,7 +122,7 @@ class InteractionViews(TestCase):
|
|||
|
||||
self.assertFalse(models.Notification.objects.exists())
|
||||
|
||||
def test_boost_unlisted(self, _):
|
||||
def test_boost_unlisted(self, *_):
|
||||
"""boost a status"""
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -136,7 +137,7 @@ class InteractionViews(TestCase):
|
|||
boost = models.Boost.objects.get()
|
||||
self.assertEqual(boost.privacy, "unlisted")
|
||||
|
||||
def test_boost_private(self, _):
|
||||
def test_boost_private(self, *_):
|
||||
"""boost a status"""
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -149,7 +150,7 @@ class InteractionViews(TestCase):
|
|||
view(request, status.id)
|
||||
self.assertFalse(models.Boost.objects.exists())
|
||||
|
||||
def test_boost_twice(self, _):
|
||||
def test_boost_twice(self, *_):
|
||||
"""boost a status"""
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -161,13 +162,17 @@ class InteractionViews(TestCase):
|
|||
view(request, status.id)
|
||||
self.assertEqual(models.Boost.objects.count(), 1)
|
||||
|
||||
def test_unboost(self, _):
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_unboost(self, *_):
|
||||
"""undo a boost"""
|
||||
view = views.Unboost.as_view()
|
||||
request = self.factory.post("")
|
||||
request.user = self.remote_user
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
status = models.Status.objects.create(user=self.local_user, content="hi")
|
||||
status = models.Status.objects.create(user=self.local_user, content="hi")
|
||||
|
||||
with patch(
|
||||
"bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores"
|
||||
):
|
||||
views.Boost.as_view()(request, status.id)
|
||||
|
||||
self.assertEqual(models.Boost.objects.count(), 1)
|
||||
|
|
|
@ -37,8 +37,12 @@ class ManageInvites(View):
|
|||
PAGE_LENGTH,
|
||||
)
|
||||
|
||||
page = paginated.get_page(request.GET.get("page"))
|
||||
data = {
|
||||
"invites": paginated.get_page(request.GET.get("page")),
|
||||
"invites": page,
|
||||
"page_range": paginated.get_elided_page_range(
|
||||
page.number, on_each_side=2, on_ends=1
|
||||
),
|
||||
"form": forms.CreateInviteForm(),
|
||||
}
|
||||
return TemplateResponse(request, "settings/manage_invites.html", data)
|
||||
|
@ -118,15 +122,16 @@ class ManageInviteRequests(View):
|
|||
reduce(operator.or_, (Q(**f) for f in filters))
|
||||
).distinct()
|
||||
|
||||
paginated = Paginator(
|
||||
requests,
|
||||
PAGE_LENGTH,
|
||||
)
|
||||
paginated = Paginator(requests, PAGE_LENGTH)
|
||||
|
||||
page = paginated.get_page(request.GET.get("page"))
|
||||
data = {
|
||||
"ignored": ignored,
|
||||
"count": paginated.count,
|
||||
"requests": paginated.get_page(request.GET.get("page")),
|
||||
"requests": page,
|
||||
"page_range": paginated.get_elided_page_range(
|
||||
page.number, on_each_side=2, on_ends=1
|
||||
),
|
||||
"sort": sort,
|
||||
}
|
||||
return TemplateResponse(request, "settings/manage_invite_requests.html", data)
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: 0.1.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-06-07 14:09+0000\n"
|
||||
"POT-Creation-Date: 2021-06-09 16:46+0000\n"
|
||||
"PO-Revision-Date: 2021-04-05 12:44+0100\n"
|
||||
"Last-Translator: Fabien Basmaison <contact@arkhi.org>\n"
|
||||
"Language-Team: Mouse Reeve <LL@li.org>\n"
|
||||
|
@ -91,23 +91,23 @@ msgstr "nom du compte :"
|
|||
msgid "A user with that username already exists."
|
||||
msgstr "Ce nom est déjà associé à un compte."
|
||||
|
||||
#: bookwyrm/settings.py:160
|
||||
#: bookwyrm/settings.py:156
|
||||
msgid "English"
|
||||
msgstr "English"
|
||||
|
||||
#: bookwyrm/settings.py:161
|
||||
#: bookwyrm/settings.py:157
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
#: bookwyrm/settings.py:162
|
||||
#: bookwyrm/settings.py:158
|
||||
msgid "Spanish"
|
||||
msgstr "Español"
|
||||
|
||||
#: bookwyrm/settings.py:163
|
||||
#: bookwyrm/settings.py:159
|
||||
msgid "French"
|
||||
msgstr "Français"
|
||||
|
||||
#: bookwyrm/settings.py:164
|
||||
#: bookwyrm/settings.py:160
|
||||
msgid "Simplified Chinese"
|
||||
msgstr "简化字"
|
||||
|
||||
|
@ -154,12 +154,12 @@ msgid "Wikipedia"
|
|||
msgstr "Wikipedia"
|
||||
|
||||
#: bookwyrm/templates/author/author.html:69
|
||||
#: bookwyrm/templates/book/book.html:78
|
||||
#: bookwyrm/templates/book/book.html:87
|
||||
msgid "View on OpenLibrary"
|
||||
msgstr "Voir sur OpenLibrary"
|
||||
|
||||
#: bookwyrm/templates/author/author.html:77
|
||||
#: bookwyrm/templates/book/book.html:81
|
||||
#: bookwyrm/templates/book/book.html:90
|
||||
msgid "View on Inventaire"
|
||||
msgstr "Voir sur Inventaire"
|
||||
|
||||
|
@ -252,7 +252,7 @@ msgid "Goodreads key:"
|
|||
msgstr "Clé Goodreads :"
|
||||
|
||||
#: bookwyrm/templates/author/edit_author.html:116
|
||||
#: bookwyrm/templates/book/book.html:124
|
||||
#: bookwyrm/templates/book/book.html:133
|
||||
#: bookwyrm/templates/book/edit_book.html:321
|
||||
#: bookwyrm/templates/lists/form.html:42
|
||||
#: bookwyrm/templates/preferences/edit_user.html:70
|
||||
|
@ -269,7 +269,7 @@ msgid "Save"
|
|||
msgstr "Enregistrer"
|
||||
|
||||
#: bookwyrm/templates/author/edit_author.html:117
|
||||
#: bookwyrm/templates/book/book.html:125 bookwyrm/templates/book/book.html:174
|
||||
#: bookwyrm/templates/book/book.html:134 bookwyrm/templates/book/book.html:183
|
||||
#: bookwyrm/templates/book/cover_modal.html:32
|
||||
#: bookwyrm/templates/book/edit_book.html:322
|
||||
#: bookwyrm/templates/moderation/report_modal.html:34
|
||||
|
@ -284,98 +284,98 @@ msgstr "Enregistrer"
|
|||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:31
|
||||
#: bookwyrm/templates/book/book.html:40
|
||||
#: bookwyrm/templates/discover/large-book.html:25
|
||||
#: bookwyrm/templates/discover/small-book.html:19
|
||||
msgid "by"
|
||||
msgstr "par"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:39 bookwyrm/templates/book/book.html:40
|
||||
#: bookwyrm/templates/book/book.html:48 bookwyrm/templates/book/book.html:49
|
||||
msgid "Edit Book"
|
||||
msgstr "Modifier le livre"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:57
|
||||
#: bookwyrm/templates/book/book.html:66
|
||||
#: bookwyrm/templates/book/cover_modal.html:5
|
||||
msgid "Add cover"
|
||||
msgstr "Ajouter une couverture"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:61
|
||||
#: bookwyrm/templates/book/book.html:70
|
||||
msgid "Failed to load cover"
|
||||
msgstr "La couverture n’a pu être chargée"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:101
|
||||
#: bookwyrm/templates/book/book.html:110
|
||||
#, python-format
|
||||
msgid "(%(review_count)s review)"
|
||||
msgid_plural "(%(review_count)s reviews)"
|
||||
msgstr[0] "(%(review_count)s critique)"
|
||||
msgstr[1] "(%(review_count)s critiques)"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:113
|
||||
#: bookwyrm/templates/book/book.html:122
|
||||
msgid "Add Description"
|
||||
msgstr "Ajouter une description"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:120
|
||||
#: bookwyrm/templates/book/book.html:129
|
||||
#: bookwyrm/templates/book/edit_book.html:136
|
||||
#: bookwyrm/templates/lists/form.html:12
|
||||
msgid "Description:"
|
||||
msgstr "Description :"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:134
|
||||
#: bookwyrm/templates/book/book.html:143
|
||||
#, python-format
|
||||
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
|
||||
msgstr "<a href=\"%(path)s/editions\">%(count)s éditions</a>"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:142
|
||||
#: bookwyrm/templates/book/book.html:151
|
||||
#, python-format
|
||||
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
|
||||
msgstr "Cette édition est sur votre étagère <a href=\"%(path)s\">%(shelf_name)s</a>."
|
||||
|
||||
#: bookwyrm/templates/book/book.html:148
|
||||
#: bookwyrm/templates/book/book.html:157
|
||||
#, python-format
|
||||
msgid "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a href=\"%(shelf_path)s\">%(shelf_name)s</a> shelf."
|
||||
msgstr "Une <a href=\"%(book_path)s\">édition différente</a> de ce livre existe sur votre étagère <a href=\"%(shelf_path)s\">%(shelf_name)s</a>."
|
||||
|
||||
#: bookwyrm/templates/book/book.html:159
|
||||
#: bookwyrm/templates/book/book.html:168
|
||||
msgid "Your reading activity"
|
||||
msgstr "Votre activité de lecture"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:162
|
||||
#: bookwyrm/templates/book/book.html:171
|
||||
msgid "Add read dates"
|
||||
msgstr "Ajouter des dates de lecture"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:171
|
||||
#: bookwyrm/templates/book/book.html:180
|
||||
msgid "Create"
|
||||
msgstr "Créer"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:181
|
||||
#: bookwyrm/templates/book/book.html:190
|
||||
msgid "You don't have any reading activity for this book."
|
||||
msgstr "Vous n’avez aucune activité de lecture pour ce livre"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:200
|
||||
#: bookwyrm/templates/book/book.html:209
|
||||
msgid "Reviews"
|
||||
msgstr "Critiques"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:205
|
||||
#: bookwyrm/templates/book/book.html:214
|
||||
msgid "Your reviews"
|
||||
msgstr "Vos critiques"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:211
|
||||
#: bookwyrm/templates/book/book.html:220
|
||||
msgid "Your comments"
|
||||
msgstr "Vos commentaires"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:217
|
||||
#: bookwyrm/templates/book/book.html:226
|
||||
msgid "Your quotes"
|
||||
msgstr "Vos citations"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:253
|
||||
#: bookwyrm/templates/book/book.html:262
|
||||
msgid "Subjects"
|
||||
msgstr "Sujets"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:265
|
||||
#: bookwyrm/templates/book/book.html:274
|
||||
msgid "Places"
|
||||
msgstr "Lieux"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:276 bookwyrm/templates/layout.html:61
|
||||
#: bookwyrm/templates/book/book.html:285 bookwyrm/templates/layout.html:61
|
||||
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
|
||||
#: bookwyrm/templates/search/layout.html:25
|
||||
#: bookwyrm/templates/search/layout.html:50
|
||||
|
@ -383,11 +383,11 @@ msgstr "Lieux"
|
|||
msgid "Lists"
|
||||
msgstr "Listes"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:287
|
||||
#: bookwyrm/templates/book/book.html:296
|
||||
msgid "Add to list"
|
||||
msgstr "Ajouter à la liste"
|
||||
|
||||
#: bookwyrm/templates/book/book.html:297
|
||||
#: bookwyrm/templates/book/book.html:306
|
||||
#: bookwyrm/templates/book/cover_modal.html:31
|
||||
#: bookwyrm/templates/lists/list.html:179
|
||||
msgid "Add"
|
||||
|
@ -2921,6 +2921,11 @@ msgstr "Niveau d’accès :"
|
|||
msgid "File exceeds maximum size: 10MB"
|
||||
msgstr "Ce fichier dépasse la taille limite : 10 Mo"
|
||||
|
||||
#: bookwyrm/templatetags/utilities.py:30
|
||||
#, python-format
|
||||
msgid "%(title)s: %(subtitle)s"
|
||||
msgstr "%(title)s (%(subtitle)s)"
|
||||
|
||||
#: bookwyrm/views/import_data.py:67
|
||||
msgid "Not a valid csv file"
|
||||
msgstr "Fichier CSV non valide"
|
||||
|
|
Loading…
Reference in a new issue