diff --git a/taiga/timeline/apps.py b/taiga/timeline/apps.py index d3eee80e..7b193552 100644 --- a/taiga/timeline/apps.py +++ b/taiga/timeline/apps.py @@ -32,9 +32,9 @@ class TimelineAppConfig(AppConfig): signals.post_save.connect(handlers.on_new_history_entry, sender=apps.get_model("history", "HistoryEntry"), dispatch_uid="timeline") - signals.pre_save.connect(handlers.create_membership_push_to_timeline, + signals.post_save.connect(handlers.create_membership_push_to_timeline, sender=apps.get_model("projects", "Membership")) - signals.post_delete.connect(handlers.delete_membership_push_to_timeline, + signals.pre_delete.connect(handlers.delete_membership_push_to_timeline, sender=apps.get_model("projects", "Membership")) signals.post_save.connect(handlers.create_user_push_to_timeline, sender=get_user_model()) diff --git a/taiga/timeline/service.py b/taiga/timeline/service.py index 7aacd4e7..f99f795e 100644 --- a/taiga/timeline/service.py +++ b/taiga/timeline/service.py @@ -17,6 +17,7 @@ # along with this program. If not, see . from django.apps import apps +from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.db.models import Model from django.db.models import Q @@ -89,10 +90,27 @@ def _push_to_timeline(objects, instance:object, event_type:str, created_datetime @app.task -def push_to_timelines(project, user, obj, event_type, created_datetime, extra_data={}): - if project is not None: +def push_to_timelines(project_id, user_id, obj_app_label, obj_model_name, obj_id, event_type, created_datetime, extra_data={}): + ObjModel = apps.get_model(obj_app_label, obj_model_name) + try: + obj = ObjModel.objects.get(id=obj_id) + except ObjModel.DoesNotExist: + return + + try: + user = get_user_model().objects.get(id=user_id) + except get_user_model().DoesNotExist: + return + + if project_id is not None: # Actions related with a project + projectModel = apps.get_model("projects", "Project") + try: + project = projectModel.objects.get(id=project_id) + except projectModel.DoesNotExist: + return + ## Project timeline _push_to_timeline(project, obj, event_type, created_datetime, namespace=build_project_namespace(project), diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index fe5bc811..738b2fe7 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -18,6 +18,7 @@ from django.conf import settings from django.contrib.auth import get_user_model +from django.contrib.contenttypes.models import ContentType from django.utils import timezone from django.utils.translation import ugettext as _ @@ -29,11 +30,14 @@ from taiga.timeline.service import (push_to_timelines, extract_user_info) -def _push_to_timelines(*args, **kwargs): +def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_data={}): + project_id = None if project is None else project.id + + ct = ContentType.objects.get_for_model(obj) if settings.CELERY_ENABLED: - push_to_timelines.delay(*args, **kwargs) + push_to_timelines.delay(project_id, user.id, ct.app_label, ct.model, obj.id, event_type, created_datetime, extra_data=extra_data) else: - push_to_timelines(*args, **kwargs) + push_to_timelines(project_id, user.id, ct.app_label, ct.model, obj.id, event_type, created_datetime, extra_data=extra_data) def _clean_description_fields(values_diff): @@ -86,7 +90,7 @@ def on_new_history_entry(sender, instance, created, **kwargs): _push_to_timelines(project, user, obj, event_type, created_datetime, extra_data=extra_data) -def create_membership_push_to_timeline(sender, instance, **kwargs): +def create_membership_push_to_timeline(sender, instance, created, **kwargs): """ Creating new membership with associated user. If the user is the project owner we don't do anything because that info will be shown in created project timeline entry @@ -96,29 +100,10 @@ def create_membership_push_to_timeline(sender, instance, **kwargs): """ # We shown in created project timeline entry - if not instance.pk and instance.user and instance.user != instance.project.owner: + if created and instance.user and instance.user != instance.project.owner: created_datetime = instance.created_at _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) - # Updating existing membership - elif instance.pk: - try: - prev_instance = sender.objects.get(pk=instance.pk) - if instance.user != prev_instance.user: - created_datetime = timezone.now() - # The new member - _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) - # If we are updating the old user is removed from project - if prev_instance.user: - _push_to_timelines(instance.project, - prev_instance.user, - prev_instance, - "delete", - created_datetime) - except sender.DoesNotExist: - # This happens with some tests, when a membership is created with a concrete id - pass - def delete_membership_push_to_timeline(sender, instance, **kwargs): if instance.user: diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 41376f77..e0353d15 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -351,29 +351,6 @@ def test_update_wiki_page_timeline(): assert user_watcher_timeline[0].data["values_diff"]["slug"][1] == "test wiki page timeline updated" -def test_update_membership_timeline(): - user_1 = factories.UserFactory.create() - user_2 = factories.UserFactory.create() - membership = factories.MembershipFactory.create(user=user_1) - membership.user = user_2 - membership.save() - project_timeline = service.get_project_timeline(membership.project) - user_1_timeline = service.get_user_timeline(user_1) - user_2_timeline = service.get_user_timeline(user_2) - assert project_timeline[0].event_type == "projects.membership.delete" - assert project_timeline[0].data["project"]["id"] == membership.project.id - assert project_timeline[0].data["user"]["id"] == user_1.id - assert project_timeline[1].event_type == "projects.membership.create" - assert project_timeline[1].data["project"]["id"] == membership.project.id - assert project_timeline[1].data["user"]["id"] == user_2.id - assert user_1_timeline[0].event_type == "projects.membership.delete" - assert user_1_timeline[0].data["project"]["id"] == membership.project.id - assert user_1_timeline[0].data["user"]["id"] == user_1.id - assert user_2_timeline[0].event_type == "projects.membership.create" - assert user_2_timeline[0].data["project"]["id"] == membership.project.id - assert user_2_timeline[0].data["user"]["id"] == user_2.id - - def test_delete_project_timeline(): project = factories.ProjectFactory.create(name="test project timeline") user_watcher= factories.UserFactory() @@ -534,9 +511,11 @@ def test_timeline_error_use_member_ids_instead_of_memberships_ids(): history_services.take_snapshot(user_story, user=member_user) user_timeline = service.get_profile_timeline(member_user) - assert len(user_timeline) == 2 + + assert len(user_timeline) == 3 assert user_timeline[0].event_type == "userstories.userstory.create" - assert user_timeline[1].event_type == "users.user.create" + assert user_timeline[1].event_type == "projects.membership.create" + assert user_timeline[2].event_type == "users.user.create" external_user_timeline = service.get_profile_timeline(external_user) assert len(external_user_timeline) == 1