2022-12-13 06:44:29 +00:00
"""
Models for Social app
DataSignalManager captures create / update / ( soft / hard ) delete from Journal app , and generate Activity objects ,
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 ActionType ( models . TextChoices ) :
Create = ' create '
Delete = ' delete '
Update = ' update '
2022-12-13 16:33:58 -05:00
Add = ' add '
Remove = ' remove '
2022-12-13 06:44:29 +00:00
Like = ' like '
Undo_Like = ' undo_like '
Announce = ' announce '
Undo_Announce = ' undo_announce '
Follow = ' follow '
Undo_Follow = ' undo_follow '
Flag = ' flag '
Move = ' move '
Accept = ' accept '
Reject = ' reject '
Block = ' block '
Undo_Block = ' undo_block '
class ActivityManager :
def __init__ ( self , user ) :
self . owner = user
def get_viewable_activities ( self , before_time = None ) :
q = Q ( owner_id__in = self . owner . following , visibility__lt = 2 ) | Q ( owner = self . owner )
2022-12-13 18:12:43 +00:00
q = q & Q ( is_viewable = True )
2022-12-13 06:44:29 +00:00
if before_time :
q = q & Q ( created_time__lt = before_time )
return Activity . objects . filter ( q )
@staticmethod
def get_manager_for_user ( user ) :
return ActivityManager ( user )
User . activity_manager = cached_property ( ActivityManager . get_manager_for_user )
User . activity_manager . __set_name__ ( User , ' activity_manager ' )
class Activity ( models . Model , UserOwnedObjectMixin ) :
owner = models . ForeignKey ( User , on_delete = models . PROTECT )
visibility = models . PositiveSmallIntegerField ( default = 0 ) # 0: Public / 1: Follower only / 2: Self only
action_type = models . CharField ( blank = False , choices = ActionType . choices , max_length = 50 )
action_object = models . ForeignKey ( Piece , on_delete = models . SET_NULL , null = True )
is_viewable = models . BooleanField ( default = True ) # if viewable in local time line, otherwise it's event only for s2s
# action_uid = TODO
@property
def target ( self ) :
return get_attself . action_object
@property
def action_class ( self ) :
return self . action_object . __class__ . __name__
2022-12-13 18:12:43 +00:00
def __str__ ( self ) :
return f ' { self . id } : { self . action_type } : { self . action_object } : { self . is_viewable } '
2022-12-13 06:44:29 +00:00
class DefaultSignalProcessor ( ) :
def __init__ ( self , action_object ) :
self . action_object = action_object
def activity_viewable ( self , action_type ) :
2022-12-13 18:12:43 +00:00
return action_type == ActionType . Create and bool ( getattr ( self . action_object , ' attached_to ' , None ) is None )
2022-12-13 06:44:29 +00:00
def created ( self ) :
2022-12-13 18:12:43 +00:00
activity = Activity . objects . create ( owner = self . action_object . owner , visibility = self . action_object . visibility , action_object = self . action_object , action_type = ActionType . Create , is_viewable = self . activity_viewable ( ActionType . Create ) )
return activity
2022-12-13 06:44:29 +00:00
def updated ( self ) :
create_activity = Activity . objects . filter ( owner = self . action_object . owner , action_object = self . action_object , action_type = ActionType . Create ) . first ( )
action_type = ActionType . Update if create_activity else ActionType . Create
is_viewable = self . activity_viewable ( action_type )
return Activity . objects . create ( owner = self . action_object . owner , visibility = self . action_object . visibility , action_object = self . action_object , action_type = action_type , is_viewable = is_viewable )
def deleted ( self ) :
create_activity = Activity . objects . filter ( owner = self . action_object . owner , action_object = self . action_object , action_type = ActionType . Create ) . first ( )
if create_activity :
2022-12-13 18:12:43 +00:00
create_activity . is_viewable = False
2022-12-13 06:44:29 +00:00
create_activity . save ( )
2022-12-13 18:12:43 +00:00
else :
_logger . warning ( f ' unable to find create activity for { self . action_object } ' )
2022-12-13 06:44:29 +00:00
# FIXME action_object=self.action_object causing issues in test when hard delete, the bare minimum is to save id of the actual object that ActivityPub requires
return Activity . objects . create ( owner = self . action_object . owner , visibility = self . action_object . visibility , action_object = None , action_type = ActionType . Delete , is_viewable = self . activity_viewable ( ActionType . Delete ) )
class UnhandledSignalProcessor ( DefaultSignalProcessor ) :
def created ( self ) :
_logger . warning ( f ' unhandled created signal for { self . action_object } ' )
def updated ( self ) :
_logger . warning ( f ' unhandled updated signal for { self . action_object } ' )
def deleted ( self ) :
_logger . warning ( f ' unhandled deleted signal for { self . action_object } ' )
class DataSignalManager :
processors = { }
@staticmethod
def save_handler ( sender , instance , created , * * kwargs ) :
processor_class = DataSignalManager . processors . get ( instance . __class__ )
if not processor_class :
processor_class = GenericSignalProcessor
processor = processor_class ( instance )
if created :
processor . created ( )
elif getattr ( instance , ' is_deleted ' , False ) :
processor . deleted ( )
else :
processor . updated ( )
@staticmethod
def delete_handler ( sender , instance , * * kwargs ) :
processor_class = DataSignalManager . processors . get ( instance . __class__ )
if not processor_class :
processor_class = GenericSignalProcessor
processor = processor_class ( instance )
processor . deleted ( )
@staticmethod
def add_handler_for_model ( model ) :
2022-12-17 16:18:16 -05:00
if not settings . DISABLE_SOCIAL :
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
@DataSignalManager.register
class MarkProcessor ( DefaultSignalProcessor ) :
2022-12-13 18:12:43 +00:00
model = ShelfMember
2022-12-13 06:44:29 +00:00
# @DataSignalManager.register
# class ReplyProcessor(DefaultSignalProcessor):
# model = Reply
# def activity_viewable(self):
# return False
# @DataSignalManager.register
# class RatingProcessor(DefaultSignalProcessor):
# model = Rating
@DataSignalManager.register
class ReviewProcessor ( DefaultSignalProcessor ) :
model = Review
@DataSignalManager.register
class CollectionProcessor ( DefaultSignalProcessor ) :
model = Collection