Redirect to new URL when a merged object is requested

This commit is contained in:
Bart Schuurmans 2024-03-01 14:37:52 +01:00
parent 5e123972e8
commit e04cd79ff8
11 changed files with 97 additions and 46 deletions

View file

@ -1,4 +1,5 @@
""" the good people stuff! the authors! """ """ the good people stuff! the authors! """
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
@ -11,7 +12,11 @@ from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.connectors import connector_manager from bookwyrm.connectors import connector_manager
from bookwyrm.settings import PAGE_LENGTH from bookwyrm.settings import PAGE_LENGTH
from bookwyrm.views.helpers import is_api_request, maybe_redirect_local_path from bookwyrm.views.helpers import (
is_api_request,
get_mergeable_object_or_404,
maybe_redirect_local_path,
)
# pylint: disable= no-self-use # pylint: disable= no-self-use
@ -21,7 +26,7 @@ class Author(View):
# pylint: disable=unused-argument # pylint: disable=unused-argument
def get(self, request, author_id, slug=None): def get(self, request, author_id, slug=None):
"""landing page for an author""" """landing page for an author"""
author = get_object_or_404(models.Author, id=author_id) author = get_mergeable_object_or_404(models.Author, id=author_id)
if is_api_request(request): if is_api_request(request):
return ActivitypubResponse(author.to_activity()) return ActivitypubResponse(author.to_activity())
@ -56,13 +61,13 @@ class EditAuthor(View):
def get(self, request, author_id): def get(self, request, author_id):
"""info about a book""" """info about a book"""
author = get_object_or_404(models.Author, id=author_id) author = get_mergeable_object_or_404(models.Author, id=author_id)
data = {"author": author, "form": forms.AuthorForm(instance=author)} data = {"author": author, "form": forms.AuthorForm(instance=author)}
return TemplateResponse(request, "author/edit_author.html", data) return TemplateResponse(request, "author/edit_author.html", data)
def post(self, request, author_id): def post(self, request, author_id):
"""edit a author cool""" """edit a author cool"""
author = get_object_or_404(models.Author, id=author_id) author = get_mergeable_object_or_404(models.Author, id=author_id)
form = forms.AuthorForm(request.POST, request.FILES, instance=author) form = forms.AuthorForm(request.POST, request.FILES, instance=author)
if not form.is_valid(): if not form.is_valid():
@ -82,7 +87,7 @@ def update_author_from_remote(request, author_id, connector_identifier):
connector = connector_manager.load_connector( connector = connector_manager.load_connector(
get_object_or_404(models.Connector, identifier=connector_identifier) get_object_or_404(models.Connector, identifier=connector_identifier)
) )
author = get_object_or_404(models.Author, id=author_id) author = get_mergeable_object_or_404(models.Author, id=author_id)
connector.update_author_from_remote(author) connector.update_author_from_remote(author)

View file

