Remove opentelemetry-related code from apps.py, formatting
This commit is contained in:
parent
0702731c7f
commit
64ea602f02
2 changed files with 308 additions and 340 deletions
|
@ -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:
|
||||
|
|
|
@ -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 )
|
||||
|
|
Loading…
Add table
Reference in a new issue