Skip to content

Commit

Permalink
OpenConceptLab/ocl_issues#1780 | events creation on source/org create…
Browse files Browse the repository at this point in the history
… and user follow/unfollow and obj representation
  • Loading branch information
snyaggarwal committed Aug 16, 2024
1 parent 7ff08cb commit 8a272ea
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 11 deletions.
8 changes: 8 additions & 0 deletions core/collections/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@ def get_static_references_criteria():
def resource_type(self):
return COLLECTION_REFERENCE_TYPE

@property
def parent(self):
return self.collection

@property
def can_compute_against_other_system_version(self):
return (not self.system or not self.version) and not self.resource_version
Expand Down Expand Up @@ -1022,6 +1026,10 @@ def get_resource_url_kwarg():
def get_url_kwarg():
return 'expansion'

@property
def parent(self):
return self.collection_version

@property
def is_default(self):
return self.uri == self.collection_version.expansion_uri
Expand Down
17 changes: 17 additions & 0 deletions core/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,23 @@ def batch_delete(queryset):
for obj in queryset.filter():
obj.delete()

def record_create_event(self):
from core.events.models import Event
self.record_event(Event.CREATED)

def record_event(self, event_type):
from core.events.models import Event
Event.record(self, event_type)

def __repr__(self):
parts = []
current = self
while current is not None:
parts.append(f"{current.resource_type}:{current.mnemonic}")
current = get(current, 'parent')

return "/".join(reversed(parts))


class CommonLogoModel(models.Model):
logo_path = models.TextField(null=True, blank=True)
Expand Down
2 changes: 2 additions & 0 deletions core/common/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def stamp_uri(sender, instance, **kwargs): # pylint: disable=unused-argument
@receiver(post_save, sender=Organization)
@receiver(post_save, sender=UserProfile)
def propagate_owner_status(sender, instance=None, created=False, **kwargs): # pylint: disable=unused-argument
if created and instance.__class__ == Organization and instance.id != 1:
instance.record_create_event()
if not created and instance:
updated_sources = 0
updated_collections = 0
Expand Down
21 changes: 21 additions & 0 deletions core/events/migrations/0006_alter_event_actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.11 on 2024-08-16 11:20

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('events', '0005_rename_created_by_event_actor_and_more'),
]

operations = [
migrations.AlterField(
model_name='event',
name='actor',
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to=settings.AUTH_USER_MODEL),
),
]
20 changes: 16 additions & 4 deletions core/events/models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from django.db import models
from pydash import has


class Event(models.Model):
object_url = models.CharField(max_length=255)
referenced_object_url = models.CharField(max_length=255)
event_type = models.CharField(max_length=255)
actor = models.ForeignKey('users.UserProfile', on_delete=models.DO_NOTHING, related_name='creator_events')
actor = models.ForeignKey('users.UserProfile', on_delete=models.DO_NOTHING, related_name='events')
created_at = models.DateTimeField(auto_now_add=True)
public = models.BooleanField(default=True) # private events are shown to creator/staff/org members only

CREATED = 'Created'
FOLLOWED = 'Followed'
UNFOLLOWED = 'Unfollowed'

@property
def type(self):
return 'Event'
Expand Down Expand Up @@ -59,9 +64,7 @@ def object_repr(self):

@staticmethod
def get_object_repr(object_instance):
if object_instance:
return f"{object_instance.__class__.__name__}:{object_instance.mnemonic}"
return None
return repr(object_instance) if object_instance else None

@property
def description(self):
Expand All @@ -73,3 +76,12 @@ def clean_fields(self, exclude=None):
if self.public is None:
self.public = False
super().clean_fields(exclude=exclude)

@classmethod
def record(cls, instance, event_type=CREATED):
if instance.id:
public_can_view = instance.public_can_view if has(instance, 'public_can_view') else True
cls.objects.create(
object_url=instance.created_by.url, event_type=event_type, actor=instance.created_by,
referenced_object_url=instance.url, public=public_can_view
)
8 changes: 4 additions & 4 deletions core/events/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_object_repr(self):
UserProfileFactory(username='batman')
OrganizationFactory(mnemonic='waynecorp')