@ -1,4 +1,5 @@
""" the good stuff! the books! """ """ the good stuff! the books! """
from uuid import uuid4 from uuid import uuid4
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
@ -15,7 +16,11 @@ from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.connectors import connector_manager, ConnectorException from bookwyrm.connectors import connector_manager, ConnectorException
from bookwyrm.connectors.abstract_connector import get_image from bookwyrm.connectors.abstract_connector import get_image
from bookwyrm.settings import PAGE_LENGTH from bookwyrm.settings import PAGE_LENGTH
from bookwyrm.views.helpers import is_api_request, maybe_redirect_local_path from bookwyrm.views.helpers import (
is_api_request,
maybe_redirect_local_path,
get_mergeable_object_or_404,
)
# pylint: disable=no-self-use # pylint: disable=no-self-use
@ -40,7 +45,11 @@ class Book(View):
# table, so they never have clashing IDs # table, so they never have clashing IDs
book = ( book = (
models.Edition.viewer_aware_objects(request.user) models.Edition.viewer_aware_objects(request.user)
.filter(Q(id=book_id) | Q(parent_work__id=book_id)) .filter(
Q(id=book_id)
| Q(parent_work__id=book_id)
| Q(absorbed__deleted_id=book_id)
)
.order_by("-edition_rank") .order_by("-edition_rank")
.select_related("parent_work") .select_related("parent_work")
.prefetch_related("authors", "file_links") .prefetch_related("authors", "file_links")
@ -82,11 +91,13 @@ class Book(View):
"book": book, "book": book,
"statuses": paginated.get_page(request.GET.get("page")), "statuses": paginated.get_page(request.GET.get("page")),
"review_count": reviews.count(), "review_count": reviews.count(),
"ratings": reviews.filter( "ratings": (
Q(content__isnull=True) | Q(content="") reviews.filter(Q(content__isnull=True) | Q(content="")).select_related(
).select_related("user") "user"
if not user_statuses )
else None, if not user_statuses
else None
),
"rating": reviews.aggregate(Avg("rating"))["rating__avg"], "rating": reviews.aggregate(Avg("rating"))["rating__avg"],
"lists": lists, "lists": lists,
"update_error": kwargs.get("update_error", False), "update_error": kwargs.get("update_error", False),
@ -130,7 +141,7 @@ class Book(View):
@require_POST @require_POST
def upload_cover(request, book_id): def upload_cover(request, book_id):
"""upload a new cover""" """upload a new cover"""
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
book.last_edited_by = request.user book.last_edited_by = request.user
url = request.POST.get("cover-url") url = request.POST.get("cover-url")
@ -168,7 +179,7 @@ def set_cover_from_url(url):
@permission_required("bookwyrm.edit_book", raise_exception=True) @permission_required("bookwyrm.edit_book", raise_exception=True)
def add_description(request, book_id): def add_description(request, book_id):
"""upload a new cover""" """upload a new cover"""
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
description = request.POST.get("description") description = request.POST.get("description")
@ -199,7 +210,9 @@ def update_book_from_remote(request, book_id, connector_identifier):
connector = connector_manager.load_connector( connector = connector_manager.load_connector(
get_object_or_404(models.Connector, identifier=connector_identifier) get_object_or_404(models.Connector, identifier=connector_identifier)
) )
book = get_object_or_404(models.Book.objects.select_subclasses(), id=book_id) book = get_mergeable_object_or_404(
models.Book.objects.select_subclasses(), id=book_id
)
try: try:
connector.update_book_from_remote(book) connector.update_book_from_remote(book)

View file

@ -1,4 +1,5 @@
""" the good stuff! the books! """ """ the good stuff! the books! """
from re import sub, findall from re import sub, findall
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.postgres.search import SearchRank, SearchVector from django.contrib.postgres.search import SearchRank, SearchVector
@ -18,9 +19,10 @@ from bookwyrm.utils.isni import (
build_author_from_isni, build_author_from_isni,
augment_author_metadata, augment_author_metadata,
) )
from bookwyrm.views.helpers import get_edition from bookwyrm.views.helpers import get_edition, get_mergeable_object_or_404
from .books import set_cover_from_url from .books import set_cover_from_url
# pylint: disable=no-self-use # pylint: disable=no-self-use
@method_decorator(login_required, name="dispatch") @method_decorator(login_required, name="dispatch")
@method_decorator( @method_decorator(
@ -42,7 +44,7 @@ class EditBook(View):
def post(self, request, book_id): def post(self, request, book_id):
"""edit a book cool""" """edit a book cool"""
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
form = forms.EditionForm(request.POST, request.FILES, instance=book) form = forms.EditionForm(request.POST, request.FILES, instance=book)
@ -130,7 +132,7 @@ class CreateBook(View):
with transaction.atomic(): with transaction.atomic():
book = form.save(request) book = form.save(request)
parent_work = get_object_or_404(models.Work, id=parent_work_id) parent_work = get_mergeable_object_or_404(models.Work, id=parent_work_id)
book.parent_work = parent_work book.parent_work = parent_work
if authors: if authors:
@ -295,7 +297,7 @@ class ConfirmEditBook(View):
if not book.parent_work: if not book.parent_work:
work_match = request.POST.get("parent_work") work_match = request.POST.get("parent_work")
if work_match and work_match != "0": if work_match and work_match != "0":
work = get_object_or_404(models.Work, id=work_match) work = get_mergeable_object_or_404(models.Work, id=work_match)
else: else:
work = models.Work.objects.create(title=form.cleaned_data["title"]) work = models.Work.objects.create(title=form.cleaned_data["title"])
work.authors.set(book.authors.all()) work.authors.set(book.authors.all())

View file

@ -1,4 +1,5 @@
""" the good stuff! the books! """ """ the good stuff! the books! """
from functools import reduce from functools import reduce
import operator import operator
@ -7,7 +8,7 @@ from django.core.cache import cache as django_cache
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.db import transaction from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import redirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.views import View from django.views import View
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
@ -15,7 +16,7 @@ from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.settings import PAGE_LENGTH from bookwyrm.settings import PAGE_LENGTH
from bookwyrm.views.helpers import is_api_request from bookwyrm.views.helpers import is_api_request, get_mergeable_object_or_404
# pylint: disable=no-self-use # pylint: disable=no-self-use
@ -24,7 +25,7 @@ class Editions(View):
def get(self, request, book_id): def get(self, request, book_id):
"""list of editions of a book""" """list of editions of a book"""
work = get_object_or_404(models.Work, id=book_id) work = get_mergeable_object_or_404(models.Work, id=book_id)
if is_api_request(request): if is_api_request(request):
return ActivitypubResponse(work.to_edition_list(**request.GET)) return ActivitypubResponse(work.to_edition_list(**request.GET))
@ -83,7 +84,7 @@ class Editions(View):
def switch_edition(request): def switch_edition(request):
"""switch your copy of a book to a different edition""" """switch your copy of a book to a different edition"""
edition_id = request.POST.get("edition") edition_id = request.POST.get("edition")
new_edition = get_object_or_404(models.Edition, id=edition_id) new_edition = get_mergeable_object_or_404(models.Edition, id=edition_id)
shelfbooks = models.ShelfBook.objects.filter( shelfbooks = models.ShelfBook.objects.filter(
book__parent_work=new_edition.parent_work, shelf__user=request.user book__parent_work=new_edition.parent_work, shelf__user=request.user
) )

View file

@ -1,4 +1,5 @@
""" the good stuff! the books! """ """ the good stuff! the books! """
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.db import transaction from django.db import transaction
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
@ -8,6 +9,7 @@ from django.utils.decorators import method_decorator
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.views.helpers import get_mergeable_object_or_404
# pylint: disable=no-self-use # pylint: disable=no-self-use
@ -20,7 +22,7 @@ class BookFileLinks(View):
def get(self, request, book_id): def get(self, request, book_id):
"""view links""" """view links"""
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
annotated_links = get_annotated_links(book) annotated_links = get_annotated_links(book)
data = {"book": book, "links": annotated_links} data = {"book": book, "links": annotated_links}
@ -36,7 +38,7 @@ class BookFileLinks(View):
# this form shouldn't ever really get here, since it's just a dropdown # this form shouldn't ever really get here, since it's just a dropdown
# get the data again rather than redirecting # get the data again rather than redirecting
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
annotated_links = get_annotated_links(book, form=form) annotated_links = get_annotated_links(book, form=form)
data = {"book": book, "links": annotated_links} data = {"book": book, "links": annotated_links}
@ -75,7 +77,7 @@ class AddFileLink(View):
def get(self, request, book_id): def get(self, request, book_id):
"""Create link form""" """Create link form"""
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
data = { data = {
"file_link_form": forms.FileLinkForm(), "file_link_form": forms.FileLinkForm(),
"book": book, "book": book,
@ -85,7 +87,9 @@ class AddFileLink(View):
@transaction.atomic @transaction.atomic
def post(self, request, book_id, link_id=None): def post(self, request, book_id, link_id=None):
"""Add a link to a copy of the book you can read""" """Add a link to a copy of the book you can read"""
book = get_object_or_404(models.Book.objects.select_subclasses(), id=book_id) book = get_mergeable_object_or_404(
models.Book.objects.select_subclasses(), id=book_id
)
link = get_object_or_404(models.FileLink, id=link_id) if link_id else None link = get_object_or_404(models.FileLink, id=link_id) if link_id else None
form = forms.FileLinkForm(request.POST, instance=link) form = forms.FileLinkForm(request.POST, instance=link)
if not form.is_valid(): if not form.is_valid():

View file

@ -1,10 +1,10 @@
""" books belonging to the same series """ """ books belonging to the same series """
from sys import float_info from sys import float_info
from django.views import View from django.views import View
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from bookwyrm.views.helpers import is_api_request from bookwyrm.views.helpers import is_api_request, get_mergeable_object_or_404
from bookwyrm import models from bookwyrm import models
@ -27,7 +27,7 @@ class BookSeriesBy(View):
if is_api_request(request): if is_api_request(request):
pass pass
author = get_object_or_404(models.Author, id=author_id) author = get_mergeable_object_or_404(models.Author, id=author_id)
results = models.Edition.objects.filter(authors=author, series=series_name) results = models.Edition.objects.filter(authors=author, series=series_name)
@ -56,9 +56,11 @@ class BookSeriesBy(View):
sorted(numbered_books, key=sort_by_series) sorted(numbered_books, key=sort_by_series)
+ sorted( + sorted(
dated_books, dated_books,
key=lambda book: book.first_published_date key=lambda book: (
if book.first_published_date book.first_published_date
else book.published_date, if book.first_published_date
else book.published_date
),
) )
+ sorted( + sorted(
unsortable_books, unsortable_books,

View file

@ -1,4 +1,5 @@
""" Helping new users figure out the lay of the land """ """ Helping new users figure out the lay of the land """
import re import re
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
@ -13,6 +14,7 @@ from django.views import View
from bookwyrm import book_search, forms, models from bookwyrm import book_search, forms, models
from bookwyrm.settings import INSTANCE_ACTOR_USERNAME from bookwyrm.settings import INSTANCE_ACTOR_USERNAME
from bookwyrm.suggested_users import suggested_users from bookwyrm.suggested_users import suggested_users
from bookwyrm.views.helpers import get_mergeable_object_or_404
from .preferences.edit_user import save_user_form from .preferences.edit_user import save_user_form
@ -80,8 +82,8 @@ class GetStartedBooks(View):
for k, v in request.POST.items() for k, v in request.POST.items()
if re.match(r"\d+", k) and re.match(r"\d+", v) if re.match(r"\d+", k) and re.match(r"\d+", v)
] ]
for (book_id, shelf_id) in shelve_actions: for book_id, shelf_id in shelve_actions:
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
shelf = get_object_or_404(models.Shelf, id=shelf_id) shelf = get_object_or_404(models.Shelf, id=shelf_id)
models.ShelfBook.objects.create(book=book, shelf=shelf, user=request.user) models.ShelfBook.objects.create(book=book, shelf=shelf, user=request.user)

View file

@ -1,4 +1,5 @@
""" helper functions used in various views """ """ helper functions used in various views """
import re import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
import dateutil.parser import dateutil.parser
@ -8,7 +9,7 @@ from dateutil.parser import ParserError
from requests import HTTPError from requests import HTTPError
from django.db.models import Q from django.db.models import Q
from django.conf import settings as django_settings from django.conf import settings as django_settings
from django.shortcuts import redirect from django.shortcuts import redirect, _get_queryset
from django.http import Http404 from django.http import Http404
from django.utils import translation from django.utils import translation
@ -232,3 +233,19 @@ def redirect_to_referer(request, *args, **kwargs):
# if not, use the args passed you'd normally pass to redirect() # if not, use the args passed you'd normally pass to redirect()
return redirect(*args or "/", **kwargs) return redirect(*args or "/", **kwargs)
# pylint: disable=redefined-builtin,invalid-name
def get_mergeable_object_or_404(klass, id):
"""variant of get_object_or_404 that also redirects if id has been merged
into another object"""
queryset = _get_queryset(klass)
try:
return queryset.get(pk=id)
except queryset.model.DoesNotExist:
try:
return queryset.get(absorbed__deleted_id=id)
except queryset.model.DoesNotExist:
pass
raise Http404(f"No {queryset.model} with ID {id} exists")

View file

@ -1,4 +1,5 @@
""" the good stuff! the books! """ """ the good stuff! the books! """
import logging import logging
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.cache import cache from django.core.cache import cache
@ -11,6 +12,7 @@ from django.views import View
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.views.helpers import get_mergeable_object_or_404
from bookwyrm.views.shelf.shelf_actions import unshelve from bookwyrm.views.shelf.shelf_actions import unshelve
from .status import CreateStatus from .status import CreateStatus
from .helpers import get_edition, handle_reading_status, is_api_request from .helpers import get_edition, handle_reading_status, is_api_request
@ -130,7 +132,7 @@ class ReadThrough(View):
def get(self, request, book_id, readthrough_id=None): def get(self, request, book_id, readthrough_id=None):
"""standalone form in case of errors""" """standalone form in case of errors"""
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
form = forms.ReadThroughForm() form = forms.ReadThroughForm()
data = {"form": form, "book": book} data = {"form": form, "book": book}
if readthrough_id: if readthrough_id:
@ -152,7 +154,7 @@ class ReadThrough(View):
) )
form = forms.ReadThroughForm(request.POST) form = forms.ReadThroughForm(request.POST)
if not form.is_valid(): if not form.is_valid():
book = get_object_or_404(models.Edition, id=book_id) book = get_mergeable_object_or_404(models.Edition, id=book_id)
data = {"form": form, "book": book} data = {"form": form, "book": book}
if request.POST.get("id"): if request.POST.get("id"):
data["readthrough"] = get_object_or_404( data["readthrough"] = get_object_or_404(

View file

@ -1,11 +1,12 @@
""" shelf views """ """ shelf views """
from django.db import IntegrityError, transaction from django.db import IntegrityError, transaction
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.views.helpers import redirect_to_referer from bookwyrm.views.helpers import redirect_to_referer, get_mergeable_object_or_404
@login_required @login_required
@ -36,7 +37,7 @@ def delete_shelf(request, shelf_id):
@transaction.atomic @transaction.atomic
def shelve(request): def shelve(request):
"""put a book on a user's shelf""" """put a book on a user's shelf"""
book = get_object_or_404(models.Edition, id=request.POST.get("book")) book = get_mergeable_object_or_404(models.Edition, id=request.POST.get("book"))
desired_shelf = get_object_or_404( desired_shelf = get_object_or_404(
request.user.shelf_set, identifier=request.POST.get("shelf") request.user.shelf_set, identifier=request.POST.get("shelf")
) )
@ -97,7 +98,7 @@ def shelve(request):
def unshelve(request, book_id=False): def unshelve(request, book_id=False):
"""remove a book from a user's shelf""" """remove a book from a user's shelf"""
identity = book_id if book_id else request.POST.get("book") identity = book_id if book_id else request.POST.get("book")
book = get_object_or_404(models.Edition, id=identity) book = get_mergeable_object_or_404(models.Edition, id=identity)
shelf_book = get_object_or_404( shelf_book = get_object_or_404(
models.ShelfBook, book=book, shelf__id=request.POST["shelf"] models.ShelfBook, book=book, shelf__id=request.POST["shelf"]
) )

View file

@ -1,4 +1,5 @@
""" what are we here for if not for posting """ """ what are we here for if not for posting """
import re import re
import logging import logging
@ -19,6 +20,7 @@ from markdown import markdown
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.models.report import DELETE_ITEM from bookwyrm.models.report import DELETE_ITEM
from bookwyrm.utils import regex, sanitizer from bookwyrm.utils import regex, sanitizer
from bookwyrm.views.helpers import get_mergeable_object_or_404
from .helpers import handle_remote_webfinger, is_api_request from .helpers import handle_remote_webfinger, is_api_request
from .helpers import load_date_in_user_tz_as_utc, redirect_to_referer from .helpers import load_date_in_user_tz_as_utc, redirect_to_referer
@ -52,7 +54,7 @@ class CreateStatus(View):
def get(self, request, status_type): # pylint: disable=unused-argument def get(self, request, status_type): # pylint: disable=unused-argument
"""compose view (...not used?)""" """compose view (...not used?)"""
book = get_object_or_404(models.Edition, id=request.GET.get("book")) book = get_mergeable_object_or_404(models.Edition, id=request.GET.get("book"))
data = {"book": book} data = {"book": book}
return TemplateResponse(request, "compose.html", data) return TemplateResponse(request, "compose.html", data)
@ -98,7 +100,7 @@ class CreateStatus(View):
# inspect the text for user tags # inspect the text for user tags
content = status.content content = status.content
mentions = find_mentions(request.user, content) mentions = find_mentions(request.user, content)
for (_, mention_user) in mentions.items(): for _, mention_user in mentions.items():
# add them to status mentions fk # add them to status mentions fk
status.mention_users.add(mention_user) status.mention_users.add(mention_user)
content = format_mentions(content, mentions) content = format_mentions(content, mentions)
@ -109,7 +111,7 @@ class CreateStatus(View):
# inspect the text for hashtags # inspect the text for hashtags
hashtags = find_or_create_hashtags(content) hashtags = find_or_create_hashtags(content)
for (_, mention_hashtag) in hashtags.items(): for _, mention_hashtag in hashtags.items():
# add them to status mentions fk # add them to status mentions fk
status.mention_hashtags.add(mention_hashtag) status.mention_hashtags.add(mention_hashtag)
content = format_hashtags(content, hashtags) content = format_hashtags(content, hashtags)
@ -140,7 +142,7 @@ class CreateStatus(View):
def format_mentions(content, mentions): def format_mentions(content, mentions):
"""Detect @mentions and make them links""" """Detect @mentions and make them links"""
for (mention_text, mention_user) in mentions.items(): for mention_text, mention_user in mentions.items():
# turn the mention into a link # turn the mention into a link
content = re.sub( content = re.sub(
rf"(?<!/)\B{mention_text}\b(?!@)", rf"(?<!/)\B{mention_text}\b(?!@)",
@ -152,7 +154,7 @@ def format_mentions(content, mentions):
def format_hashtags(content, hashtags): def format_hashtags(content, hashtags):
"""Detect #hashtags and make them links""" """Detect #hashtags and make them links"""
for (mention_text, mention_hashtag) in hashtags.items(): for mention_text, mention_hashtag in hashtags.items():
# turn the mention into a link # turn the mention into a link
content = re.sub( content = re.sub(
rf"(?<!/)\B{mention_text}\b(?!@)", rf"(?<!/)\B{mention_text}\b(?!@)",