[Backport] Improving asynch timeline generation
parent
2b9ddc27db
commit
51a673586a
|
@ -32,9 +32,9 @@ class TimelineAppConfig(AppConfig):
|
||||||
signals.post_save.connect(handlers.on_new_history_entry,
|
signals.post_save.connect(handlers.on_new_history_entry,
|
||||||
sender=apps.get_model("history", "HistoryEntry"),
|
sender=apps.get_model("history", "HistoryEntry"),
|
||||||
dispatch_uid="timeline")
|
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"))
|
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"))
|
sender=apps.get_model("projects", "Membership"))
|
||||||
signals.post_save.connect(handlers.create_user_push_to_timeline,
|
signals.post_save.connect(handlers.create_user_push_to_timeline,
|
||||||
sender=get_user_model())
|
sender=get_user_model())
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
@ -89,10 +90,27 @@ def _push_to_timeline(objects, instance:object, event_type:str, created_datetime
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def push_to_timelines(project, user, obj, event_type, created_datetime, extra_data={}):
|
def push_to_timelines(project_id, user_id, obj_app_label, obj_model_name, obj_id, event_type, created_datetime, extra_data={}):
|
||||||
if project is not None:
|
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
|
# 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
|
## Project timeline
|
||||||
_push_to_timeline(project, obj, event_type, created_datetime,
|
_push_to_timeline(project, obj, event_type, created_datetime,
|
||||||
namespace=build_project_namespace(project),
|
namespace=build_project_namespace(project),
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
@ -29,11 +30,14 @@ from taiga.timeline.service import (push_to_timelines,
|
||||||
extract_user_info)
|
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:
|
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:
|
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):
|
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)
|
_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
|
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
|
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
|
# 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
|
created_datetime = instance.created_at
|
||||||
_push_to_timelines(instance.project, instance.user, instance, "create", created_datetime)
|
_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):
|
def delete_membership_push_to_timeline(sender, instance, **kwargs):
|
||||||
if instance.user:
|
if instance.user:
|
||||||
|
|
|
@ -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"
|
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():
|
def test_delete_project_timeline():
|
||||||
project = factories.ProjectFactory.create(name="test project timeline")
|
project = factories.ProjectFactory.create(name="test project timeline")
|
||||||
user_watcher= factories.UserFactory()
|
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)
|
history_services.take_snapshot(user_story, user=member_user)
|
||||||
|
|
||||||
user_timeline = service.get_profile_timeline(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[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)
|
external_user_timeline = service.get_profile_timeline(external_user)
|
||||||
assert len(external_user_timeline) == 1
|
assert len(external_user_timeline) == 1
|
||||||
|
|
Loading…
Reference in New Issue