1
0
Fork 0

Remove opentelemetry-related code from apps.py, formatting

This commit is contained in:
Reinout Meliesie 2025-03-11 02:23:21 +01:00
parent 0702731c7f
commit 64ea602f02
Signed by: zedfrigg
GPG key ID: 3AFCC06481308BC6
2 changed files with 308 additions and 340 deletions

View file

@ -1,406 +1,381 @@
""" access the activity streams stored in redis """
from datetime import timedelta
from django.dispatch import receiver
from django.db import transaction
from django.db.models import signals, Q
from django.utils import timezone
""" Access the activity streams stored in Redis """
from bookwyrm import models
from bookwyrm.redis_store import RedisStore, r
from bookwyrm.tasks import app, STREAMS, IMPORT_TRIGGERED
from bookwyrm . redis_store import RedisStore , r
from bookwyrm . tasks import app , STREAMS , IMPORT_TRIGGERED
from datetime import timedelta
from django . dispatch import receiver
from django . db import transaction
from django . db . models import signals , Q
from django . utils import timezone
class ActivityStream (RedisStore) :
"""A category of activity stream (like home, local, books)"""
class ActivityStream(RedisStore):
"""a category of activity stream (like home, local, books)"""
def stream_id(self, user_id):
"""the redis key for this user's instance of this stream"""
def stream_id (self , user_id ) :
"""The redis key for this user's instance of this stream"""
return f"{user_id}-{self.key}"
def unread_id(self, user_id):
"""the redis key for this user's unread count for this stream"""
stream_id = self.stream_id(user_id)
def unread_id ( self , user_id ) :
"""The redis key for this user's unread count for this stream"""
stream_id = self . stream_id (user_id)
return f"{stream_id}-unread"
def unread_by_status_type_id(self, user_id):
"""the redis key for this user's unread count for this stream"""
stream_id = self.stream_id(user_id)
def unread_by_status_type_id ( self , user_id ) :
"""The redis key for this user's unread count for this stream"""
stream_id = self . stream_id (user_id)
return f"{stream_id}-unread-by-type"
def get_rank(self, obj):
"""statuses are sorted by date published"""
return obj.published_date.timestamp()
def get_rank ( self , obj ) :
"""Statuses are sorted by date published"""
return obj . published_date . timestamp ()
def add_status(self, status, increment_unread=False):
"""add a status to users' feeds"""
audience = self.get_audience(status)
# the pipeline contains all the add-to-stream activities
pipeline = self.add_object_to_stores(
status, self.get_stores_for_users(audience), execute=False
def add_status ( self , status , increment_unread = False ) :
"""Add a status to users' feeds"""
audience = self . get_audience (status)
# The pipeline contains all the add-to-stream activities
pipeline = self . add_object_to_stores (
status ,
self . get_stores_for_users (audience) ,
execute = False ,
)
if increment_unread:
for user_id in audience:
# add to the unread status count
pipeline.incr(self.unread_id(user_id))
# add to the unread status count for status type
pipeline.hincrby(
self.unread_by_status_type_id(user_id), get_status_type(status), 1
if increment_unread :
for user_id in audience :
# Add to the unread status count
pipeline . incr ( self . unread_id (user_id) )
# Add to the unread status count for status type
pipeline . hincrby (
self . unread_by_status_type_id (user_id) ,
get_status_type (status) ,
1 ,
)
# and go!
pipeline.execute()
# And go!
pipeline . execute ()
def add_user_statuses(self, viewer, user):
"""add a user's statuses to another user's feed"""
# only add the statuses that the viewer should be able to see (ie, not dms)
statuses = models.Status.privacy_filter(viewer).filter(user=user)
self.bulk_add_objects_to_store(statuses, self.stream_id(viewer.id))
def add_user_statuses ( self , viewer , user ) :
"""Add a user's statuses to another user's feed"""
# Only add the statuses that the viewer should be able to see (ie, not DMs)
statuses = models . Status . privacy_filter (viewer) . filter ( user = user )
self . bulk_add_objects_to_store ( statuses , self . stream_id ( viewer . id ) )
def remove_user_statuses(self, viewer, user):
"""remove a user's status from another user's feed"""
# remove all so that followers only statuses are removed
statuses = user.status_set.all()
self.bulk_remove_objects_from_store(statuses, self.stream_id(viewer.id))
def remove_user_statuses ( self , viewer , user ) :
"""Remove a user's status from another user's feed"""
# Remove all so that followers only statuses are removed
statuses = user . status_set . all ()
self . bulk_remove_objects_from_store ( statuses , self . stream_id ( viewer . id ) )
def get_activity_stream(self, user):
"""load the statuses to be displayed"""
# clear unreads for this feed
r.set(self.unread_id(user.id), 0)
r.delete(self.unread_by_status_type_id(user.id))
def get_activity_stream ( self , user ) :
"""Load the statuses to be displayed"""
# Clear unreads for this feed
r . set ( self . unread_id ( user . id ) , 0 )
r . delete ( self . unread_by_status_type_id ( user . id ) )
statuses = self.get_store(self.stream_id(user.id))
return (
models.Status.objects.select_subclasses()
.filter(id__in=statuses)
.select_related(
"user",
"reply_parent",
"comment__book",
"review__book",
"quotation__book",
)
.prefetch_related("mention_books", "mention_users")
.order_by("-published_date")
)
statuses = self.get_store ( self . stream_id ( user . id ) )
return models . Status . objects . select_subclasses ()
. filter ( id__in = statuses )
. select_related ( "user" , "reply_parent" , "comment__book" , "review__book" , "quotation__book" )
. prefetch_related ( "mention_books" , "mention_users" )
. order_by ("-published_date")
def get_unread_count(self, user):
"""get the unread status count for this user's feed"""
return int(r.get(self.unread_id(user.id)) or 0)
def get_unread_count ( self , user ) :
"""Get the unread status count for this user's feed"""
return int ( r . get ( self . unread_id ( user . id ) ) or 0 )
def get_unread_count_by_status_type(self, user):
"""get the unread status count for this user's feed's status types"""
status_types = r.hgetall(self.unread_by_status_type_id(user.id))
def get_unread_count_by_status_type ( self , user ) :
"""Get the unread status count for this user's feed's status types"""
status_types = r . hgetall ( self . unread_by_status_type_id ( user . id ) )
return {
str(key.decode("utf-8")): int(value) or 0
for key, value in status_types.items()
str ( key . decode ("utf-8") ) : int (value) or 0
for key , value in status_types . items ()
}
def populate_streams(self, user):
"""go from zero to a timeline"""
self.populate_store(self.stream_id(user.id))
def populate_streams ( self , user) :
"""Go from zero to a timeline"""
self . populate_store ( self . stream_id ( user . id ) )
def _get_audience(self, status): # pylint: disable=no-self-use
"""given a status, what users should see it, excluding the author"""
# direct messages don't appear in feeds, direct comments/reviews/etc do
if status.privacy == "direct" and status.status_type == "Note":
return models.User.objects.none()
def _get_audience ( self , status) :
"""Given a status, what users should see it, excluding the author"""
# Direct messages don't appear in feeds, direct comments/reviews/etc do
if status . privacy == "direct" and status . status_type == "Note" :
return models . User . objects . none ()
# everybody who could plausibly see this status
audience = models.User.objects.filter(
is_active=True,
local=True, # we only create feeds for users of this instance
).exclude(
Q(id__in=status.user.blocks.all()) | Q(blocks=status.user) # not blocked
# Everybody who could plausibly see this status
audience = models . User . objects
. filter ( is_active = True , local = True ) # We only create feeds for users of this instance
. exclude (
Q ( id__in = status . user . blocks . all () ) |
Q ( blocks = status . user) # Not blocked
)
# Only visible to the poster and mentioned users
if status . privacy == "direct" :
audience = audience . filter ( Q ( id__in = status . mention_users . all () ) ) # If the user is mentioned
# Don't show replies to statuses the user can't see
elif status . reply_parent and status . reply_parent . privacy == "followers" :
audience = audience . filter (
Q ( id = status . reply_parent . user . id ) | # If the user is the OG author
( Q ( following = status . user ) & Q ( following = status . reply_parent . user ) ) # If the user is following both authors
)
# Only visible to the poster's followers and tagged users
elif status . privacy == "followers" :
audience = audience . filter ( Q ( following = status . user ) ) # If the user is following the author
return audience . distinct ("id")
def get_audience ( self , status ) :
"""Given a status, what users should see it"""
audience = self . _get_audience (status) . values_list ( "id" , flat = True )
status_author = models . User . objects
. filter ( is_active = True , local = True , id = status . user . id )
. values_list ( "id" , flat = True )
return list ( set (audience) | set (status_author) )
def get_stores_for_users ( self , user_ids ) :
"""Convert a list of user ids into Redis store ids"""
return [ self . stream_id (user_id) for user_id in user_ids ]
def get_statuses_for_user ( self , user ) :
"""Given a user, what statuses should they see on this stream"""
return models . Status . privacy_filter (
user ,
privacy_levels = [ "public" , "unlisted" , "followers" ] ,
)
# only visible to the poster and mentioned users
if status.privacy == "direct":
audience = audience.filter(
Q(id__in=status.mention_users.all()) # if the user is mentioned
)
# don't show replies to statuses the user can't see
elif status.reply_parent and status.reply_parent.privacy == "followers":
audience = audience.filter(
Q(id=status.reply_parent.user.id) # if the user is the OG author
| (
Q(following=status.user) & Q(following=status.reply_parent.user)
) # if the user is following both authors
)
# only visible to the poster's followers and tagged users
elif status.privacy == "followers":
audience = audience.filter(
Q(following=status.user) # if the user is following the author
)
return audience.distinct("id")
def get_audience(self, status):
"""given a status, what users should see it"""
audience = self._get_audience(status).values_list("id", flat=True)
status_author = models.User.objects.filter(
is_active=True, local=True, id=status.user.id
).values_list("id", flat=True)
return list(set(audience) | set(status_author))
def get_stores_for_users(self, user_ids):
"""convert a list of user ids into redis store ids"""
return [self.stream_id(user_id) for user_id in user_ids]
def get_statuses_for_user(self, user): # pylint: disable=no-self-use
"""given a user, what statuses should they see on this stream"""
return models.Status.privacy_filter(
user,
privacy_levels=["public", "unlisted", "followers"],
)
def get_objects_for_store(self, store):
user = models.User.objects.get(id=store.split("-")[0])
return self.get_statuses_for_user(user)
def get_objects_for_store ( self , store ) :
user = models . User . objects . get ( id = store . split ("-") [0] )
return self . get_statuses_for_user (user)
class HomeStream(ActivityStream):
"""users you follow"""
class HomeStream (ActivityStream) :
"""Users you follow"""
key = "home"
def get_audience(self, status):
audience = super()._get_audience(status)
# if the user is following the author
audience = audience.filter(following=status.user).values_list("id", flat=True)
# if the user is the post's author
status_author = models.User.objects.filter(
is_active=True, local=True, id=status.user.id
).values_list("id", flat=True)
return list(set(audience) | set(status_author))
def get_audience ( self , status ) :
audience = super () . _get_audience (status)
# If the user is following the author
audience = audience . filter ( following = status . user ) . values_list ( "id" , flat = True )
# If the user is the post's author
status_author = models . User . objects
. filter ( is_active = True , local = True , id = status . user . id )
. values_list ( "id" , flat = True )
return list ( set (audience) | set (status_author) )
def get_statuses_for_user(self, user):
return models.Status.privacy_filter(
user,
privacy_levels=["public", "unlisted", "followers"],
).exclude(
~Q( # remove everything except
Q(user__followers=user) # user following
| Q(user=user) # is self
| Q(mention_users=user) # mentions user
),
)
def get_statuses_for_user ( self , user ) :
return models . Status . privacy_filter (
user ,
privacy_levels = [ "public" , "unlisted" , "followers" ] ,
) . exclude ( ~ Q ( # Remove everything except
Q ( user__followers = user ) | # User following
Q ( user = user ) | # Is self
Q ( mention_users = user ) # Mentions user
) )
class LocalStream(ActivityStream):
"""users you follow"""
class LocalStream (ActivityStream) :
"""Users you follow"""
key = "local"
def get_audience(self, status):
# this stream wants no part in non-public statuses
if status.privacy != "public" or not status.user.local:
def get_audience ( self , status ) :
# This stream wants no part in non-public statuses
if status . privacy != "public" or not status . user . local :
return []
return super().get_audience(status)
return super () . get_audience (status)
def get_statuses_for_user(self, user):
# all public statuses by a local user
return models.Status.privacy_filter(
user,
privacy_levels=["public"],
).filter(user__local=True)
def get_statuses_for_user ( self , user ) :
# All public statuses by a local user
return models . Status
. privacy_filter ( user , privacy_levels = [ "public" ] )
. filter ( user__local = True )
class BooksStream(ActivityStream):
"""books on your shelves"""
class BooksStream (ActivityStream) :
"""Books on your shelves"""
key = "books"
def _get_audience(self, status):
"""anyone with the mentioned book on their shelves"""
work = (
status.book.parent_work
if hasattr(status, "book")
else status.mention_books.first().parent_work
)
def _get_audience ( self , status ) :
"""Anyone with the mentioned book on their shelves"""
work = status . book . parent_work if hasattr ( status , "book" )
else status . mention_books . first () . parent_work
audience = super()._get_audience(status)
return audience.filter(shelfbook__book__parent_work=work)
audience = super () . _get_audience (status)
return audience . filter ( shelfbook__book__parent_work = work )
def get_audience(self, status):
# only show public statuses on the books feed,
# and only statuses that mention books
if status.privacy != "public" or not (
status.mention_books.exists() or hasattr(status, "book")
):
def get_audience ( self , status ) :
# Only show public statuses on the books feed, and only statuses that mention books
if (
status . privacy != "public" or
not ( status . mention_books . exists () or hasattr ( status , "book" ) )
) :
return []
return super().get_audience(status)
return super () . get_audience (status)
def get_statuses_for_user(self, user):
"""any public status that mentions the user's books"""
books = user.shelfbook_set.values_list(
"book__parent_work__id", flat=True
).distinct()
return (
models.Status.privacy_filter(
user,
privacy_levels=["public"],
def get_statuses_for_user ( self , user ) :
"""Any public status that mentions the user's books"""
books = user . shelfbook_set
. values_list ( "book__parent_work__id" , flat = True )
. distinct ()
return models . Status
. privacy_filter ( user , privacy_levels = [ "public" ] )
. filter (
Q ( comment__book__parent_work__id__in = books ) |
Q ( quotation__book__parent_work__id__in = books ) |
Q ( review__book__parent_work__id__in = books ) |
Q ( mention_books__parent_work__id__in = books )
)
.filter(
Q(comment__book__parent_work__id__in=books)
| Q(quotation__book__parent_work__id__in=books)
| Q(review__book__parent_work__id__in=books)
| Q(mention_books__parent_work__id__in=books)
)
.distinct()
)
. distinct ()
def add_book_statuses(self, user, book):
"""add statuses about a book to a user's feed"""
work = book.parent_work
statuses = models.Status.privacy_filter(
user,
privacy_levels=["public"],
)
def add_book_statuses ( self , user , book ) :
"""Add statuses about a book to a user's feed"""
work = book . parent_work
statuses = models . Status . privacy_filter ( user , privacy_levels = [ "public" ] )
book_comments = statuses.filter(Q(comment__book__parent_work=work))
book_quotations = statuses.filter(Q(quotation__book__parent_work=work))
book_reviews = statuses.filter(Q(review__book__parent_work=work))
book_mentions = statuses.filter(Q(mention_books__parent_work=work))
book_comments = statuses . filter ( Q ( comment__book__parent_work = work ) )
book_quotations = statuses . filter ( Q ( quotation__book__parent_work = work ) )
book_reviews = statuses . filter ( Q ( review__book__parent_work = work ) )
book_mentions = statuses . filter ( Q ( mention_books__parent_work = work ) )
self.bulk_add_objects_to_store(book_comments, self.stream_id(user.id))
self.bulk_add_objects_to_store(book_quotations, self.stream_id(user.id))
self.bulk_add_objects_to_store(book_reviews, self.stream_id(user.id))
self.bulk_add_objects_to_store(book_mentions, self.stream_id(user.id))
self . bulk_add_objects_to_store ( book_comments , self . stream_id ( user . id ) )
self . bulk_add_objects_to_store ( book_quotations , self . stream_id ( user . id ) )
self . bulk_add_objects_to_store ( book_reviews , self . stream_id ( user . id ) )
self . bulk_add_objects_to_store ( book_mentions , self . stream_id ( user . id ) )
def remove_book_statuses(self, user, book):
"""add statuses about a book to a user's feed"""
work = book.parent_work
statuses = models.Status.privacy_filter(
user,
privacy_levels=["public"],
)
def remove_book_statuses ( self , user , book ) :
"""Add statuses about a book to a user's feed"""
work = book . parent_work
statuses = models . Status . privacy_filter ( user , privacy_levels = [ "public" ] )
book_comments = statuses.filter(Q(comment__book__parent_work=work))
book_quotations = statuses.filter(Q(quotation__book__parent_work=work))
book_reviews = statuses.filter(Q(review__book__parent_work=work))
book_mentions = statuses.filter(Q(mention_books__parent_work=work))
book_comments = statuses . filter ( Q ( comment__book__parent_work = work ) )
book_quotations = statuses . filter ( Q ( quotation__book__parent_work = work ) )
book_reviews = statuses . filter ( Q ( review__book__parent_work = work ) )
book_mentions = statuses . filter ( Q ( mention_books__parent_work = work ) )
self.bulk_remove_objects_from_store(book_comments, self.stream_id(user.id))
self.bulk_remove_objects_from_store(book_quotations, self.stream_id(user.id))
self.bulk_remove_objects_from_store(book_reviews, self.stream_id(user.id))
self.bulk_remove_objects_from_store(book_mentions, self.stream_id(user.id))
self . bulk_remove_objects_from_store ( book_comments , self . stream_id ( user . id ) )
self . bulk_remove_objects_from_store ( book_quotations , self . stream_id ( user . id ) )
self . bulk_remove_objects_from_store ( book_reviews , self . stream_id ( user . id ) )
self . bulk_remove_objects_from_store ( book_mentions , self . stream_id ( user . id ) )
# determine which streams are enabled in settings.py
# Determine which streams are enabled in settings.py
streams = {
"home": HomeStream(),
"local": LocalStream(),
"books": BooksStream(),
"home" : HomeStream () ,
"local" : LocalStream () ,
"books" : BooksStream () ,
}
@receiver(signals.post_save)
# pylint: disable=unused-argument
def add_status_on_create(sender, instance, created, *args, **kwargs):
"""add newly created statuses to activity feeds"""
# we're only interested in new statuses
if not issubclass(sender, models.Status):
@ receiver ( signals . post_save )
def add_status_on_create ( sender , instance , created , * args , ** kwargs ) :
"""Add newly created statuses to activity feeds"""
# We're only interested in new statuses
if not issubclass ( sender , models . Status ) :
return
if instance.deleted:
remove_status_task.delay(instance.id)
if instance . deleted :
remove_status_task . delay ( instance . id )
return
# We don't want to create multiple add_status_tasks for each status, and because
# the transactions are atomic, on_commit won't run until the status is ready to add.
if not created:
# We don't want to create multiple add_status_tasks for each status, and because the transactions are atomic,
# on_commit won't run until the status is ready to add.
if not created :
return
# when creating new things, gotta wait on the transaction
transaction.on_commit(
lambda: add_status_on_create_command(sender, instance, created)
)
# When creating new things, gotta wait on the transaction
transaction . on_commit ( lambda : add_status_on_create_command ( sender , instance , created ) )
def add_status_on_create_command(sender, instance, created):
"""runs this code only after the database commit completes"""
# boosts trigger 'saves" twice, so don't bother duplicating the task
if sender == models.Boost and not created:
def add_status_on_create_command ( sender , instance , created ) :
"""Runs this code only after the database commit completes"""
# Boosts trigger 'saves" twice, so don't bother duplicating the task
if sender == models . Boost and not created :
return
priority = STREAMS
# check if this is an old status, de-prioritize if so
# Check if this is an old status, de-prioritize if so
# (this will happen if federation is very slow, or, more expectedly, on csv import)
if instance.published_date < timezone.now() - timedelta(
days=1
) or instance.created_date < instance.published_date - timedelta(days=1):
# a backdated status from a local user is an import, don't add it
if instance.user.local:
if (
instance . published_date < timezone . now () - timedelta ( days = 1 ) or
instance.created_date < instance.published_date - timedelta(days=1)
) :
# A backdated status from a local user is an import, don't add it
if instance . user . local :
return
# an out of date remote status is a low priority but should be added
# An out of date remote status is a low priority but should be added
priority = IMPORT_TRIGGERED
add_status_task.apply_async(
args=(instance.id,),
kwargs={"increment_unread": created},
queue=priority,
add_status_task . apply_async (
args = ( instance . id , ) ,
kwargs = { "increment_unread" : created } ,
queue = priority ,
)
if sender == models.Boost:
handle_boost_task.delay(instance.id)
if sender == models . Boost :
handle_boost_task . delay ( instance . id )
@receiver(signals.post_delete, sender=models.Boost)
# pylint: disable=unused-argument
def remove_boost_on_delete(sender, instance, *args, **kwargs):
"""boosts are deleted"""
# remove the boost
remove_status_task.delay(instance.id)
# re-add the original status
add_status_task.delay(instance.boosted_status.id)
@ receiver ( signals . post_delete , sender = models . Boost )
def remove_boost_on_delete ( sender , instance , * args , ** kwargs ) :
"""Boosts are deleted"""
# Remove the boost
remove_status_task . delay ( instance . id )
# Re-add the original status
add_status_task . delay ( instance . boosted_status . id )
@receiver(signals.post_save, sender=models.UserFollows)
# pylint: disable=unused-argument
def add_statuses_on_follow(sender, instance, created, *args, **kwargs):
"""add a newly followed user's statuses to feeds"""
if not created or not instance.user_subject.local:
@ receiver ( signals . post_save , sender = models . UserFollows )
def add_statuses_on_follow ( sender , instance , created , * args , ** kwargs ) :
"""Add a newly followed user's statuses to feeds"""
if not created or not instance . user_subject . local :
return
add_user_statuses_task.delay(
instance.user_subject.id, instance.user_object.id, stream_list=["home"]
add_user_statuses_task . delay (
instance . user_subject . id ,
instance . user_object . id ,
stream_list = [ "home" ] ,
)
@receiver(signals.post_delete, sender=models.UserFollows)
# pylint: disable=unused-argument
def remove_statuses_on_unfollow(sender, instance, *args, **kwargs):
"""remove statuses from a feed on unfollow"""
if not instance.user_subject.local:
@ receiver ( signals . post_delete , sender = models . UserFollows )
def remove_statuses_on_unfollow ( sender , instance , * args , ** kwargs ) :
"""Remove statuses from a feed on unfollow"""
if not instance . user_subject . local :
return
remove_user_statuses_task.delay(
instance.user_subject.id, instance.user_object.id, stream_list=["home"]
remove_user_statuses_task . delay (
instance . user_subject . id ,
instance . user_object . id ,
stream_list = [ "home" ] ,
)
@receiver(signals.post_save, sender=models.UserBlocks)
# pylint: disable=unused-argument
def remove_statuses_on_block(sender, instance, *args, **kwargs):
"""remove statuses from all feeds on block"""
# blocks apply ot all feeds
if instance.user_subject.local:
remove_user_statuses_task.delay(
instance.user_subject.id, instance.user_object.id
@ receiver ( signals . post_save , sender = models . UserBlocks )
def remove_statuses_on_block ( sender , instance , * args , ** kwargs ) :
"""Remove statuses from all feeds on block"""
# Blocks apply ot all feeds
if instance . user_subject . local :
remove_user_statuses_task . delay (
instance . user_subject . id ,
instance . user_object . id ,
)
# and in both directions
if instance.user_object.local:
remove_user_statuses_task.delay(
instance.user_object.id, instance.user_subject.id
# And in both directions
if instance . user_object . local :
remove_user_statuses_task . delay (
instance . user_object . id ,
instance . user_subject . id ,
)
@receiver(signals.post_delete, sender=models.UserBlocks)
# pylint: disable=unused-argument
def add_statuses_on_unblock(sender, instance, *args, **kwargs):
"""add statuses back to all feeds on unblock"""
# make sure there isn't a block in the other direction
@ -430,7 +405,6 @@ def add_statuses_on_unblock(sender, instance, *args, **kwargs):
@receiver(signals.post_save, sender=models.User)
# pylint: disable=unused-argument
def populate_streams_on_account_create(sender, instance, created, *args, **kwargs):
"""build a user's feeds when they join"""
if not created or not instance.local:
@ -447,7 +421,6 @@ def populate_streams_on_account_create_command(instance_id):
@receiver(signals.pre_save, sender=models.ShelfBook)
# pylint: disable=unused-argument
def add_statuses_on_shelve(sender, instance, *args, **kwargs):
"""update books stream when user shelves a book"""
if not instance.user.local:
@ -463,7 +436,6 @@ def add_statuses_on_shelve(sender, instance, *args, **kwargs):
@receiver(signals.post_delete, sender=models.ShelfBook)
# pylint: disable=unused-argument
def remove_statuses_on_unshelve(sender, instance, *args, **kwargs):
"""update books stream when user unshelves a book"""
if not instance.user.local:

View file

@ -1,55 +1,51 @@
"""Do further startup configuration and initialization"""
import logging
import os
import urllib
import logging
from django.apps import AppConfig
from bookwyrm import settings
logger = logging.getLogger(__name__)
from django . apps import AppConfig
def download_file(url, destination):
logger = logging . getLogger (__name__)
def download_file ( url , destination) :
"""Downloads a file to the given path"""
try:
try :
# Ensure our destination directory exists
os.makedirs(os.path.dirname(destination), exist_ok=True)
with urllib.request.urlopen(url) as stream:
with open(destination, "b+w") as outfile:
outfile.write(stream.read())
except (urllib.error.HTTPError, urllib.error.URLError) as err:
logger.error("Failed to download file %s: %s", url, err)
except OSError as err:
logger.error("Couldn't open font file %s for writing: %s", destination, err)
except Exception as err: # pylint:disable=broad-except
logger.error("Unknown error in file download: %s", err)
os . makedirs ( os . path . dirname (destination) , exist_ok = True )
with urllib . request . urlopen (url) as stream :
with open ( destination , "b+w" ) as outfile :
outfile . write ( stream . read () )
except ( urllib . error . HTTPError , urllib . error . URLError ) as err :
logger . error ( "Failed to download file %s: %s" , url , err )
except OSError as err :
logger . error ( "Couldn't open font file %s for writing: %s" , destination , err )
except Exception as err :
logger . error ( "Unknown error in file download: %s" , err )
class BookwyrmConfig(AppConfig):
class BookwyrmConfig (AppConfig) :
"""Handles additional configuration"""
name = "bookwyrm"
verbose_name = "BookWyrm"
def ready(self):
"""set up OTLP and preview image files, if desired"""
if settings.OTEL_EXPORTER_OTLP_ENDPOINT or settings.OTEL_EXPORTER_CONSOLE:
# pylint: disable=import-outside-toplevel
from bookwyrm.telemetry import open_telemetry
open_telemetry.instrumentDjango()
open_telemetry.instrumentPostgres()
if settings.ENABLE_PREVIEW_IMAGES and settings.FONTS:
def ready (self) :
"""Set up preview image files, if desired"""
if settings . ENABLE_PREVIEW_IMAGES and settings . FONTS :
# Download any fonts that we don't have yet
logger.debug("Downloading fonts..")
for name, config in settings.FONTS.items():
font_path = os.path.join(
settings.FONT_DIR, config["directory"], config["filename"]
logger . debug ("Downloading fonts..")
for name , config in settings . FONTS . items () :
font_path = os . path . join (
settings . FONT_DIR , config [ "directory" ] , config [ "filename" ]
)
if "url" in config and not os.path.exists(font_path):
logger.info("Just a sec, downloading %s", name)
download_file(config["url"], font_path)
if "url" in config and not os . path . exists (font_path) :
logger . info ( "Just a sec, downloading %s" , name )
download_file ( config [ "url" ] , font_path )