mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-10-31 22:19:00 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
f6366e1c4a
13 changed files with 145 additions and 17 deletions
|
@ -6,7 +6,8 @@ from .base_activity import ActivityEncoder, Signature, naive_parse
|
||||||
from .base_activity import Link, Mention
|
from .base_activity import Link, Mention
|
||||||
from .base_activity import ActivitySerializerError, resolve_remote_id
|
from .base_activity import ActivitySerializerError, resolve_remote_id
|
||||||
from .image import Image
|
from .image import Image
|
||||||
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
|
from .note import Note, GeneratedNote, Article, Comment, Quotation
|
||||||
|
from .note import Review, Rating
|
||||||
from .note import Tombstone
|
from .note import Tombstone
|
||||||
from .ordered_collection import OrderedCollection, OrderedCollectionPage
|
from .ordered_collection import OrderedCollection, OrderedCollectionPage
|
||||||
from .ordered_collection import BookList, Shelf
|
from .ordered_collection import BookList, Shelf
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Tombstone(ActivityObject):
|
||||||
|
|
||||||
type: str = "Tombstone"
|
type: str = "Tombstone"
|
||||||
|
|
||||||
def to_model(self, *args, **kwargs):
|
def to_model(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||||
""" this should never really get serialized, just searched for """
|
""" this should never really get serialized, just searched for """
|
||||||
model = apps.get_model("bookwyrm.Status")
|
model = apps.get_model("bookwyrm.Status")
|
||||||
return model.find_existing_by_remote_id(self.id)
|
return model.find_existing_by_remote_id(self.id)
|
||||||
|
@ -60,6 +60,14 @@ class Comment(Note):
|
||||||
type: str = "Comment"
|
type: str = "Comment"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(init=False)
|
||||||
|
class Quotation(Comment):
|
||||||
|
""" a quote and commentary on a book """
|
||||||
|
|
||||||
|
quote: str
|
||||||
|
type: str = "Quotation"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class Review(Comment):
|
class Review(Comment):
|
||||||
""" a full book review """
|
""" a full book review """
|
||||||
|
@ -70,8 +78,9 @@ class Review(Comment):
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class Quotation(Comment):
|
class Rating(Comment):
|
||||||
""" a quote and commentary on a book """
|
""" just a star rating """
|
||||||
|
|
||||||
quote: str
|
rating: int
|
||||||
type: str = "Quotation"
|
content: str = None
|
||||||
|
type: str = "Rating"
|
||||||
|
|
|
@ -54,8 +54,8 @@ class RegisterForm(CustomForm):
|
||||||
|
|
||||||
class RatingForm(CustomForm):
|
class RatingForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Review
|
model = models.ReviewRating
|
||||||
fields = ["user", "book", "content", "rating", "privacy"]
|
fields = ["user", "book", "rating", "privacy"]
|
||||||
|
|
||||||
|
|
||||||
class ReviewForm(CustomForm):
|
class ReviewForm(CustomForm):
|
||||||
|
|
66
bookwyrm/migrations/0046_reviewrating.py
Normal file
66
bookwyrm/migrations/0046_reviewrating.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# Generated by Django 3.0.7 on 2021-02-25 18:36
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db import connection
|
||||||
|
from django.db.models import Q
|
||||||
|
import django.db.models.deletion
|
||||||
|
from psycopg2.extras import execute_values
|
||||||
|
|
||||||
|
|
||||||
|
def convert_review_rating(app_registry, schema_editor):
|
||||||
|
""" take rating type Reviews and convert them to ReviewRatings """
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
reviews = (
|
||||||
|
app_registry.get_model("bookwyrm", "Review")
|
||||||
|
.objects.using(db_alias)
|
||||||
|
.filter(Q(content__isnull=True) | Q(content=""))
|
||||||
|
)
|
||||||
|
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
values = [(r.id,) for r in reviews]
|
||||||
|
execute_values(
|
||||||
|
cursor,
|
||||||
|
"""
|
||||||
|
INSERT INTO bookwyrm_reviewrating(review_ptr_id)
|
||||||
|
VALUES %s""",
|
||||||
|
values,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def unconvert_review_rating(app_registry, schema_editor):
|
||||||
|
""" undo the conversion from ratings back to reviews"""
|
||||||
|
# All we need to do to revert this is drop the table, which Django will do
|
||||||
|
# on its own, as long as we have a valid reverse function. So, this is a
|
||||||
|
# no-op function so Django will do its thing
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0045_auto_20210210_2114"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ReviewRating",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"review_ptr",
|
||||||
|
models.OneToOneField(
|
||||||
|
auto_created=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
to="bookwyrm.Review",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
bases=("bookwyrm.review",),
|
||||||
|
),
|
||||||
|
migrations.RunPython(convert_review_rating, unconvert_review_rating),
|
||||||
|
]
|
13
bookwyrm/migrations/0047_merge_20210228_1839.py
Normal file
13
bookwyrm/migrations/0047_merge_20210228_1839.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Generated by Django 3.0.7 on 2021-02-28 18:39
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0046_reviewrating"),
|
||||||
|
("bookwyrm", "0046_sitesettings_privacy_policy"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = []
|
13
bookwyrm/migrations/0048_merge_20210308_1754.py
Normal file
13
bookwyrm/migrations/0048_merge_20210308_1754.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Generated by Django 3.0.7 on 2021-03-08 17:54
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0047_connector_isbn_search_url"),
|
||||||
|
("bookwyrm", "0047_merge_20210228_1839"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = []
|
|
@ -9,7 +9,8 @@ from .connector import Connector
|
||||||
from .shelf import Shelf, ShelfBook
|
from .shelf import Shelf, ShelfBook
|
||||||
from .list import List, ListItem
|
from .list import List, ListItem
|
||||||
|
|
||||||
from .status import Status, GeneratedNote, Review, Comment, Quotation
|
from .status import Status, GeneratedNote, Comment, Quotation
|
||||||
|
from .status import Review, ReviewRating
|
||||||
from .status import Boost
|
from .status import Boost
|
||||||
from .attachment import Image
|
from .attachment import Image
|
||||||
from .favorite import Favorite
|
from .favorite import Favorite
|
||||||
|
|
|
@ -278,13 +278,12 @@ class Review(Status):
|
||||||
def pure_name(self):
|
def pure_name(self):
|
||||||
""" clarify review names for mastodon serialization """
|
""" clarify review names for mastodon serialization """
|
||||||
if self.rating:
|
if self.rating:
|
||||||
# pylint: disable=bad-string-format-type
|
return 'Review of "{}" ({:d} stars): {}'.format(
|
||||||
return 'Review of "%s" (%d stars): %s' % (
|
|
||||||
self.book.title,
|
self.book.title,
|
||||||
self.rating,
|
self.rating,
|
||||||
self.name,
|
self.name,
|
||||||
)
|
)
|
||||||
return 'Review of "%s": %s' % (self.book.title, self.name)
|
return 'Review of "{}": {}'.format(self.book.title, self.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pure_content(self):
|
def pure_content(self):
|
||||||
|
@ -295,6 +294,22 @@ class Review(Status):
|
||||||
pure_type = "Article"
|
pure_type = "Article"
|
||||||
|
|
||||||
|
|
||||||
|
class ReviewRating(Review):
|
||||||
|
""" a subtype of review that only contains a rating """
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.rating:
|
||||||
|
raise ValueError("ReviewRating object must include a numerical rating")
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pure_content(self):
|
||||||
|
return 'Rated "{}": {:d} stars'.format(self.book.title, self.rating)
|
||||||
|
|
||||||
|
activity_serializer = activitypub.Rating
|
||||||
|
pure_type = "Note"
|
||||||
|
|
||||||
|
|
||||||
class Boost(ActivityMixin, Status):
|
class Boost(ActivityMixin, Status):
|
||||||
""" boost'ing a post """
|
""" boost'ing a post """
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
{% for error in form.openlibrary_key.errors %}
|
{% for error in form.openlibrary_key.errors %}
|
||||||
<p class="help is-danger">{{ error | escape }}</p>
|
<p class="help is-danger">{{ error | escape }}</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<p class="fields is-grouped"><label class="label" for="id_librarything_key">{% trans "OCLC Number:" %}</label> {{ form.oclc_number }} </p>
|
<p class="fields is-grouped"><label class="label" for="id_oclc_number">{% trans "OCLC Number:" %}</label> {{ form.oclc_number }} </p>
|
||||||
{% for error in form.oclc_number.errors %}
|
{% for error in form.oclc_number.errors %}
|
||||||
<p class="help is-danger">{{ error | escape }}</p>
|
<p class="help is-danger">{{ error | escape }}</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% load bookwyrm_tags %}
|
{% load bookwyrm_tags %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
{% if status.status_type == 'Review' %}
|
{% if status.status_type == 'Review' or status.status_type == 'Rating' %}
|
||||||
<div>
|
<div>
|
||||||
{% if status.name %}
|
{% if status.name %}
|
||||||
<h3 class="title is-5 has-subtitle" dir="auto">
|
<h3 class="title is-5 has-subtitle" dir="auto">
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
{% if status.status_type == 'GeneratedNote' %}
|
{% if status.status_type == 'GeneratedNote' %}
|
||||||
{{ status.content | safe }}
|
{{ status.content | safe }}
|
||||||
{% elif status.status_type == 'Review' and not status.name and not status.content%}
|
{% elif status.status_type == 'Rating' %}
|
||||||
{% trans "rated" %}
|
{% trans "rated" %}
|
||||||
{% elif status.status_type == 'Review' %}
|
{% elif status.status_type == 'Review' %}
|
||||||
{% trans "reviewed" %}
|
{% trans "reviewed" %}
|
||||||
|
|
|
@ -10,7 +10,15 @@ from bookwyrm.utils import regex
|
||||||
user_path = r"^user/(?P<username>%s)" % regex.username
|
user_path = r"^user/(?P<username>%s)" % regex.username
|
||||||
local_user_path = r"^user/(?P<username>%s)" % regex.localname
|
local_user_path = r"^user/(?P<username>%s)" % regex.localname
|
||||||
|
|
||||||
status_types = ["status", "review", "comment", "quotation", "boost", "generatednote"]
|
status_types = [
|
||||||
|
"status",
|
||||||
|
"review",
|
||||||
|
"reviewrating",
|
||||||
|
"comment",
|
||||||
|
"quotation",
|
||||||
|
"boost",
|
||||||
|
"generatednote",
|
||||||
|
]
|
||||||
status_path = r"%s/(%s)/(?P<status_id>\d+)" % (user_path, "|".join(status_types))
|
status_path = r"%s/(%s)/(?P<status_id>\d+)" % (user_path, "|".join(status_types))
|
||||||
|
|
||||||
book_path = r"^book/(?P<book_id>\d+)"
|
book_path = r"^book/(?P<book_id>\d+)"
|
||||||
|
|
|
@ -57,7 +57,7 @@ class CreateStatus(View):
|
||||||
status.mention_users.set(set(status.mention_users.all()))
|
status.mention_users.set(set(status.mention_users.all()))
|
||||||
|
|
||||||
# don't apply formatting to generated notes
|
# don't apply formatting to generated notes
|
||||||
if not isinstance(status, models.GeneratedNote):
|
if not isinstance(status, models.GeneratedNote) and content:
|
||||||
status.content = to_markdown(content)
|
status.content = to_markdown(content)
|
||||||
# do apply formatting to quotes
|
# do apply formatting to quotes
|
||||||
if hasattr(status, "quote"):
|
if hasattr(status, "quote"):
|
||||||
|
@ -85,6 +85,8 @@ class DeleteStatus(View):
|
||||||
|
|
||||||
def find_mentions(content):
|
def find_mentions(content):
|
||||||
""" detect @mentions in raw status content """
|
""" detect @mentions in raw status content """
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
for match in re.finditer(regex.strict_username, content):
|
for match in re.finditer(regex.strict_username, content):
|
||||||
username = match.group().strip().split("@")[1:]
|
username = match.group().strip().split("@")[1:]
|
||||||
if len(username) == 1:
|
if len(username) == 1:
|
||||||
|
|
Loading…
Reference in a new issue