diff --git a/taiga/export_import/dump_service.py b/taiga/export_import/dump_service.py index c3511d80..405e76bc 100644 --- a/taiga/export_import/dump_service.py +++ b/taiga/export_import/dump_service.py @@ -65,6 +65,14 @@ def store_user_stories(project, data): return results +def store_timeline_entries(project, data): + results = [] + for timeline in data.get('timeline', []): + tl = service.store_timeline_entry(project, timeline) + results.append(tl) + return results + + def store_issues(project, data): issues = [] for issue in data.get('issues', []): @@ -167,4 +175,11 @@ def dict_to_project(data, owner=None): store_tags_colors(proj, data) + if service.get_errors(clear=False): + raise TaigaImportError('error importing colors') + + store_timeline_entries(proj, data) + if service.get_errors(clear=False): + raise TaigaImportError('error importing timelines') + return proj diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py index 60b0ad6f..0aca3999 100644 --- a/taiga/export_import/serializers.py +++ b/taiga/export_import/serializers.py @@ -15,6 +15,7 @@ # along with this program. If not, see . import base64 +import copy import os from collections import OrderedDict @@ -39,6 +40,8 @@ from taiga.projects.milestones import models as milestones_models from taiga.projects.wiki import models as wiki_models from taiga.projects.history import models as history_models from taiga.projects.attachments import models as attachments_models +from taiga.timeline import models as timeline_models +from taiga.timeline import service as timeline_service from taiga.users import models as users_models from taiga.projects.votes import services as votes_service from taiga.projects.history import services as history_service @@ -548,6 +551,39 @@ class WikiLinkExportSerializer(serializers.ModelSerializer): exclude = ('id', 'project') + +class TimelineDataField(serializers.WritableField): + read_only = False + + def to_native(self, data): + new_data = copy.deepcopy(data) + try: + user = users_models.User.objects.get(pk=new_data["user"]["id"]) + new_data["user"]["email"] = user.email + del new_data["user"]["id"] + except users_models.User.DoesNotExist: + pass + return new_data + + def from_native(self, data): + new_data = copy.deepcopy(data) + try: + user = users_models.User.objects.get(email=new_data["user"]["email"]) + new_data["user"]["id"] = user.id + del new_data["user"]["email"] + except users_models.User.DoesNotExist: + pass + + return new_data + + +class TimelineExportSerializer(serializers.ModelSerializer): + data = TimelineDataField() + class Meta: + model = timeline_models.Timeline + exclude = ('id', 'project', 'namespace', 'object_id') + + class ProjectExportSerializer(serializers.ModelSerializer): owner = UserRelatedField(required=False) default_points = serializers.SlugRelatedField(slug_field="name", required=False) @@ -579,7 +615,12 @@ class ProjectExportSerializer(serializers.ModelSerializer): anon_permissions = PgArrayField(required=False) public_permissions = PgArrayField(required=False) modified_date = serializers.DateTimeField(required=False) + timeline = serializers.SerializerMethodField("get_timeline") class Meta: model = projects_models.Project exclude = ('id', 'creation_template', 'members') + + def get_timeline(self, obj): + timeline_qs = timeline_service.get_project_timeline(obj) + return TimelineExportSerializer(timeline_qs, many=True).data diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py index 79eeee8b..df15684e 100644 --- a/taiga/export_import/service.py +++ b/taiga/export_import/service.py @@ -23,6 +23,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from taiga.projects.history.services import make_key_from_model_object +from taiga.timeline.service import build_project_namespace from taiga.projects.references import sequences as seq from taiga.projects.references import models as refs from taiga.projects.services import find_invited_user @@ -275,6 +276,19 @@ def store_attachment(project, obj, attachment): return serialized +def store_timeline_entry(project, timeline): + serialized = serializers.TimelineExportSerializer(data=timeline, context={"project": project}) + if serialized.is_valid(): + serialized.object.project = project + serialized.object.namespace = build_project_namespace(project) + serialized.object.object_id = project.id + serialized.object._importing = True + serialized.save() + return serialized + add_errors("timeline", serialized.errors) + return serialized + + def store_history(project, obj, history): serialized = serializers.HistoryExportSerializer(data=history, context={"project": project}) if serialized.is_valid(): diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index 468eb439..c8b38823 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -76,6 +76,8 @@ class HistoryEntry(models.Model): # snapshot. The rest are partial snapshot. is_snapshot = models.BooleanField(default=False) + _importing = None + @cached_property def is_change(self): return self.type == HistoryType.change diff --git a/taiga/timeline/models.py b/taiga/timeline/models.py index 40f46899..52882850 100644 --- a/taiga/timeline/models.py +++ b/taiga/timeline/models.py @@ -29,8 +29,8 @@ class Timeline(models.Model): content_type = models.ForeignKey(ContentType, related_name="content_type_timelines") object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') - namespace = models.SlugField(default="default") - event_type = models.SlugField() + namespace = models.CharField(max_length=250, default="default", db_index=True) + event_type = models.CharField(max_length=250, db_index=True) project = models.ForeignKey(Project) data = JsonField() data_content_type = models.ForeignKey(ContentType, related_name="data_timelines") diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index 27de0569..7f88dde3 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -71,6 +71,9 @@ def _push_to_timelines(project, user, obj, event_type, extra_data={}): def on_new_history_entry(sender, instance, created, **kwargs): + if instance._importing: + return + if instance.is_hidden: return None