Moves suggestion logic to celery
This commit is contained in:
parent
644e5926db
commit
edfc27a3cd
|
@ -50,7 +50,7 @@ class RedisStore(ABC):
|
||||||
pipeline.execute()
|
pipeline.execute()
|
||||||
|
|
||||||
def bulk_remove_objects_from_store(self, objs, store):
|
def bulk_remove_objects_from_store(self, objs, store):
|
||||||
"""remoev a list of objects from a given store"""
|
"""remove a list of objects from a given store"""
|
||||||
pipeline = r.pipeline()
|
pipeline = r.pipeline()
|
||||||
for obj in objs[: self.max_length]:
|
for obj in objs[: self.max_length]:
|
||||||
pipeline.zrem(store, -1, obj.id)
|
pipeline.zrem(store, -1, obj.id)
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.db.models import signals, Count, Q
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from bookwyrm.redis_store import RedisStore, r
|
from bookwyrm.redis_store import RedisStore, r
|
||||||
|
from bookwyrm.tasks import app
|
||||||
|
|
||||||
|
|
||||||
class SuggestedUsers(RedisStore):
|
class SuggestedUsers(RedisStore):
|
||||||
|
@ -18,6 +19,8 @@ class SuggestedUsers(RedisStore):
|
||||||
|
|
||||||
def store_id(self, user): # pylint: disable=no-self-use
|
def store_id(self, user): # pylint: disable=no-self-use
|
||||||
"""the key used to store this user's recs"""
|
"""the key used to store this user's recs"""
|
||||||
|
if isinstance(user, int):
|
||||||
|
return "{:d}-suggestions".format(user)
|
||||||
return "{:d}-suggestions".format(user.id)
|
return "{:d}-suggestions".format(user.id)
|
||||||
|
|
||||||
def get_counts_from_rank(self, rank): # pylint: disable=no-self-use
|
def get_counts_from_rank(self, rank): # pylint: disable=no-self-use
|
||||||
|
@ -46,7 +49,9 @@ class SuggestedUsers(RedisStore):
|
||||||
"""given a user, who might want to follow them"""
|
"""given a user, who might want to follow them"""
|
||||||
return models.User.objects.filter(
|
return models.User.objects.filter(
|
||||||
local=True,
|
local=True,
|
||||||
).exclude(following=obj)
|
).exclude(
|
||||||
|
Q(id=obj.id) | Q(followers=obj) | Q(id__in=obj.blocks.all()) | Q(blocks=obj)
|
||||||
|
)
|
||||||
|
|
||||||
def rerank_obj(self, obj, update_only=True):
|
def rerank_obj(self, obj, update_only=True):
|
||||||
"""update all the instances of this user with new ranks"""
|
"""update all the instances of this user with new ranks"""
|
||||||
|
@ -56,6 +61,8 @@ class SuggestedUsers(RedisStore):
|
||||||
store_user,
|
store_user,
|
||||||
id=obj.id,
|
id=obj.id,
|
||||||
).first()
|
).first()
|
||||||
|
if not annotated_user:
|
||||||
|
continue
|
||||||
|
|
||||||
pipeline.zadd(
|
pipeline.zadd(
|
||||||
self.store_id(store_user),
|
self.store_id(store_user),
|
||||||
|
@ -66,8 +73,6 @@ class SuggestedUsers(RedisStore):
|
||||||
|
|
||||||
def rerank_user_suggestions(self, user):
|
def rerank_user_suggestions(self, user):
|
||||||
"""update the ranks of the follows suggested to a user"""
|
"""update the ranks of the follows suggested to a user"""
|
||||||
if not user.local:
|
|
||||||
raise ValueError("Trying to create suggestions for remote user: ", user.id)
|
|
||||||
self.populate_store(self.store_id(user))
|
self.populate_store(self.store_id(user))
|
||||||
|
|
||||||
def remove_suggestion(self, user, suggested_user):
|
def remove_suggestion(self, user, suggested_user):
|
||||||
|
@ -128,8 +133,8 @@ def update_suggestions_on_follow(sender, instance, created, *args, **kwargs):
|
||||||
return
|
return
|
||||||
|
|
||||||
if instance.user_subject.local:
|
if instance.user_subject.local:
|
||||||
suggested_users.remove_suggestion(instance.user_subject, instance.user_object)
|
remove_suggestion_task.delay(instance.user_subject.id, instance.user_object.id)
|
||||||
suggested_users.rerank_obj(instance.user_object)
|
rerank_user_task.delay(instance.user_object.id)
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=models.UserBlocks)
|
@receiver(signals.post_save, sender=models.UserBlocks)
|
||||||
|
@ -137,9 +142,9 @@ def update_suggestions_on_follow(sender, instance, created, *args, **kwargs):
|
||||||
def update_suggestions_on_block(sender, instance, *args, **kwargs):
|
def update_suggestions_on_block(sender, instance, *args, **kwargs):
|
||||||
"""remove blocked users from recs"""
|
"""remove blocked users from recs"""
|
||||||
if instance.user_subject.local:
|
if instance.user_subject.local:
|
||||||
suggested_users.remove_suggestion(instance.user_subject, instance.user_object)
|
remove_suggestion_task.delay(instance.user_subject.id, instance.user_object.id)
|
||||||
if instance.user_object.local:
|
if instance.user_object.local:
|
||||||
suggested_users.remove_suggestion(instance.user_object, instance.user_subject)
|
remove_suggestion_task.delay(instance.user_object.id, instance.user_subject.id)
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_delete, sender=models.UserFollows)
|
@receiver(signals.post_delete, sender=models.UserFollows)
|
||||||
|
@ -147,7 +152,7 @@ def update_suggestions_on_block(sender, instance, *args, **kwargs):
|
||||||
def update_suggestions_on_unfollow(sender, instance, **kwargs):
|
def update_suggestions_on_unfollow(sender, instance, **kwargs):
|
||||||
"""update rankings, but don't re-suggest because it was probably intentional"""
|
"""update rankings, but don't re-suggest because it was probably intentional"""
|
||||||
if instance.user_object.discoverable:
|
if instance.user_object.discoverable:
|
||||||
suggested_users.rerank_obj(instance.user_object)
|
rerank_user_task.delay(instance.user_object.id)
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=models.ShelfBook)
|
@receiver(signals.post_save, sender=models.ShelfBook)
|
||||||
|
@ -157,24 +162,50 @@ def update_rank_on_shelving(sender, instance, *args, **kwargs):
|
||||||
"""when a user shelves or unshelves a book, re-compute their rank"""
|
"""when a user shelves or unshelves a book, re-compute their rank"""
|
||||||
# if it's a local user, re-calculate who is rec'ed to them
|
# if it's a local user, re-calculate who is rec'ed to them
|
||||||
if instance.user.local:
|
if instance.user.local:
|
||||||
suggested_users.rerank_user_suggestions(instance.user)
|
rerank_suggestions_task.delay(instance.user.id)
|
||||||
|
|
||||||
# if the user is discoverable, update their rankings
|
# if the user is discoverable, update their rankings
|
||||||
if not instance.user.discoverable:
|
if instance.user.discoverable:
|
||||||
return
|
rerank_user_task.delay(instance.user.id)
|
||||||
suggested_users.rerank_obj(instance.user)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=models.User)
|
@receiver(signals.post_save, sender=models.User)
|
||||||
# pylint: disable=unused-argument, too-many-arguments
|
# pylint: disable=unused-argument, too-many-arguments
|
||||||
def add_new_user(sender, instance, created, **kwargs):
|
def add_new_user(sender, instance, created, **kwargs):
|
||||||
"""a new user, wow how cool"""
|
"""a new user, wow how cool"""
|
||||||
|
# a new user is found, create suggestions for them
|
||||||
if created and instance.local:
|
if created and instance.local:
|
||||||
# a new user is found, create suggestions for them
|
rerank_suggestions_task.delay(instance.id)
|
||||||
suggested_users.rerank_user_suggestions(instance)
|
|
||||||
|
|
||||||
# this happens on every save, not just when discoverability changes, annoyingly
|
# this happens on every save, not just when discoverability changes, annoyingly
|
||||||
if instance.discoverable:
|
if instance.discoverable:
|
||||||
suggested_users.rerank_obj(instance, update_only=False)
|
rerank_user_task.delay(instance.id, update_only=False)
|
||||||
elif not created:
|
elif not created:
|
||||||
suggested_users.remove_object_from_related_stores(instance)
|
remove_user_task.delay(instance.id)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def rerank_suggestions_task(user_id):
|
||||||
|
"""do the hard work in celery"""
|
||||||
|
suggested_users.rerank_user_suggestions(user_id)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def rerank_user_task(user_id, update_only=False):
|
||||||
|
"""do the hard work in celery"""
|
||||||
|
user = models.User.objects.get(id=user_id)
|
||||||
|
suggested_users.rerank_obj(user, update_only=update_only)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def remove_user_task(user_id):
|
||||||
|
"""do the hard work in celery"""
|
||||||
|
user = models.User.objects.get(id=user_id)
|
||||||
|
suggested_users.remove_object_from_related_stores(user)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def remove_suggestion_task(user_id, suggested_user_id):
|
||||||
|
"""remove a specific user from a specific user's suggestions"""
|
||||||
|
suggested_user = models.User.objects.get(id=suggested_user_id)
|
||||||
|
suggested_users.remove_suggestion(user_id, suggested_user)
|
||||||
|
|
|
@ -25,4 +25,5 @@ app.autodiscover_tasks(["bookwyrm"], related_name="connectors.abstract_connector
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="emailing")
|
app.autodiscover_tasks(["bookwyrm"], related_name="emailing")
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="goodreads_import")
|
app.autodiscover_tasks(["bookwyrm"], related_name="goodreads_import")
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="models.user")
|
app.autodiscover_tasks(["bookwyrm"], related_name="models.user")
|
||||||
|
app.autodiscover_tasks(["bookwyrm"], related_name="suggested_users")
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="views.inbox")
|
app.autodiscover_tasks(["bookwyrm"], related_name="views.inbox")
|
||||||
|
|
Loading…
Reference in New Issue