Including epic in timelines
parent
35b799e89d
commit
db34ad32c9
|
@ -22,7 +22,7 @@ from django.utils.translation import ugettext as _
|
||||||
from taiga.base.api.utils import get_object_or_404
|
from taiga.base.api.utils import get_object_or_404
|
||||||
from taiga.base import filters, response
|
from taiga.base import filters, response
|
||||||
from taiga.base import exceptions as exc
|
from taiga.base import exceptions as exc
|
||||||
from taiga.base.decorators import list_route, detail_route
|
from taiga.base.decorators import list_route
|
||||||
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
||||||
from taiga.base.api.mixins import BlockedByProjectMixin
|
from taiga.base.api.mixins import BlockedByProjectMixin
|
||||||
from taiga.base.api.viewsets import NestedViewSetMixin
|
from taiga.base.api.viewsets import NestedViewSetMixin
|
||||||
|
@ -33,7 +33,6 @@ from taiga.projects.models import Project, EpicStatus
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
from taiga.projects.tagging.api import TaggedResourceMixin
|
from taiga.projects.tagging.api import TaggedResourceMixin
|
||||||
from taiga.projects.userstories.models import UserStory
|
|
||||||
from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin
|
from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
@ -226,7 +225,8 @@ class EpicViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin,
|
||||||
return response.Ok(epics_serialized.data)
|
return response.Ok(epics_serialized.data)
|
||||||
|
|
||||||
|
|
||||||
class EpicRelatedUserStoryViewSet(NestedViewSetMixin, BlockedByProjectMixin, ModelCrudViewSet):
|
class EpicRelatedUserStoryViewSet(NestedViewSetMixin, HistoryResourceMixin,
|
||||||
|
BlockedByProjectMixin, ModelCrudViewSet):
|
||||||
queryset = models.RelatedUserStory.objects.all()
|
queryset = models.RelatedUserStory.objects.all()
|
||||||
serializer_class = serializers.EpicRelatedUserStorySerializer
|
serializer_class = serializers.EpicRelatedUserStorySerializer
|
||||||
validator_class = validators.EpicRelatedUserStoryValidator
|
validator_class = validators.EpicRelatedUserStoryValidator
|
||||||
|
@ -288,13 +288,16 @@ class EpicRelatedUserStoryViewSet(NestedViewSetMixin, BlockedByProjectMixin, Mod
|
||||||
if project.blocked_code is not None:
|
if project.blocked_code is not None:
|
||||||
raise exc.Blocked(_("Blocked element"))
|
raise exc.Blocked(_("Blocked element"))
|
||||||
|
|
||||||
services.create_related_userstories_in_bulk(
|
related_userstories = services.create_related_userstories_in_bulk(
|
||||||
data["bulk_userstories"],
|
data["bulk_userstories"],
|
||||||
epic,
|
epic,
|
||||||
project=project,
|
project=project,
|
||||||
owner=request.user
|
owner=request.user
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for related_userstory in related_userstories:
|
||||||
|
self.persist_history_snapshot(obj=related_userstory)
|
||||||
|
|
||||||
related_uss_serialized = self.get_serializer_class()(epic.relateduserstory_set.all(), many=True)
|
related_uss_serialized = self.get_serializer_class()(epic.relateduserstory_set.all(), many=True)
|
||||||
return response.Ok(related_uss_serialized.data)
|
return response.Ok(related_uss_serialized.data)
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ class Epic(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.M
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RelatedUserStory(models.Model):
|
class RelatedUserStory(WatchedModelMixin, models.Model):
|
||||||
user_story = models.ForeignKey("userstories.UserStory", on_delete=models.CASCADE)
|
user_story = models.ForeignKey("userstories.UserStory", on_delete=models.CASCADE)
|
||||||
epic = models.ForeignKey("epics.Epic", on_delete=models.CASCADE)
|
epic = models.ForeignKey("epics.Epic", on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
@ -111,3 +111,11 @@ class RelatedUserStory(models.Model):
|
||||||
@property
|
@property
|
||||||
def project(self):
|
def project(self):
|
||||||
return self.epic.project
|
return self.epic.project
|
||||||
|
|
||||||
|
@property
|
||||||
|
def owner_id(self):
|
||||||
|
return self.epic.owner_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def assigned_to_id(self):
|
||||||
|
return self.epic.assigned_to_id
|
||||||
|
|
|
@ -124,7 +124,7 @@ def create_related_userstories_in_bulk(bulk_data, epic, **additional_fields):
|
||||||
finally:
|
finally:
|
||||||
connect_userstories_signals()
|
connect_userstories_signals()
|
||||||
|
|
||||||
return userstories
|
return related_userstories
|
||||||
|
|
||||||
|
|
||||||
def update_epic_related_userstories_order_in_bulk(bulk_data: list, epic: object):
|
def update_epic_related_userstories_order_in_bulk(bulk_data: list, epic: object):
|
||||||
|
|
|
@ -112,8 +112,11 @@ def epic_values(diff):
|
||||||
if "status" in diff:
|
if "status" in diff:
|
||||||
values["status"] = _get_us_status_values(diff["status"])
|
values["status"] = _get_us_status_values(diff["status"])
|
||||||
|
|
||||||
# TODO EPICS: What happen with usr stories?
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def epic_related_userstory_values(diff):
|
||||||
|
values = _common_users_values(diff)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,6 +320,16 @@ def epic_freezer(epic) -> dict:
|
||||||
return snapshot
|
return snapshot
|
||||||
|
|
||||||
|
|
||||||
|
def epic_related_userstory_freezer(related_us) -> dict:
|
||||||
|
snapshot = {
|
||||||
|
"user_story": related_us.user_story.id,
|
||||||
|
"epic": related_us.epic.id,
|
||||||
|
"order": related_us.order
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshot
|
||||||
|
|
||||||
|
|
||||||
def userstory_freezer(us) -> dict:
|
def userstory_freezer(us) -> dict:
|
||||||
rp_cls = apps.get_model("userstories", "RolePoints")
|
rp_cls = apps.get_model("userstories", "RolePoints")
|
||||||
rpqsd = rp_cls.objects.filter(user_story=us)
|
rpqsd = rp_cls.objects.filter(user_story=us)
|
||||||
|
|
|
@ -51,6 +51,7 @@ from .models import HistoryType
|
||||||
from .freeze_impl import project_freezer
|
from .freeze_impl import project_freezer
|
||||||
from .freeze_impl import milestone_freezer
|
from .freeze_impl import milestone_freezer
|
||||||
from .freeze_impl import epic_freezer
|
from .freeze_impl import epic_freezer
|
||||||
|
from .freeze_impl import epic_related_userstory_freezer
|
||||||
from .freeze_impl import userstory_freezer
|
from .freeze_impl import userstory_freezer
|
||||||
from .freeze_impl import issue_freezer
|
from .freeze_impl import issue_freezer
|
||||||
from .freeze_impl import task_freezer
|
from .freeze_impl import task_freezer
|
||||||
|
@ -60,6 +61,7 @@ from .freeze_impl import wikipage_freezer
|
||||||
from .freeze_impl import project_values
|
from .freeze_impl import project_values
|
||||||
from .freeze_impl import milestone_values
|
from .freeze_impl import milestone_values
|
||||||
from .freeze_impl import epic_values
|
from .freeze_impl import epic_values
|
||||||
|
from .freeze_impl import epic_related_userstory_values
|
||||||
from .freeze_impl import userstory_values
|
from .freeze_impl import userstory_values
|
||||||
from .freeze_impl import issue_values
|
from .freeze_impl import issue_values
|
||||||
from .freeze_impl import task_values
|
from .freeze_impl import task_values
|
||||||
|
@ -397,6 +399,7 @@ def prefetch_owners_in_history_queryset(qs):
|
||||||
register_freeze_implementation("projects.project", project_freezer)
|
register_freeze_implementation("projects.project", project_freezer)
|
||||||
register_freeze_implementation("milestones.milestone", milestone_freezer,)
|
register_freeze_implementation("milestones.milestone", milestone_freezer,)
|
||||||
register_freeze_implementation("epics.epic", epic_freezer)
|
register_freeze_implementation("epics.epic", epic_freezer)
|
||||||
|
register_freeze_implementation("epics.relateduserstory", epic_related_userstory_freezer)
|
||||||
register_freeze_implementation("userstories.userstory", userstory_freezer)
|
register_freeze_implementation("userstories.userstory", userstory_freezer)
|
||||||
register_freeze_implementation("issues.issue", issue_freezer)
|
register_freeze_implementation("issues.issue", issue_freezer)
|
||||||
register_freeze_implementation("tasks.task", task_freezer)
|
register_freeze_implementation("tasks.task", task_freezer)
|
||||||
|
@ -405,6 +408,7 @@ register_freeze_implementation("wiki.wikipage", wikipage_freezer)
|
||||||
register_values_implementation("projects.project", project_values)
|
register_values_implementation("projects.project", project_values)
|
||||||
register_values_implementation("milestones.milestone", milestone_values)
|
register_values_implementation("milestones.milestone", milestone_values)
|
||||||
register_values_implementation("epics.epic", epic_values)
|
register_values_implementation("epics.epic", epic_values)
|
||||||
|
register_values_implementation("epics.relateduserstory", epic_related_userstory_values)
|
||||||
register_values_implementation("userstories.userstory", userstory_values)
|
register_values_implementation("userstories.userstory", userstory_values)
|
||||||
register_values_implementation("issues.issue", issue_values)
|
register_values_implementation("issues.issue", issue_values)
|
||||||
register_values_implementation("tasks.task", task_values)
|
register_values_implementation("tasks.task", task_values)
|
||||||
|
|
|
@ -98,7 +98,8 @@ class ProjectExtraInfoSerializerMixin(serializers.LightSerializer):
|
||||||
serialized_project = {
|
serialized_project = {
|
||||||
"name": obj.project.name,
|
"name": obj.project.name,
|
||||||
"slug": obj.project.slug,
|
"slug": obj.project.slug,
|
||||||
"logo_small_url": services.get_logo_small_thumbnail_url(obj.project)
|
"logo_small_url": services.get_logo_small_thumbnail_url(obj.project),
|
||||||
|
"id": obj.project_id
|
||||||
}
|
}
|
||||||
self._serialized_project[obj.project_id] = serialized_project
|
self._serialized_project[obj.project_id] = serialized_project
|
||||||
|
|
||||||
|
|
|
@ -336,6 +336,7 @@ def get_related_people(obj):
|
||||||
related_people = related_people.exclude(is_active=False)
|
related_people = related_people.exclude(is_active=False)
|
||||||
related_people = related_people.exclude(is_system=True)
|
related_people = related_people.exclude(is_system=True)
|
||||||
related_people = related_people.distinct()
|
related_people = related_people.distinct()
|
||||||
|
|
||||||
return related_people
|
return related_people
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,7 @@ def _push_to_timeline(objects, instance: object, event_type: str, created_dateti
|
||||||
@app.task
|
@app.task
|
||||||
def push_to_timelines(project_id, user_id, obj_app_label, obj_model_name, obj_id, event_type,
|
def push_to_timelines(project_id, user_id, obj_app_label, obj_model_name, obj_id, event_type,
|
||||||
created_datetime, extra_data={}):
|
created_datetime, extra_data={}):
|
||||||
|
|
||||||
ObjModel = apps.get_model(obj_app_label, obj_model_name)
|
ObjModel = apps.get_model(obj_app_label, obj_model_name)
|
||||||
try:
|
try:
|
||||||
obj = ObjModel.objects.get(id=obj_id)
|
obj = ObjModel.objects.get(id=obj_id)
|
||||||
|
@ -266,13 +267,25 @@ def extract_epic_info(instance):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def extract_userstory_info(instance):
|
def extract_userstory_info(instance, include_project=False):
|
||||||
return {
|
userstory_info = {
|
||||||
"id": instance.pk,
|
"id": instance.pk,
|
||||||
"ref": instance.ref,
|
"ref": instance.ref,
|
||||||
"subject": instance.subject,
|
"subject": instance.subject,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if include_project:
|
||||||
|
userstory_info["project"] = extract_project_info(instance.project)
|
||||||
|
|
||||||
|
return userstory_info
|
||||||
|
|
||||||
|
|
||||||
|
def extract_related_userstory_info(instance):
|
||||||
|
return {
|
||||||
|
"id": instance.pk,
|
||||||
|
"subject": instance.user_story.subject
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def extract_issue_info(instance):
|
def extract_issue_info(instance):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -55,6 +55,20 @@ def epic_timeline(instance, extra_data={}):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@register_timeline_implementation("epics.relateduserstory", "create")
|
||||||
|
@register_timeline_implementation("epics.relateduserstory", "change")
|
||||||
|
@register_timeline_implementation("epics.relateduserstory", "delete")
|
||||||
|
def epic_related_userstory_timeline(instance, extra_data={}):
|
||||||
|
result = {
|
||||||
|
"relateduserstory": service.extract_related_userstory_info(instance),
|
||||||
|
"epic": service.extract_epic_info(instance.epic),
|
||||||
|
"userstory": service.extract_userstory_info(instance.user_story, include_project=True),
|
||||||
|
"project": service.extract_project_info(instance.project),
|
||||||
|
}
|
||||||
|
result.update(extra_data)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
@register_timeline_implementation("userstories.userstory", "create")
|
@register_timeline_implementation("userstories.userstory", "create")
|
||||||
@register_timeline_implementation("userstories.userstory", "change")
|
@register_timeline_implementation("userstories.userstory", "change")
|
||||||
@register_timeline_implementation("userstories.userstory", "delete")
|
@register_timeline_implementation("userstories.userstory", "delete")
|
||||||
|
|
Loading…
Reference in New Issue