self.assertEqual(self.user_event.object_repr, "UserProfile:batman")
self.assertEqual(self.user_event.object_repr, "User:batman")
self.assertEqual(self.org_event.object_repr, "Organization:waynecorp")

def test_referenced_object(self):
Expand Down Expand Up @@ -91,7 +91,7 @@ def test_referenced_object_repr(self):
self.assertEqual(self.user_event.referenced_object_repr, "Organization:waynecorp")

self.org_event.referenced_object_url = batman.uri
self.assertEqual(self.org_event.referenced_object_repr, "UserProfile:batman")
self.assertEqual(self.org_event.referenced_object_repr, "User:batman")

def test_description(self):
batman = UserProfileFactory(username='batman', first_name='Bruce', last_name='Wayne')
Expand All @@ -100,12 +100,12 @@ def test_description(self):
self.user_event.referenced_object_url = '/orgs/waynecorp/'
self.assertEqual(
self.user_event.description,
f"UserProfile:batman Joined Organization:waynecorp by Alfred Pennyworth at {self.now}")
f"User:batman Joined Organization:waynecorp by Alfred Pennyworth at {self.now}")

self.org_event.referenced_object_url = batman.uri
self.assertEqual(
self.org_event.description,
f"Organization:waynecorp Subscribed UserProfile:batman by Alfred Pennyworth at {self.now}")
f"Organization:waynecorp Subscribed User:batman by Alfred Pennyworth at {self.now}")

def test_clean_fields(self):
self.user_event.referenced_object_url = '/orgs/waynecorp/'
Expand Down
2 changes: 1 addition & 1 deletion core/integration_tests/tests_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class EventsViewTest(OCLAPITestCase):
def test_get(self):
def test_get(self): # pylint:disable=too-many-statements
alfred = UserProfileFactory(username='alfred', first_name='Alfred', last_name='Pennyworth')
bruce = UserProfileFactory(username='bruce', first_name='Bruce', last_name='Wayne')
joker = UserProfileFactory(username='joker', first_name='Joker', last_name='The Clown')
Expand Down
2 changes: 2 additions & 0 deletions core/sources/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

@receiver(post_save, sender=Source)
def propagate_parent_attributes(sender, instance=None, created=False, **kwargs): # pylint: disable=unused-argument
if created:
instance.record_create_event()
if not created and instance:
updated_concepts = 0
updated_mappings = 0
Expand Down
22 changes: 22 additions & 0 deletions core/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,28 @@ def follower_queryset(self):
def following_queryset(self):
return self.followers.through.objects.filter(follower=self)

def follow(self, following):
self.following.add(following)

from core.events.models import Event
self.events.create(
actor=self,
event_type=Event.FOLLOWED,
object_url=self.url,
referenced_object_url=following.url,
)

def unfollow(self, following):
self.following.remove(following)

from core.events.models import Event
self.events.create(
actor=self,
event_type=Event.UNFOLLOWED,
object_url=self.url,
referenced_object_url=following.url,
)


class Follower(models.Model):
class Meta:
Expand Down
6 changes: 4 additions & 2 deletions core/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,9 @@ def post(self, request, *args, **kwargs): # pylint: disable=unused-argument
followed = self.get_following_user()
if followed.username == follower.username:
raise Http400('User cannot follow themselves')
follower.following.add(followed)

follower.follow(followed)

return Response(status=status.HTTP_204_NO_CONTENT)

def get_following_user(self):
Expand All @@ -589,7 +591,7 @@ class UserFollowingView(AbstractFollowerFollowedView):
def delete(self, request, *args, **kwargs): # pylint: disable=unused-argument
follower = self.get_object()
followed = self.get_following_user()
follower.following.remove(followed)
follower.unfollow(followed)
return Response(status=status.HTTP_204_NO_CONTENT)

def get_following_user(self):
Expand Down

0 comments on commit 8a272ea

Please sign in to comment.