lib.itmens/social/models.py

202 lines
6.3 KiB
Python
Raw Normal View History

2022-12-13 06:44:29 +00:00
"""
Models for Social app
DataSignalManager captures create/update/(soft/hard)delete/add/remove from Journal app, and generate Activity objects,
2022-12-13 06:44:29 +00:00
ActivityManager generates chronological view for user and, in future, ActivityStreams
"""
from django.db import models
from users.models import User
from catalog.common.models import Item
from journal.models import *
import logging
from functools import cached_property
from django.db.models.signals import post_save, post_delete, pre_delete
from django.db.models import Q
2022-12-17 16:18:16 -05:00
from django.conf import settings
2022-12-13 06:44:29 +00:00
_logger = logging.getLogger(__name__)
class ActivityTemplate(models.TextChoices):
2022-12-29 23:57:02 -05:00
MarkItem = "mark_item"
ReviewItem = "review_item"
CreateCollection = "create_collection"
LikeCollection = "like_collection"
2023-01-14 09:02:53 -05:00
FeatureCollection = "feature_collection"
CommentChildItem = "comment_child_item"
class LocalActivity(models.Model, UserOwnedObjectMixin):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
2022-12-29 23:57:02 -05:00
visibility = models.PositiveSmallIntegerField(
default=0
) # 0: Public / 1: Follower only / 2: Self only
template = models.CharField(
blank=False, choices=ActivityTemplate.choices, max_length=50
)
action_object = models.ForeignKey(Piece, on_delete=models.CASCADE)
created_time = models.DateTimeField(default=timezone.now, db_index=True)
2022-12-13 06:44:29 +00:00
2023-01-07 12:00:09 -05:00
class Meta:
index_together = [
["owner", "created_time"],
]
2022-12-21 14:34:36 -05:00
def __str__(self):
2022-12-29 23:57:02 -05:00
return f"Activity [{self.owner}:{self.template}:{self.action_object}]"
2022-12-21 14:34:36 -05:00
2022-12-13 06:44:29 +00:00
class ActivityManager:
def __init__(self, user):
self.owner = user
def get_timeline(self, before_time=None):
2023-07-07 16:54:15 -04:00
following = [x for x in self.owner.following if x not in self.owner.ignoring]
q = Q(owner_id__in=following, visibility__lt=2) | Q(owner=self.owner)
2022-12-13 06:44:29 +00:00
if before_time:
q = q & Q(created_time__lt=before_time)
2022-12-29 23:57:02 -05:00
return (
LocalActivity.objects.filter(q)
.order_by("-created_time")
.prefetch_related("action_object", "owner")
2022-12-29 23:57:02 -05:00
) # .select_related() https://github.com/django-polymorphic/django-polymorphic/pull/531
2022-12-13 06:44:29 +00:00
@staticmethod
def get_manager_for_user(user):
return ActivityManager(user)
User.activity_manager = cached_property(ActivityManager.get_manager_for_user) # type: ignore
User.activity_manager.__set_name__(User, "activity_manager") # type: ignore
2022-12-13 06:44:29 +00:00
class DataSignalManager:
processors = {}
@staticmethod
def save_handler(sender, instance, created, **kwargs):
processor_class = DataSignalManager.processors.get(instance.__class__)
2023-01-14 09:02:53 -05:00
if processor_class:
processor = processor_class(instance)
if created:
2022-12-29 23:57:02 -05:00
if hasattr(processor, "created"):
processor.created()
2022-12-29 23:57:02 -05:00
elif hasattr(processor, "updated"):
processor.updated()
2022-12-13 06:44:29 +00:00
@staticmethod
def delete_handler(sender, instance, **kwargs):
processor_class = DataSignalManager.processors.get(instance.__class__)
if processor_class:
processor = processor_class(instance)
2022-12-29 23:57:02 -05:00
if hasattr(processor, "deleted"):
processor.deleted()
2022-12-13 06:44:29 +00:00
@staticmethod
def add_handler_for_model(model):
if settings.DISABLE_MODEL_SIGNAL:
_logger.warn(
f"{model.__name__} are not being indexed with DISABLE_MODEL_SIGNAL configuration"
)
return
post_save.connect(DataSignalManager.save_handler, sender=model)
pre_delete.connect(DataSignalManager.delete_handler, sender=model)
2022-12-13 06:44:29 +00:00
@staticmethod
def register(processor):
DataSignalManager.add_handler_for_model(processor.model)
DataSignalManager.processors[processor.model] = processor
return processor
class DefaultActivityProcessor:
model = None
template = None
2022-12-13 06:44:29 +00:00
def __init__(self, action_object):
self.action_object = action_object
2022-12-13 06:44:29 +00:00
def created(self):
params = {
2022-12-29 23:57:02 -05:00
"owner": self.action_object.owner,
"visibility": self.action_object.visibility,
"template": self.template,
"action_object": self.action_object,
2022-12-31 00:20:20 -05:00
"created_time": self.action_object.created_time,
}
LocalActivity.objects.create(**params)
2022-12-13 06:44:29 +00:00
def updated(self):
2022-12-29 23:57:02 -05:00
activity = LocalActivity.objects.filter(
action_object=self.action_object
).first()
if not activity:
self.created()
elif (
activity.visibility != self.action_object.visibility
or activity.created_time != activity.action_object.created_time
):
activity.visibility = self.action_object.visibility
activity.created_time = activity.action_object.created_time
activity.save()
2022-12-13 06:44:29 +00:00
@DataSignalManager.register
class MarkProcessor(DefaultActivityProcessor):
model = ShelfMember
template = ActivityTemplate.MarkItem
2022-12-13 06:44:29 +00:00
@DataSignalManager.register
class ReviewProcessor(DefaultActivityProcessor):
2022-12-13 06:44:29 +00:00
model = Review
template = ActivityTemplate.ReviewItem
2022-12-13 06:44:29 +00:00
@DataSignalManager.register
class CollectionProcessor(DefaultActivityProcessor):
2022-12-13 06:44:29 +00:00
model = Collection
template = ActivityTemplate.CreateCollection
@DataSignalManager.register
class LikeCollectionProcessor(DefaultActivityProcessor):
model = Like
template = ActivityTemplate.LikeCollection
def created(self):
2022-12-21 14:34:36 -05:00
if isinstance(self.action_object.target, Collection):
super().created()
def updated(self):
2022-12-21 14:34:36 -05:00
if isinstance(self.action_object.target, Collection):
super().updated()
2023-01-14 09:02:53 -05:00
@DataSignalManager.register
class FeaturedCollectionProcessor(DefaultActivityProcessor):
model = FeaturedCollection
template = ActivityTemplate.FeatureCollection
@DataSignalManager.register
class CommentChildItemProcessor(DefaultActivityProcessor):
model = Comment
template = ActivityTemplate.CommentChildItem
def created(self):
if self.action_object.item.class_name in ["podcastepisode", "tvepisode"]:
super().created()
def updated(self):
if self.action_object.item.class_name in ["podcastepisode", "tvepisode"]:
super().updated()
def reset_social_visibility_for_user(user: User, visibility: int):
LocalActivity.objects.filter(owner=user).update(visibility=visibility)