From 1e48340c48cf1910a222c4b1c135b1870d25cdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 21 Aug 2014 16:05:49 +0200 Subject: [PATCH] Adding initial version of exporter/importer --- settings/common.py | 1 + taiga/export_import/__init__.py | 0 taiga/export_import/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/dump_project.py | 22 ++ .../management/commands/load_dump.py | 18 + taiga/export_import/models.py | 0 taiga/export_import/renderers.py | 4 + taiga/export_import/serializers.py | 315 ++++++++++++++++++ taiga/export_import/service.py | 196 +++++++++++ ...fied_date__chg_field_attachment_created.py | 211 ++++++++++++ taiga/projects/attachments/models.py | 14 +- ...auto__chg_field_historyentry_created_at.py | 37 ++ taiga/projects/history/models.py | 3 +- ...date__chg_field_issue_subject__chg_fiel.py | 235 +++++++++++++ taiga/projects/issues/models.py | 14 +- taiga/projects/issues/serializers.py | 1 + ...ted_at__chg_field_project_created_date_.py | 208 ++++++++++++ ...__chg_field_milestone_created_date__chg.py | 218 ++++++++++++ taiga/projects/milestones/models.py | 15 +- taiga/projects/milestones/serializers.py | 1 + taiga/projects/models.py | 23 +- .../notifications/migrations/0001_initial.py | 207 ++++++++++++ .../notifications/migrations/__init__.py | 0 taiga/projects/notifications/models.py | 16 +- ...05_auto__chg_field_reference_created_at.py | 200 +++++++++++ taiga/projects/references/models.py | 3 +- ...hg_field_task_modified_date__chg_field_.py | 291 ++++++++++++++++ taiga/projects/tasks/models.py | 14 +- taiga/projects/tasks/serializers.py | 3 +- ...ct__chg_field_userstory_modified_date__.py | 269 +++++++++++++++ taiga/projects/userstories/models.py | 15 +- taiga/projects/userstories/serializers.py | 1 + ...d_date__chg_field_wikipage_modified_dat.py | 211 ++++++++++++ taiga/projects/wiki/models.py | 15 +- taiga/projects/wiki/serializers.py | 1 + .../0002_auto__chg_field_timeline_created.py | 40 +++ taiga/timeline/models.py | 3 +- 38 files changed, 2790 insertions(+), 35 deletions(-) create mode 100644 taiga/export_import/__init__.py create mode 100644 taiga/export_import/management/__init__.py create mode 100644 taiga/export_import/management/commands/__init__.py create mode 100644 taiga/export_import/management/commands/dump_project.py create mode 100644 taiga/export_import/management/commands/load_dump.py create mode 100644 taiga/export_import/models.py create mode 100644 taiga/export_import/renderers.py create mode 100644 taiga/export_import/serializers.py create mode 100644 taiga/export_import/service.py create mode 100644 taiga/projects/attachments/migrations/0002_auto__chg_field_attachment_modified_date__chg_field_attachment_created.py create mode 100644 taiga/projects/history/migrations/0002_auto__chg_field_historyentry_created_at.py create mode 100644 taiga/projects/issues/migrations/0009_auto__chg_field_issue_modified_date__chg_field_issue_subject__chg_fiel.py create mode 100644 taiga/projects/migrations/0034_auto__chg_field_membership_created_at__chg_field_project_created_date_.py create mode 100644 taiga/projects/milestones/migrations/0003_auto__del_unique_milestone_slug__chg_field_milestone_created_date__chg.py create mode 100644 taiga/projects/notifications/migrations/0001_initial.py create mode 100644 taiga/projects/notifications/migrations/__init__.py create mode 100644 taiga/projects/references/migrations/0005_auto__chg_field_reference_created_at.py create mode 100644 taiga/projects/tasks/migrations/0009_auto__chg_field_task_subject__chg_field_task_modified_date__chg_field_.py create mode 100644 taiga/projects/userstories/migrations/0015_auto__chg_field_userstory_subject__chg_field_userstory_modified_date__.py create mode 100644 taiga/projects/wiki/migrations/0005_auto__chg_field_wikipage_created_date__chg_field_wikipage_modified_dat.py create mode 100644 taiga/timeline/migrations/0002_auto__chg_field_timeline_created.py diff --git a/settings/common.py b/settings/common.py index 10755d73..c7cebab0 100644 --- a/settings/common.py +++ b/settings/common.py @@ -184,6 +184,7 @@ INSTALLED_APPS = [ "taiga.searches", "taiga.timeline", "taiga.mdrender", + "taiga.export_import", "south", "reversion", diff --git a/taiga/export_import/__init__.py b/taiga/export_import/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/export_import/management/__init__.py b/taiga/export_import/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/export_import/management/commands/__init__.py b/taiga/export_import/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/export_import/management/commands/dump_project.py b/taiga/export_import/management/commands/dump_project.py new file mode 100644 index 00000000..89164516 --- /dev/null +++ b/taiga/export_import/management/commands/dump_project.py @@ -0,0 +1,22 @@ +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option + +from taiga.projects.models import Project +from taiga.export_import.renderers import ExportRenderer +from taiga.export_import.service import project_to_dict + +class Command(BaseCommand): + args = '' + help = 'Export a project to json' + renderer_context = {"indent": 4} + renderer = ExportRenderer() + + def handle(self, *args, **options): + for project_slug in args: + try: + project = Project.objects.get(slug=project_slug) + except Project.DoesNotExist: + raise CommandError('Project "%s" does not exist' % project_slug) + + data = project_to_dict(project) + print(self.renderer.render(data, renderer_context=self.renderer_context).decode('utf-8')) diff --git a/taiga/export_import/management/commands/load_dump.py b/taiga/export_import/management/commands/load_dump.py new file mode 100644 index 00000000..857f30b1 --- /dev/null +++ b/taiga/export_import/management/commands/load_dump.py @@ -0,0 +1,18 @@ +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option + +import json + +from taiga.projects.models import Project +from taiga.export_import.renderers import ExportRenderer +from taiga.export_import.service import dict_to_project + +class Command(BaseCommand): + args = ' ' + help = 'Export a project to json' + renderer_context = {"indent": 4} + renderer = ExportRenderer() + + def handle(self, *args, **options): + data = json.loads(open(args[0], 'r').read()) + dict_to_project(data, args[1]) diff --git a/taiga/export_import/models.py b/taiga/export_import/models.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/export_import/renderers.py b/taiga/export_import/renderers.py new file mode 100644 index 00000000..265c078d --- /dev/null +++ b/taiga/export_import/renderers.py @@ -0,0 +1,4 @@ +from rest_framework.renderers import UnicodeJSONRenderer + +class ExportRenderer(UnicodeJSONRenderer): + pass diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py new file mode 100644 index 00000000..ad0cb6dc --- /dev/null +++ b/taiga/export_import/serializers.py @@ -0,0 +1,315 @@ +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino +# Copyright (C) 2014 David Barragán +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.contrib.contenttypes.models import ContentType +from django.core.files.base import ContentFile + +from rest_framework import serializers + +import json +import base64 +import os +import io +from collections import OrderedDict + +from taiga.projects import models as projects_models +from taiga.projects.userstories import models as userstories_models +from taiga.projects.tasks import models as tasks_models +from taiga.projects.issues import models as issues_models +from taiga.projects.milestones import models as milestones_models +from taiga.projects.wiki import models as wiki_models +from taiga.projects.votes import models as votes_models +from taiga.projects.notifications import models as notifications_models +from taiga.projects.history import models as history_models +from taiga.projects.attachments import models as attachments_models +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 +from taiga.base.serializers import JsonField, PgArrayField + +class AttachedFileField(serializers.WritableField): + read_only = False + + def to_native(self, obj): + if not obj: + return None + + return OrderedDict([ + ("data", base64.b64encode(obj.read()).decode('utf-8')), + ("name", os.path.basename(obj.name)), + ]) + + def from_native(self, data): + if not data: + return None + return ContentFile(base64.b64decode(data['data']), name=data['name']) + + +class UserRelatedField(serializers.RelatedField): + read_only = False + + def to_native(self, obj): + if obj: + return obj.email + return None + + def from_native(self, data): + try: + return users_models.User.objects.get(email=data) + except users_models.User.DoesNotExist: + return None + +class ProjectRelatedField(serializers.RelatedField): + read_only = False + + def __init__(self, slug_field, *args, **kwargs): + self.slug_field = slug_field + super().__init__(*args, **kwargs) + + def to_native(self, obj): + if obj: + return getattr(obj, self.slug_field) + return None + + def from_native(self, data): + try: + kwargs = {self.slug_field: data, "project": self.context['project']} + return self.queryset.get(**kwargs) + except self.parent.opts.model.DoesNotExist: + return None + + +class PointsExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.Points + exclude = ('id', 'project') + +class UserStoryStatusExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.UserStoryStatus + exclude = ('id', 'project') + +class TaskStatusExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.TaskStatus + exclude = ('id', 'project') + +class IssueStatusExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.IssueStatus + exclude = ('id', 'project') + +class PriorityExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.Priority + exclude = ('id', 'project') + +class SeverityExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.Severity + exclude = ('id', 'project') + +class IssueTypeExportSerializer(serializers.ModelSerializer): + class Meta: + model = projects_models.IssueType + exclude = ('id', 'project') + +class RoleExportSerializer(serializers.ModelSerializer): + permissions = PgArrayField(required=False) + + class Meta: + model = users_models.Role + exclude = ('id', 'project') + +class MembershipExportSerializer(serializers.ModelSerializer): + user = UserRelatedField(required=False) + role = ProjectRelatedField(slug_field="slug") + + class Meta: + model = projects_models.Membership + exclude = ('id', 'project') + + def full_clean(self, instance): + return instance + + +class RolePointsExportSerializer(serializers.ModelSerializer): + role = ProjectRelatedField(slug_field="slug") + points = ProjectRelatedField(slug_field="name") + + class Meta: + model = userstories_models.RolePoints + exclude = ('id', 'user_story') + +class MilestoneExportSerializer(serializers.ModelSerializer): + owner = UserRelatedField(required=False) + watchers = UserRelatedField(many=True, required=False) + tasks_without_us = serializers.SerializerMethodField("get_tasks_without_us") + + class Meta: + model = milestones_models.Milestone + exclude = ('id', 'project') + + def get_tasks_without_us(self, obj): + queryset = tasks_models.Task.objects.filter(milestone=obj, user_story__isnull=True) + return TaskExportSerializer(queryset.order_by('ref'), many=True).data + +class AttachmentExportSerializer(serializers.ModelSerializer): + owner = UserRelatedField() + attached_file = AttachedFileField() + + class Meta: + model = attachments_models.Attachment + exclude = ('id', 'content_type', 'object_id', 'project') + +class AttachmentExportSerializerMixin(serializers.ModelSerializer): + attachments = serializers.SerializerMethodField("get_attachments") + + def get_attachments(self, obj): + content_type = ContentType.objects.get_for_model(obj.__class__) + attachments_qs = attachments_models.Attachment.objects.filter(object_id=obj.pk, content_type=content_type) + return AttachmentExportSerializer(attachments_qs, many=True).data + +class TaskExportSerializer(AttachmentExportSerializerMixin, serializers.ModelSerializer): + owner = UserRelatedField(required=False) + status = ProjectRelatedField(slug_field="name", required=False) + milestone = ProjectRelatedField(slug_field="slug", required=False) + assigned_to = UserRelatedField(required=False) + watchers = UserRelatedField(many=True, required=False) + + class Meta: + model = tasks_models.Task + exclude = ('id', 'project', 'user_story') + +class UserStoryExportSerializer(AttachmentExportSerializerMixin, serializers.ModelSerializer): + role_points = RolePointsExportSerializer(many=True, required=False) + generated_from_issue = ProjectRelatedField(slug_field="ref", required=False) + owner = UserRelatedField(required=False) + status = ProjectRelatedField(slug_field="name", required=False) + tasks = TaskExportSerializer(many=True, required=False) + milestone = ProjectRelatedField(slug_field="slug", required=False) + watchers = UserRelatedField(many=True, required=False) + + class Meta: + model = userstories_models.UserStory + exclude = ('id', 'project', 'points') + +def _convert_user(user_pk): + try: + user = users_models.User.objects.get(pk=user_pk) + except users_models.User.DoesNotExist: + return "#imported#{}".format(user_pk) + return user.email + +def _convert_user_tuple(user_tuple): + return (_convert_user(user_tuple[0]), user_tuple[1]) + +class HistoryExportSerializer(serializers.ModelSerializer): + user = serializers.SerializerMethodField("get_user") + diff = serializers.SerializerMethodField("get_diff") + snapshot = JsonField() + values = serializers.SerializerMethodField("get_values") + + def get_user(self, obj): + return (_convert_user(obj.user['pk']), obj.user['name']) + + def get_values(self, obj): + for key, value in obj.values.items(): + if key == "users": + obj.values["users"] = dict(map(_convert_user_tuple, value.items())) + + return obj.values + + def get_diff(self, obj): + for key, value in obj.diff.items(): + if key == "assigned_to": + obj.diff["assigned_to"] = map(_convert_user, value) + + return obj.diff + + class Meta: + model = history_models.HistoryEntry + +class HistoryExportSerializerMixin(serializers.ModelSerializer): + history = serializers.SerializerMethodField("get_history") + + def get_history(self, obj): + history_qs = history_service.get_history_queryset_by_model_instance(obj) + return HistoryExportSerializer(history_qs, many=True).data + + +class IssueExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin, serializers.ModelSerializer): + owner = UserRelatedField(required=False) + status = ProjectRelatedField(slug_field="name", required=False) + assigned_to = UserRelatedField(required=False) + priority = ProjectRelatedField(slug_field="name", required=False) + severity = ProjectRelatedField(slug_field="name", required=False) + type = ProjectRelatedField(slug_field="name", required=False) + milestone = ProjectRelatedField(slug_field="slug", required=False) + watchers = UserRelatedField(many=True, required=False) + votes = serializers.SerializerMethodField("get_votes") + + def get_votes(self, obj): + return [x.email for x in votes_service.get_voters(obj)] + + class Meta: + model = issues_models.Issue + exclude = ('id', 'project') + +class WikiPageExportSerializer(AttachmentExportSerializerMixin, serializers.ModelSerializer): + owner = UserRelatedField(required=False) + last_modifier = UserRelatedField(required=False) + watchers = UserRelatedField(many=True, required=False) + + class Meta: + model = wiki_models.WikiPage + exclude = ('id', 'project') + +class WikiLinkExportSerializer(serializers.ModelSerializer): + class Meta: + model = wiki_models.WikiLink + exclude = ('id', 'project') + +class ProjectExportSerializer(serializers.ModelSerializer): + owner = UserRelatedField() + default_points = serializers.SlugRelatedField(slug_field="name", required=False) + default_us_status = serializers.SlugRelatedField(slug_field="name", required=False) + default_task_status = serializers.SlugRelatedField(slug_field="name", required=False) + default_priority = serializers.SlugRelatedField(slug_field="name", required=False) + default_severity = serializers.SlugRelatedField(slug_field="name", required=False) + default_issue_status = serializers.SlugRelatedField(slug_field="name", required=False) + default_issue_type = serializers.SlugRelatedField(slug_field="name", required=False) + memberships = MembershipExportSerializer(many=True, required=False) + points = PointsExportSerializer(many=True, required=False) + us_statuses = UserStoryStatusExportSerializer(many=True, required=False) + task_statuses = TaskStatusExportSerializer(many=True, required=False) + issue_statuses = IssueStatusExportSerializer(many=True, required=False) + priorities = PriorityExportSerializer(many=True, required=False) + severities = SeverityExportSerializer(many=True, required=False) + issue_types = IssueTypeExportSerializer(many=True, required=False) + roles = RoleExportSerializer(many=True, required=False) + milestones = MilestoneExportSerializer(many=True, required=False) + wiki_pages = WikiPageExportSerializer(many=True, required=False) + wiki_links = WikiLinkExportSerializer(many=True, required=False) + user_stories = UserStoryExportSerializer(many=True, required=False) + issues = IssueExportSerializer(many=True, required=False) + tags_colors = JsonField(required=False) + anon_permissions = PgArrayField(required=False) + public_permissions = PgArrayField(required=False) + + class Meta: + model = projects_models.Project + exclude = ('id', 'creation_template', 'members') diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py new file mode 100644 index 00000000..92b75c91 --- /dev/null +++ b/taiga/export_import/service.py @@ -0,0 +1,196 @@ +from . import serializers +from taiga.projects.models import Project +from django.db.models import signals +from django.db import transaction +from django.contrib.contenttypes.models import ContentType + +def project_to_dict(project): + return serializers.ProjectExportSerializer(project).data + +def dict_to_project(data, owner=None): + signals.pre_save.receivers = [] + signals.post_save.receivers = [] + signals.pre_delete.receivers = [] + signals.post_delete.receivers = [] + + @transaction.atomic + def store_project(data): + project_data = {} + for key, value in data.items(): + excluded_fields = [ + "default_points", "default_us_status", "default_task_status", + "default_priority", "default_severity", "default_issue_status", + "default_issue_type", "memberships", "points", "us_statuses", + "task_statuses", "issue_statuses", "priorities", "severities", + "issue_types", "roles", "milestones", "wiki_pages", + "wiki_links", "notify_policies", "user_stories", "issues" + ] + if key not in excluded_fields: + project_data[key] = value + + serialized = serializers.ProjectExportSerializer(data=project_data) + serialized.is_valid() + serialized.object._importing = True + serialized.object.save() + return serialized.object + + @transaction.atomic + def store_choices(project, data, field, relation, serializer, default_field): + relation.all().delete() + + for point in data[field]: + serialized = serializer(data=point) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + @transaction.atomic + def store_default_choices(project, data): + project.default_points = project.points.all().get(name=data['default_points']) + project.default_issue_type = project.issue_types.get(name=data['default_issue_type']) + project.default_issue_status = project.issue_statuses.get(name=data['default_issue_status']) + project.default_us_status = project.us_statuses.get(name=data['default_us_status']) + project.default_task_status = project.task_statuses.get(name=data['default_task_status']) + project.default_priority = project.priorities.get(name=data['default_priority']) + project.default_severity = project.severities.get(name=data['default_severity']) + project._importing = True + project.save() + + @transaction.atomic + def store_roles(project, data): + project.roles.all().delete() + for role in data['roles']: + serialized = serializers.RoleExportSerializer(data=role) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + @transaction.atomic + def store_memberships(project, data): + for membership in data['memberships']: + serialized = serializers.MembershipExportSerializer(data=membership, context={"project": project}) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + @transaction.atomic + def store_task(project, us, task): + serialized = serializers.TaskExportSerializer(data=task, context={"project": project}) + serialized.is_valid() + serialized.object.user_story = us + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + for task_attachment in task['attachments']: + store_attachment(project, serialized.object, task_attachment) + + @transaction.atomic + def store_milestones(project, data): + for milestone in data['milestones']: + serialized = serializers.MilestoneExportSerializer(data=milestone) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + for task_without_us in milestone['tasks_without_us']: + store_task(project, None, task_without_us) + + def store_attachment(project, obj, attachment): + serialized = serializers.AttachmentExportSerializer(data=attachment) + serialized.is_valid() + serialized.object.content_type = ContentType.objects.get_for_model(obj.__class__) + serialized.object.object_id = obj.id + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + @transaction.atomic + def store_wiki_pages(project, data): + for wiki_page in data['wiki_pages']: + serialized = serializers.WikiPageExportSerializer(data=wiki_page) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + for attachment in wiki_page['attachments']: + store_attachment(project, serialized.object, attachment) + + @transaction.atomic + def store_wiki_links(project, data): + for wiki_link in data['wiki_links']: + serialized = serializers.WikiLinkExportSerializer(data=wiki_link) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + @transaction.atomic + def store_role_point(project, us, role_point): + serialized = serializers.RolePointsExportSerializer(data=role_point, context={"project": project} ) + serialized.is_valid() + serialized.object.user_story = us + serialized.save() + + @transaction.atomic + def store_user_stories(project, data): + for userstory in data['user_stories']: + userstory_data = {} + for key, value in userstory.items(): + excluded_fields = [ + 'tasks', 'role_points' + ] + if key not in excluded_fields: + userstory_data[key] = value + serialized_us = serializers.UserStoryExportSerializer(data=userstory_data, context={"project": project}) + serialized_us.is_valid() + serialized_us.object.project = project + serialized_us.object._importing = True + serialized_us.save() + + for task in userstory['tasks']: + store_task(project, serialized_us.object, task) + + for us_attachment in userstory['attachments']: + store_attachment(project, serialized_us.object, us_attachment) + + for role_point in userstory['role_points']: + store_role_point(project, serialized_us.object, role_point) + + @transaction.atomic + def store_issues(project, data): + for issue in data['issues']: + serialized = serializers.IssueExportSerializer(data=issue, context={"project": project}) + serialized.is_valid() + serialized.object.project = project + serialized.object._importing = True + serialized.save() + + for attachment in issue['attachments']: + store_attachment(project, serialized.object, attachment) + + if owner: + data['owner'] = owner + + project = store_project(data) + store_choices(project, data, "points", project.points, serializers.PointsExportSerializer, "default_points") + store_choices(project, data, "issue_types", project.issue_types, serializers.IssueTypeExportSerializer, "default_issue_type") + store_choices(project, data, "issue_statuses", project.issue_statuses, serializers.IssueStatusExportSerializer, "default_issue_status") + store_choices(project, data, "us_statuses", project.us_statuses, serializers.UserStoryStatusExportSerializer, "default_us_status") + store_choices(project, data, "task_statuses", project.task_statuses, serializers.TaskStatusExportSerializer, "default_task_status") + store_choices(project, data, "priorities", project.priorities, serializers.PriorityExportSerializer, "default_priority") + store_choices(project, data, "severities", project.severities, serializers.SeverityExportSerializer, "default_severity") + store_default_choices(project, data) + store_roles(project, data) + store_memberships(project, data) + store_milestones(project, data) + store_wiki_pages(project, data) + store_wiki_links(project, data) + + store_user_stories(project, data) + store_issues(project, data) diff --git a/taiga/projects/attachments/migrations/0002_auto__chg_field_attachment_modified_date__chg_field_attachment_created.py b/taiga/projects/attachments/migrations/0002_auto__chg_field_attachment_modified_date__chg_field_attachment_created.py new file mode 100644 index 00000000..a8edcdc3 --- /dev/null +++ b/taiga/projects/attachments/migrations/0002_auto__chg_field_attachment_modified_date__chg_field_attachment_created.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Attachment.modified_date' + db.alter_column('attachments_attachment', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'Attachment.created_date' + db.alter_column('attachments_attachment', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'Attachment.modified_date' + db.alter_column('attachments_attachment', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + # Changing field 'Attachment.created_date' + db.alter_column('attachments_attachment', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'attachments.attachment': { + 'Meta': {'ordering': "['project', 'created_date']", 'object_name': 'Attachment'}, + 'attached_file': ('django.db.models.fields.files.FileField', [], {'max_length': '500', 'blank': 'True', 'null': 'True'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_deprecated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'change_attachments'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'attachments'"}) + }, + 'contenttypes.contenttype': { + 'Meta': {'db_table': "'django_content_type'", 'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'object_name': 'ContentType'}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'projects.issuestatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"}) + }, + 'projects.issuetype': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueType'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"}) + }, + 'projects.membership': { + 'Meta': {'unique_together': "(('user', 'project'),)", 'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']", 'object_name': 'Membership'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '255', 'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '60', 'blank': 'True', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'null': 'True', 'blank': 'True', 'related_name': "'memberships'", 'to': "orm['users.User']"}) + }, + 'projects.points': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Points'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}), + 'value': ('django.db.models.fields.FloatField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) + }, + 'projects.priority': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Priority'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'default': '[]', 'null': 'True', 'blank': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'null': 'True', 'blank': 'True', 'related_name': "'projects'", 'to': "orm['projects.ProjectTemplate']"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.IssueStatus']", 'related_name': "'+'"}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.IssueType']", 'related_name': "'+'"}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Points']", 'related_name': "'+'"}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Priority']", 'related_name': "'+'"}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Severity']", 'related_name': "'+'"}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.TaskStatus']", 'related_name': "'+'"}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.UserStoryStatus']", 'related_name': "'+'"}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['users.User']", 'related_name': "'projects'", 'through': "orm['projects.Membership']"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'default': '[]', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True', 'unique': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'default': 'None', 'null': 'True', 'blank': 'True'}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'default': '[]', 'dimension': '2', 'blank': 'True'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True', 'null': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'ordering': "['name']", 'object_name': 'ProjectTemplate'}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True', 'unique': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True', 'null': 'True'}) + }, + 'projects.severity': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Severity'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"}) + }, + 'projects.taskstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'TaskStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"}) + }, + 'projects.userstorystatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'UserStoryStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) + }, + 'users.role': { + 'Meta': {'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']", 'object_name': 'Role'}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'default': '[]', 'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'}) + }, + 'users.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'bio': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'color': ('django.db.models.fields.CharField', [], {'default': "'#b3bbb6'", 'max_length': '9', 'blank': 'True'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'blank': 'True', 'null': 'True'}), + 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True', 'null': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'max_length': '500', 'blank': 'True', 'null': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'blank': 'True', 'null': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + } + } + + complete_apps = ['attachments'] \ No newline at end of file diff --git a/taiga/projects/attachments/models.py b/taiga/projects/attachments/models.py index 55a00d74..a063c24c 100644 --- a/taiga/projects/attachments/models.py +++ b/taiga/projects/attachments/models.py @@ -46,9 +46,10 @@ class Attachment(models.Model): object_id = models.PositiveIntegerField(null=False, blank=False, verbose_name=_("object id")) content_object = generic.GenericForeignKey("content_type", "object_id") - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) attached_file = models.FileField(max_length=500, null=True, blank=True, @@ -57,6 +58,7 @@ class Attachment(models.Model): is_deprecated = models.BooleanField(default=False, verbose_name=_("is deprecated")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) order = models.IntegerField(default=0, null=False, blank=False, verbose_name=_("order")) + _importing = None class Meta: verbose_name = "attachment" @@ -66,5 +68,11 @@ class Attachment(models.Model): ("view_attachment", "Can view attachment"), ) + def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() + + return super().save(*args, **kwargs) + def __str__(self): return "Attachment: {}".format(self.id) diff --git a/taiga/projects/history/migrations/0002_auto__chg_field_historyentry_created_at.py b/taiga/projects/history/migrations/0002_auto__chg_field_historyentry_created_at.py new file mode 100644 index 00000000..23d00999 --- /dev/null +++ b/taiga/projects/history/migrations/0002_auto__chg_field_historyentry_created_at.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'HistoryEntry.created_at' + db.alter_column('history_historyentry', 'created_at', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'HistoryEntry.created_at' + db.alter_column('history_historyentry', 'created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'history.historyentry': { + 'Meta': {'ordering': "['created_at']", 'object_name': 'HistoryEntry'}, + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'comment_html': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'diff': ('django_pgjson.fields.JsonField', [], {'default': 'None', 'blank': 'False'}), + 'id': ('django.db.models.fields.CharField', [], {'primary_key': 'True', 'default': "'9571feee-2ebf-11e4-8a54-1c75086d5bff'", 'unique': 'True', 'max_length': '255'}), + 'is_snapshot': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'default': 'None', 'null': 'True', 'blank': 'True'}), + 'snapshot': ('django_pgjson.fields.JsonField', [], {'default': 'None', 'blank': 'False'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'user': ('django_pgjson.fields.JsonField', [], {'default': 'None'}), + 'values': ('django_pgjson.fields.JsonField', [], {'default': 'None', 'blank': 'False'}) + } + } + + complete_apps = ['history'] \ No newline at end of file diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index 746aa409..ff344b2f 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -15,6 +15,7 @@ import uuid from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from django.db import models from django.db.models.loading import get_model from django.utils.functional import cached_property @@ -36,7 +37,7 @@ class HistoryEntry(models.Model): editable=False, default=lambda: str(uuid.uuid1())) user = JsonField(blank=True, default=None, null=True) - created_at = models.DateTimeField(auto_now_add=True) + created_at = models.DateTimeField(default=timezone.now) type = models.SmallIntegerField(choices=HISTORY_TYPE_CHOICES) is_snapshot = models.BooleanField(default=False) diff --git a/taiga/projects/issues/migrations/0009_auto__chg_field_issue_modified_date__chg_field_issue_subject__chg_fiel.py b/taiga/projects/issues/migrations/0009_auto__chg_field_issue_modified_date__chg_field_issue_subject__chg_fiel.py new file mode 100644 index 00000000..47fa0ffb --- /dev/null +++ b/taiga/projects/issues/migrations/0009_auto__chg_field_issue_modified_date__chg_field_issue_subject__chg_fiel.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Issue.modified_date' + db.alter_column('issues_issue', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'Issue.subject' + db.alter_column('issues_issue', 'subject', self.gf('django.db.models.fields.TextField')()) + + # Changing field 'Issue.created_date' + db.alter_column('issues_issue', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'Issue.modified_date' + db.alter_column('issues_issue', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + # Changing field 'Issue.subject' + db.alter_column('issues_issue', 'subject', self.gf('django.db.models.fields.CharField')(max_length=500)) + + # Changing field 'Issue.created_date' + db.alter_column('issues_issue', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'issues.issue': { + 'Meta': {'object_name': 'Issue', 'ordering': "['project', '-created_date']"}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'related_name': "'issues_assigned_to_me'", 'null': 'True', 'default': 'None'}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['milestones.Milestone']", 'related_name': "'issues'", 'null': 'True', 'default': 'None'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'related_name': "'owned_issues'", 'null': 'True', 'default': 'None'}), + 'priority': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Priority']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'blank': 'True', 'null': 'True', 'db_index': 'True', 'default': 'None'}), + 'severity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Severity']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.IssueStatus']"}), + 'subject': ('django.db.models.fields.TextField', [], {}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'null': 'True', 'default': 'None'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.IssueType']"}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['users.User']", 'related_name': "'issues_issue+'", 'null': 'True', 'symmetrical': 'False'}) + }, + 'milestones.milestone': { + 'Meta': {'object_name': 'Milestone', 'unique_together': "[('name', 'project'), ('slug', 'project')]", 'ordering': "['project', 'created_date']"}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': '0.0'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {}), + 'estimated_start': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'related_name': "'owned_milestones'", 'null': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'milestones'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['users.User']", 'related_name': "'milestones_milestone+'", 'null': 'True', 'symmetrical': 'False'}) + }, + 'projects.issuestatus': { + 'Meta': {'object_name': 'IssueStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'object_name': 'IssueType', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'object_name': 'Membership', 'unique_together': "(('user', 'project'),)", 'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '255', 'null': 'True', 'default': 'None'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '60', 'null': 'True', 'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'related_name': "'memberships'", 'null': 'True', 'default': 'None'}) + }, + 'projects.points': { + 'Meta': {'object_name': 'Points', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'projects.priority': { + 'Meta': {'object_name': 'Priority', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'null': 'True', 'default': '[]'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['projects.ProjectTemplate']", 'related_name': "'projects'", 'null': 'True', 'default': 'None'}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.IssueStatus']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.IssueType']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Points']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Priority']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Severity']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.TaskStatus']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.UserStoryStatus']", 'unique': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'through': "orm['projects.Membership']", 'related_name': "'projects'", 'to': "orm['users.User']", 'symmetrical': 'False'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'null': 'True', 'default': '[]'}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'null': 'True', 'default': 'None'}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dimension': '2', 'dbtype': "'text'", 'default': '[]'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': '0'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'object_name': 'ProjectTemplate', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}) + }, + 'projects.severity': { + 'Meta': {'object_name': 'Severity', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'object_name': 'TaskStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'object_name': 'UserStoryStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'users.role': { + 'Meta': {'object_name': 'Role', 'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'null': 'True', 'default': '[]'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'bio': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '9', 'default': "'#a294a6'"}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'email_token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'null': 'True', 'default': 'None'}), + 'full_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '256'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75', 'null': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '500', 'null': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'null': 'True', 'default': 'None'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + } + } + + complete_apps = ['issues'] \ No newline at end of file diff --git a/taiga/projects/issues/models.py b/taiga/projects/issues/models.py index 21675d1c..8c82e6ae 100644 --- a/taiga/projects/issues/models.py +++ b/taiga/projects/issues/models.py @@ -47,9 +47,10 @@ class Issue(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models. verbose_name=_("milestone")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="issues", verbose_name=_("project")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) finished_date = models.DateTimeField(null=True, blank=True, verbose_name=_("finished date")) @@ -60,6 +61,7 @@ class Issue(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models. default=None, related_name="issues_assigned_to_me", verbose_name=_("assigned to")) attachments = generic.GenericRelation("attachments.Attachment") + _importing = None class Meta: verbose_name = "issue" @@ -70,6 +72,12 @@ class Issue(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models. ("view_issue", "Can view issue"), ) + def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() + + return super().save(*args, **kwargs) + def __str__(self): return "({1}) {0}".format(self.ref, self.subject) diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index 13055010..1a270ee5 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -34,6 +34,7 @@ class IssueSerializer(serializers.ModelSerializer): class Meta: model = models.Issue + read_only_fields = ('id', 'ref', 'created_date', 'modified_date') def get_comment(self, obj): # NOTE: This method and field is necessary to historical comments work diff --git a/taiga/projects/migrations/0034_auto__chg_field_membership_created_at__chg_field_project_created_date_.py b/taiga/projects/migrations/0034_auto__chg_field_membership_created_at__chg_field_project_created_date_.py new file mode 100644 index 00000000..4d69a08e --- /dev/null +++ b/taiga/projects/migrations/0034_auto__chg_field_membership_created_at__chg_field_project_created_date_.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Membership.created_at' + db.alter_column('projects_membership', 'created_at', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'Project.created_date' + db.alter_column('projects_project', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'Project.modified_date' + db.alter_column('projects_project', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'ProjectTemplate.created_date' + db.alter_column('projects_projecttemplate', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'ProjectTemplate.modified_date' + db.alter_column('projects_projecttemplate', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'Membership.created_at' + db.alter_column('projects_membership', 'created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + # Changing field 'Project.created_date' + db.alter_column('projects_project', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + # Changing field 'Project.modified_date' + db.alter_column('projects_project', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + # Changing field 'ProjectTemplate.created_date' + db.alter_column('projects_projecttemplate', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + # Changing field 'ProjectTemplate.modified_date' + db.alter_column('projects_projecttemplate', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + models = { + 'projects.issuestatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'IssueStatus', 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'IssueType', 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']", 'object_name': 'Membership', 'unique_together': "(('user', 'project'),)"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'null': 'True', 'blank': 'True', 'max_length': '255', 'default': 'None'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '60', 'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'memberships'", 'default': 'None'}) + }, + 'projects.points': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'Points', 'unique_together': "(('project', 'name'),)"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True', 'default': 'None'}) + }, + 'projects.priority': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'Priority', 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'blank': 'True', 'default': '[]', 'null': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'blank': 'True', 'to': "orm['projects.ProjectTemplate']", 'related_name': "'projects'", 'default': 'None'}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.IssueStatus']", 'related_name': "'+'"}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.IssueType']", 'related_name': "'+'"}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.Points']", 'related_name': "'+'"}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.Priority']", 'related_name': "'+'"}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.Severity']", 'related_name': "'+'"}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.TaskStatus']", 'related_name': "'+'"}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True', 'to': "orm['projects.UserStoryStatus']", 'related_name': "'+'"}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'through': "orm['projects.Membership']", 'related_name': "'projects'", 'to': "orm['users.User']", 'symmetrical': 'False'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'blank': 'True', 'default': '[]', 'null': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'blank': 'True', 'default': 'None', 'null': 'True'}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'blank': 'True', 'dimension': '2', 'default': '[]'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True', 'default': '0'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '250'}) + }, + 'projects.projecttemplate': { + 'Meta': {'ordering': "['name']", 'object_name': 'ProjectTemplate'}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '250'}) + }, + 'projects.severity': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'Severity', 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'TaskStatus', 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'object_name': 'UserStoryStatus', 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True', 'default': 'None'}) + }, + 'users.role': { + 'Meta': {'ordering': "['order', 'slug']", 'object_name': 'Role', 'unique_together': "(('slug', 'project'),)"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'dbtype': "'text'", 'blank': 'True', 'default': '[]', 'null': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'bio': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '9', 'default': "'#8e74aa'"}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'email_token': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '200', 'default': 'None'}), + 'full_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '256'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'null': 'True', 'blank': 'True', 'max_length': '75'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'null': 'True', 'blank': 'True', 'max_length': '500'}), + 'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '200', 'default': 'None'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + } + } + + complete_apps = ['projects'] \ No newline at end of file diff --git a/taiga/projects/milestones/migrations/0003_auto__del_unique_milestone_slug__chg_field_milestone_created_date__chg.py b/taiga/projects/milestones/migrations/0003_auto__del_unique_milestone_slug__chg_field_milestone_created_date__chg.py new file mode 100644 index 00000000..fd95ca2d --- /dev/null +++ b/taiga/projects/milestones/migrations/0003_auto__del_unique_milestone_slug__chg_field_milestone_created_date__chg.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Removing unique constraint on 'Milestone', fields ['slug'] + db.delete_unique('milestones_milestone', ['slug']) + + + # Changing field 'Milestone.created_date' + db.alter_column('milestones_milestone', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'Milestone.modified_date' + db.alter_column('milestones_milestone', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + # Adding unique constraint on 'Milestone', fields ['slug', 'project'] + db.create_unique('milestones_milestone', ['slug', 'project_id']) + + + def backwards(self, orm): + # Removing unique constraint on 'Milestone', fields ['slug', 'project'] + db.delete_unique('milestones_milestone', ['slug', 'project_id']) + + # Adding unique constraint on 'Milestone', fields ['slug'] + db.create_unique('milestones_milestone', ['slug']) + + + # Changing field 'Milestone.created_date' + db.alter_column('milestones_milestone', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + # Changing field 'Milestone.modified_date' + db.alter_column('milestones_milestone', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + models = { + 'milestones.milestone': { + 'Meta': {'unique_together': "[('name', 'project'), ('slug', 'project')]", 'object_name': 'Milestone', 'ordering': "['project', 'created_date']"}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'default': '0.0', 'null': 'True'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {}), + 'estimated_start': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'related_name': "'owned_milestones'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'milestones'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['users.User']", 'symmetrical': 'False', 'null': 'True', 'related_name': "'milestones_milestone+'"}) + }, + 'projects.issuestatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'IssueStatus', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"}) + }, + 'projects.issuetype': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'IssueType', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"}) + }, + 'projects.membership': { + 'Meta': {'unique_together': "(('user', 'project'),)", 'object_name': 'Membership', 'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '255', 'default': 'None', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '60', 'default': 'None', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'default': 'None', 'null': 'True', 'related_name': "'memberships'"}) + }, + 'projects.points': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Points', 'ordering': "['project', 'order', 'name']"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}), + 'value': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'default': 'None', 'null': 'True'}) + }, + 'projects.priority': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Priority', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'default': '[]', 'null': 'True', 'dbtype': "'text'"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['projects.ProjectTemplate']", 'default': 'None', 'null': 'True', 'related_name': "'projects'"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.IssueStatus']", 'unique': 'True', 'null': 'True'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.IssueType']", 'unique': 'True', 'null': 'True'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.Points']", 'unique': 'True', 'null': 'True'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.Priority']", 'unique': 'True', 'null': 'True'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.Severity']", 'unique': 'True', 'null': 'True'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.TaskStatus']", 'unique': 'True', 'null': 'True'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'to': "orm['projects.UserStoryStatus']", 'unique': 'True', 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['users.User']", 'through': "orm['projects.Membership']", 'symmetrical': 'False', 'related_name': "'projects'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'default': '[]', 'null': 'True', 'dbtype': "'text'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'default': 'None', 'null': 'True', 'dbtype': "'text'"}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'default': '[]', 'dbtype': "'text'", 'dimension': '2'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'default': '0', 'null': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'object_name': 'ProjectTemplate', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}) + }, + 'projects.severity': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Severity', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"}) + }, + 'projects.taskstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'TaskStatus', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"}) + }, + 'projects.userstorystatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'UserStoryStatus', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'default': 'None', 'null': 'True'}) + }, + 'users.role': { + 'Meta': {'unique_together': "(('slug', 'project'),)", 'object_name': 'Role', 'ordering': "['order', 'slug']"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'default': '[]', 'null': 'True', 'dbtype': "'text'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'bio': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '9', 'default': "'#3f564d'"}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'email_token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'default': 'None', 'null': 'True'}), + 'full_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '256'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75', 'null': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '500', 'null': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'default': 'None', 'null': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + } + } + + complete_apps = ['milestones'] \ No newline at end of file diff --git a/taiga/projects/milestones/models.py b/taiga/projects/milestones/models.py index 3f6383d4..224f4e53 100644 --- a/taiga/projects/milestones/models.py +++ b/taiga/projects/milestones/models.py @@ -17,6 +17,7 @@ from django.db import models from django.conf import settings from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from taiga.base.utils.slug import slugify_uniquely from taiga.base.utils.dicts import dict_sum @@ -31,7 +32,7 @@ class Milestone(WatchedModelMixin, models.Model): name = models.CharField(max_length=200, db_index=True, null=False, blank=False, verbose_name=_("name")) # TODO: Change the unique restriction to a unique together with the project id - slug = models.SlugField(max_length=250, unique=True, null=False, blank=True, + slug = models.SlugField(max_length=250, db_index=True, null=False, blank=True, verbose_name=_("slug")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name="owned_milestones", verbose_name=_("owner")) @@ -39,9 +40,10 @@ class Milestone(WatchedModelMixin, models.Model): related_name="milestones", verbose_name=_("project")) estimated_start = models.DateField(verbose_name=_("estimated start date")) estimated_finish = models.DateField(verbose_name=_("estimated finish date")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) closed = models.BooleanField(default=False, null=False, blank=True, verbose_name=_("is closed")) @@ -49,12 +51,13 @@ class Milestone(WatchedModelMixin, models.Model): verbose_name=_("disponibility")) order = models.PositiveSmallIntegerField(default=1, null=False, blank=False, verbose_name=_("order")) + _importing = None class Meta: verbose_name = "milestone" verbose_name_plural = "milestones" ordering = ["project", "created_date"] - unique_together = ("name", "project") + unique_together = [("name", "project"), ("slug", "project")] permissions = ( ("view_milestone", "Can view milestone"), ) @@ -66,6 +69,8 @@ class Milestone(WatchedModelMixin, models.Model): return "".format(self.id) def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() if not self.slug: self.slug = slugify_uniquely(self.name, self.__class__) diff --git a/taiga/projects/milestones/serializers.py b/taiga/projects/milestones/serializers.py index 652455ce..009a8b3b 100644 --- a/taiga/projects/milestones/serializers.py +++ b/taiga/projects/milestones/serializers.py @@ -32,6 +32,7 @@ class MilestoneSerializer(serializers.ModelSerializer): class Meta: model = models.Milestone + read_only_fields = ('id', 'created_date', 'modified_date') def get_total_points(self, obj): return sum(obj.total_points.values()) diff --git a/taiga/projects/models.py b/taiga/projects/models.py index 98e0cdcc..3592af30 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -62,7 +62,7 @@ class Membership(models.Model): # Invitation metadata email = models.EmailField(max_length=255, default=None, null=True, blank=True, verbose_name=_("email")) - created_at = models.DateTimeField(auto_now_add=True, default=timezone.now, + created_at = models.DateTimeField(default=timezone.now, verbose_name=_("creado el")) token = models.CharField(max_length=60, blank=True, null=True, default=None, verbose_name=_("token")) @@ -122,9 +122,10 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): verbose_name=_("slug")) description = models.TextField(null=False, blank=False, verbose_name=_("description")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, related_name="owned_projects", verbose_name=_("owner")) @@ -164,6 +165,7 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): verbose_name=_("is private")) tags_colors = TextArrayField(dimension=2, null=False, blank=True, verbose_name=_("tags colors"), default=[]) + _importing = None class Meta: verbose_name = "project" @@ -180,6 +182,9 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): return "".format(self.id) def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() + if not self.slug: base_slug = slugify_uniquely(self.name, self.__class__) slug = base_slug @@ -471,9 +476,10 @@ class ProjectTemplate(models.Model): verbose_name=_("slug"), unique=True) description = models.TextField(null=False, blank=False, verbose_name=_("description")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) default_owner_role = models.CharField(max_length=50, null=False, blank=False, @@ -502,6 +508,7 @@ class ProjectTemplate(models.Model): priorities = JsonField(null=True, blank=True, verbose_name=_("priorities")) severities = JsonField(null=True, blank=True, verbose_name=_("severities")) roles = JsonField(null=True, blank=True, verbose_name=_("roles")) + _importing = None class Meta: verbose_name = "project template" @@ -515,6 +522,8 @@ class ProjectTemplate(models.Model): return "".format(self.slug) def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() super().save(*args, **kwargs) def load_data_from_project(self, project): diff --git a/taiga/projects/notifications/migrations/0001_initial.py b/taiga/projects/notifications/migrations/0001_initial.py new file mode 100644 index 00000000..72dea8e3 --- /dev/null +++ b/taiga/projects/notifications/migrations/0001_initial.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'NotifyPolicy' + db.create_table('notifications_notifypolicy', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('project', self.gf('django.db.models.fields.related.ForeignKey')(related_name='notify_policies', to=orm['projects.Project'])), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='notify_policies', to=orm['users.User'])), + ('notify_level', self.gf('django.db.models.fields.SmallIntegerField')()), + ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('modified_at', self.gf('django.db.models.fields.DateTimeField')()), + )) + db.send_create_signal('notifications', ['NotifyPolicy']) + + # Adding unique constraint on 'NotifyPolicy', fields ['project', 'user'] + db.create_unique('notifications_notifypolicy', ['project_id', 'user_id']) + + + def backwards(self, orm): + # Removing unique constraint on 'NotifyPolicy', fields ['project', 'user'] + db.delete_unique('notifications_notifypolicy', ['project_id', 'user_id']) + + # Deleting model 'NotifyPolicy' + db.delete_table('notifications_notifypolicy') + + + models = { + 'notifications.notifypolicy': { + 'Meta': {'object_name': 'NotifyPolicy', 'unique_together': "(('project', 'user'),)", 'ordering': "['created_at']"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {}), + 'notify_level': ('django.db.models.fields.SmallIntegerField', [], {}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_policies'", 'to': "orm['projects.Project']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_policies'", 'to': "orm['users.User']"}) + }, + 'projects.issuestatus': { + 'Meta': {'object_name': 'IssueStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'object_name': 'IssueType', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'object_name': 'Membership', 'unique_together': "(('user', 'project'),)", 'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '60'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'related_name': "'memberships'", 'null': 'True', 'to': "orm['users.User']"}) + }, + 'projects.points': { + 'Meta': {'object_name': 'Points', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'default': 'None', 'blank': 'True', 'null': 'True'}) + }, + 'projects.priority': { + 'Meta': {'object_name': 'Priority', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'default': '[]', 'blank': 'True', 'null': 'True', 'dbtype': "'text'"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'related_name': "'projects'", 'null': 'True', 'to': "orm['projects.ProjectTemplate']"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.IssueStatus']", 'null': 'True'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.IssueType']", 'null': 'True'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.Points']", 'null': 'True'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.Priority']", 'null': 'True'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.Severity']", 'null': 'True'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.TaskStatus']", 'null': 'True'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.UserStoryStatus']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['users.User']", 'related_name': "'projects'", 'through': "orm['projects.Membership']"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'default': '[]', 'blank': 'True', 'null': 'True', 'dbtype': "'text'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'unique': 'True', 'max_length': '250'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'dbtype': "'text'"}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'default': '[]', 'blank': 'True', 'dimension': '2', 'dbtype': "'text'"}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True', 'null': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}) + }, + 'projects.projecttemplate': { + 'Meta': {'object_name': 'ProjectTemplate', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'unique': 'True', 'max_length': '250'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}) + }, + 'projects.severity': { + 'Meta': {'object_name': 'Severity', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'object_name': 'TaskStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'object_name': 'UserStoryStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'blank': 'True', 'null': 'True'}) + }, + 'users.role': { + 'Meta': {'object_name': 'Role', 'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'default': '[]', 'blank': 'True', 'null': 'True', 'dbtype': "'text'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'bio': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'color': ('django.db.models.fields.CharField', [], {'default': "'#35acb0'", 'blank': 'True', 'max_length': '9'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'email_token': ('django.db.models.fields.CharField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '200'}), + 'full_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '256'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'null': 'True', 'max_length': '75'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '500'}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '200'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + } + } + + complete_apps = ['notifications'] \ No newline at end of file diff --git a/taiga/projects/notifications/migrations/__init__.py b/taiga/projects/notifications/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/notifications/models.py b/taiga/projects/notifications/models.py index 80b84fc1..6a7db7e8 100644 --- a/taiga/projects/notifications/models.py +++ b/taiga/projects/notifications/models.py @@ -16,6 +16,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from .choices import NOTIFY_LEVEL_CHOICES @@ -25,13 +26,20 @@ class NotifyPolicy(models.Model): This class represents a persistence for project user notifications preference. """ - project = models.ForeignKey("projects.Project", related_name="+") - user = models.ForeignKey("users.User", related_name="+") + project = models.ForeignKey("projects.Project", related_name="notify_policies") + user = models.ForeignKey("users.User", related_name="notify_policies") notify_level = models.SmallIntegerField(choices=NOTIFY_LEVEL_CHOICES) - created_at = models.DateTimeField(auto_now_add=True) - modified_at = models.DateTimeField(auto_now=True) + created_at = models.DateTimeField(default=timezone.now) + modified_at = models.DateTimeField() + _importing = None class Meta: unique_together = ("project", "user",) ordering = ["created_at"] + + def save(self, *args, **kwargs): + if not self._importing: + self.modified_at = timezone.now() + + return super().save(*args, **kwargs) diff --git a/taiga/projects/references/migrations/0005_auto__chg_field_reference_created_at.py b/taiga/projects/references/migrations/0005_auto__chg_field_reference_created_at.py new file mode 100644 index 00000000..934f2069 --- /dev/null +++ b/taiga/projects/references/migrations/0005_auto__chg_field_reference_created_at.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Reference.created_at' + db.alter_column('references_reference', 'created_at', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'Reference.created_at' + db.alter_column('references_reference', 'created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'contenttypes.contenttype': { + 'Meta': {'object_name': 'ContentType', 'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'projects.issuestatus': { + 'Meta': {'object_name': 'IssueStatus', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"}) + }, + 'projects.issuetype': { + 'Meta': {'object_name': 'IssueType', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"}) + }, + 'projects.membership': { + 'Meta': {'object_name': 'Membership', 'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']", 'unique_together': "(('user', 'project'),)"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '60'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'related_name': "'memberships'", 'to': "orm['users.User']"}) + }, + 'projects.points': { + 'Meta': {'object_name': 'Points', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}), + 'value': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'projects.priority': { + 'Meta': {'object_name': 'Priority', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'null': 'True', 'default': '[]', 'dbtype': "'text'"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'related_name': "'projects'", 'to': "orm['projects.ProjectTemplate']"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.IssueStatus']", 'related_name': "'+'"}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.IssueType']", 'related_name': "'+'"}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.Points']", 'related_name': "'+'"}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.Priority']", 'related_name': "'+'"}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.Severity']", 'related_name': "'+'"}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.TaskStatus']", 'related_name': "'+'"}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'to': "orm['projects.UserStoryStatus']", 'related_name': "'+'"}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['users.User']", 'symmetrical': 'False', 'related_name': "'projects'", 'through': "orm['projects.Membership']"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'null': 'True', 'default': '[]', 'dbtype': "'text'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'dbtype': "'text'"}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'default': '[]', 'dbtype': "'text'", 'dimension': '2'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': '0'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}) + }, + 'projects.projecttemplate': { + 'Meta': {'object_name': 'ProjectTemplate', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250', 'unique': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}) + }, + 'projects.severity': { + 'Meta': {'object_name': 'Severity', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"}) + }, + 'projects.taskstatus': { + 'Meta': {'object_name': 'TaskStatus', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"}) + }, + 'projects.userstorystatus': { + 'Meta': {'object_name': 'UserStoryStatus', 'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'references.reference': { + 'Meta': {'object_name': 'Reference', 'ordering': "['created_at']", 'unique_together': "(['project', 'ref'],)"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'+'"}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'references'"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {}) + }, + 'users.role': { + 'Meta': {'object_name': 'Role', 'ordering': "['order', 'slug']", 'unique_together': "(('slug', 'project'),)"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'null': 'True', 'default': '[]', 'dbtype': "'text'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'bio': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "'#7d4751'", 'max_length': '9'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'email_token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '200'}), + 'full_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '256'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'null': 'True', 'max_length': '75'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '500'}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '200'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + } + } + + complete_apps = ['references'] \ No newline at end of file diff --git a/taiga/projects/references/models.py b/taiga/projects/references/models.py index d817d9c0..8dfbdd66 100644 --- a/taiga/projects/references/models.py +++ b/taiga/projects/references/models.py @@ -15,6 +15,7 @@ # along with this program. If not, see . from django.db import models +from django.utils import timezone from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.generic import GenericForeignKey @@ -31,7 +32,7 @@ class Reference(models.Model): object_id = models.PositiveIntegerField() ref = models.BigIntegerField() content_object = GenericForeignKey("content_type", "object_id") - created_at = models.DateTimeField(auto_now_add=True) + created_at = models.DateTimeField(default=timezone.now) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="references") diff --git a/taiga/projects/tasks/migrations/0009_auto__chg_field_task_subject__chg_field_task_modified_date__chg_field_.py b/taiga/projects/tasks/migrations/0009_auto__chg_field_task_subject__chg_field_task_modified_date__chg_field_.py new file mode 100644 index 00000000..b8b7e5da --- /dev/null +++ b/taiga/projects/tasks/migrations/0009_auto__chg_field_task_subject__chg_field_task_modified_date__chg_field_.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Task.subject' + db.alter_column('tasks_task', 'subject', self.gf('django.db.models.fields.TextField')()) + + # Changing field 'Task.modified_date' + db.alter_column('tasks_task', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'Task.created_date' + db.alter_column('tasks_task', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'Task.subject' + db.alter_column('tasks_task', 'subject', self.gf('django.db.models.fields.CharField')(max_length=500)) + + # Changing field 'Task.modified_date' + db.alter_column('tasks_task', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + # Changing field 'Task.created_date' + db.alter_column('tasks_task', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'issues.issue': { + 'Meta': {'ordering': "['project', '-created_date']", 'object_name': 'Issue'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['users.User']", 'blank': 'True', 'related_name': "'issues_assigned_to_me'"}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['milestones.Milestone']", 'blank': 'True', 'related_name': "'issues'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['users.User']", 'blank': 'True', 'related_name': "'owned_issues'"}), + 'priority': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Priority']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': 'None', 'db_index': 'True', 'blank': 'True'}), + 'severity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Severity']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.IssueStatus']"}), + 'subject': ('django.db.models.fields.TextField', [], {}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': 'None', 'dbtype': "'text'", 'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.IssueType']"}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'related_name': "'issues_issue+'", 'to': "orm['users.User']", 'blank': 'True', 'symmetrical': 'False'}) + }, + 'milestones.milestone': { + 'Meta': {'ordering': "['project', 'created_date']", 'unique_together': "[('name', 'project'), ('slug', 'project')]", 'object_name': 'Milestone'}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'null': 'True', 'default': '0.0', 'blank': 'True'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {}), + 'estimated_start': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'owned_milestones'", 'to': "orm['users.User']", 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'milestones'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'related_name': "'milestones_milestone+'", 'to': "orm['users.User']", 'blank': 'True', 'symmetrical': 'False'}) + }, + 'projects.issuestatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'IssueStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'IssueType'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']", 'unique_together': "(('user', 'project'),)", 'object_name': 'Membership'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'null': 'True', 'default': 'None', 'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'max_length': '60', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['users.User']", 'blank': 'True', 'related_name': "'memberships'"}) + }, + 'projects.points': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'Points'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'default': 'None', 'blank': 'True'}) + }, + 'projects.priority': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'Priority'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': '[]', 'dbtype': "'text'", 'blank': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['projects.ProjectTemplate']", 'blank': 'True', 'related_name': "'projects'"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.IssueStatus']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.IssueType']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.Points']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.Priority']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.Severity']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.TaskStatus']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'null': 'True', 'related_name': "'+'", 'to': "orm['projects.UserStoryStatus']", 'blank': 'True', 'unique': 'True', 'on_delete': 'models.SET_NULL'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'through': "orm['projects.Membership']", 'to': "orm['users.User']", 'symmetrical': 'False', 'related_name': "'projects'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': '[]', 'dbtype': "'text'", 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '250', 'blank': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': 'None', 'dbtype': "'text'", 'blank': 'True'}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'default': '[]', 'dimension': '2', 'dbtype': "'text'", 'blank': 'True'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'default': '0', 'blank': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'ordering': "['name']", 'object_name': 'ProjectTemplate'}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '250', 'blank': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}) + }, + 'projects.severity': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'Severity'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'TaskStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'UserStoryStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'default': 'None', 'blank': 'True'}) + }, + 'tasks.task': { + 'Meta': {'ordering': "['project', 'created_date']", 'object_name': 'Task'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['users.User']", 'blank': 'True', 'related_name': "'tasks_assigned_to_me'"}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_iocaine': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['milestones.Milestone']", 'blank': 'True', 'related_name': "'tasks'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['users.User']", 'blank': 'True', 'related_name': "'owned_tasks'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': 'None', 'db_index': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'to': "orm['projects.TaskStatus']"}), + 'subject': ('django.db.models.fields.TextField', [], {}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': 'None', 'dbtype': "'text'", 'blank': 'True'}), + 'user_story': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'tasks'", 'to': "orm['userstories.UserStory']", 'blank': 'True'}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'related_name': "'tasks_task+'", 'to': "orm['users.User']", 'blank': 'True', 'symmetrical': 'False'}) + }, + 'users.role': { + 'Meta': {'ordering': "['order', 'slug']", 'unique_together': "(('slug', 'project'),)", 'object_name': 'Role'}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': '[]', 'dbtype': "'text'", 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'}) + }, + 'users.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'bio': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'color': ('django.db.models.fields.CharField', [], {'default': "'#e98dda'", 'max_length': '9', 'blank': 'True'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'max_length': '200', 'blank': 'True'}), + 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'null': 'True', 'max_length': '75', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'null': 'True', 'max_length': '500', 'blank': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'max_length': '200', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'userstories.rolepoints': { + 'Meta': {'ordering': "['user_story', 'role']", 'unique_together': "(('user_story', 'role'),)", 'object_name': 'RolePoints'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'points': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_points'", 'to': "orm['projects.Points']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_points'", 'to': "orm['users.Role']"}), + 'user_story': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_points'", 'to': "orm['userstories.UserStory']"}) + }, + 'userstories.userstory': { + 'Meta': {'ordering': "['project', 'order', 'ref']", 'object_name': 'UserStory'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['users.User']", 'blank': 'True', 'related_name': "'userstories_assigned_to_me'"}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'client_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finish_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'generated_from_issue': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'generated_user_stories'", 'to': "orm['issues.Issue']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'default': 'None', 'to': "orm['milestones.Milestone']", 'blank': 'True', 'related_name': "'user_stories'", 'on_delete': 'models.SET_NULL'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'owned_user_stories'", 'to': "orm['users.User']", 'blank': 'True', 'on_delete': 'models.SET_NULL'}), + 'points': ('django.db.models.fields.related.ManyToManyField', [], {'through': "orm['userstories.RolePoints']", 'to': "orm['projects.Points']", 'symmetrical': 'False', 'related_name': "'userstories'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_stories'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': 'None', 'db_index': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'user_stories'", 'to': "orm['projects.UserStoryStatus']", 'blank': 'True', 'on_delete': 'models.SET_NULL'}), + 'subject': ('django.db.models.fields.TextField', [], {}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': 'None', 'dbtype': "'text'", 'blank': 'True'}), + 'team_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'related_name': "'userstories_userstory+'", 'to': "orm['users.User']", 'blank': 'True', 'symmetrical': 'False'}) + } + } + + complete_apps = ['tasks'] \ No newline at end of file diff --git a/taiga/projects/tasks/models.py b/taiga/projects/tasks/models.py index 0bf6d6e0..538ac2f3 100644 --- a/taiga/projects/tasks/models.py +++ b/taiga/projects/tasks/models.py @@ -46,9 +46,10 @@ class Task(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.M milestone = models.ForeignKey("milestones.Milestone", null=True, blank=True, default=None, related_name="tasks", verbose_name=_("milestone")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) finished_date = models.DateTimeField(null=True, blank=True, verbose_name=_("finished date")) @@ -61,6 +62,7 @@ class Task(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.M attachments = generic.GenericRelation("attachments.Attachment") is_iocaine = models.BooleanField(default=False, null=False, blank=True, verbose_name=_("is iocaine")) + _importing = None class Meta: verbose_name = "task" @@ -71,6 +73,12 @@ class Task(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.M ("view_task", "Can view task"), ) + def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() + + return super().save(*args, **kwargs) + def __str__(self): return "({1}) {0}".format(self.ref, self.subject) diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index b82e79cf..24b679d4 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -35,6 +35,7 @@ class TaskSerializer(serializers.ModelSerializer): class Meta: model = models.Task + read_only_fields = ('id', 'ref', 'created_date', 'modified_date') def get_comment(self, obj): return "" @@ -72,5 +73,5 @@ class TasksBulkSerializer(ProjectExistsValidator, SprintExistsValidator, TaskSta project_id = serializers.IntegerField() sprint_id = serializers.IntegerField() status_id = serializers.IntegerField(required=False) - us_id = serializers.IntegerField() + us_id = serializers.IntegerField(required=False) bulk_tasks = serializers.CharField() diff --git a/taiga/projects/userstories/migrations/0015_auto__chg_field_userstory_subject__chg_field_userstory_modified_date__.py b/taiga/projects/userstories/migrations/0015_auto__chg_field_userstory_subject__chg_field_userstory_modified_date__.py new file mode 100644 index 00000000..8c0e5ef6 --- /dev/null +++ b/taiga/projects/userstories/migrations/0015_auto__chg_field_userstory_subject__chg_field_userstory_modified_date__.py @@ -0,0 +1,269 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'UserStory.subject' + db.alter_column('userstories_userstory', 'subject', self.gf('django.db.models.fields.TextField')()) + + # Changing field 'UserStory.modified_date' + db.alter_column('userstories_userstory', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'UserStory.created_date' + db.alter_column('userstories_userstory', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'UserStory.subject' + db.alter_column('userstories_userstory', 'subject', self.gf('django.db.models.fields.CharField')(max_length=500)) + + # Changing field 'UserStory.modified_date' + db.alter_column('userstories_userstory', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + # Changing field 'UserStory.created_date' + db.alter_column('userstories_userstory', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'issues.issue': { + 'Meta': {'ordering': "['project', '-created_date']", 'object_name': 'Issue'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'default': 'None', 'related_name': "'issues_assigned_to_me'"}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['milestones.Milestone']", 'default': 'None', 'related_name': "'issues'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'default': 'None', 'related_name': "'owned_issues'"}), + 'priority': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Priority']", 'related_name': "'issues'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issues'"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'blank': 'True', 'db_index': 'True', 'default': 'None', 'null': 'True'}), + 'severity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Severity']", 'related_name': "'issues'"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.IssueStatus']", 'related_name': "'issues'"}), + 'subject': ('django.db.models.fields.TextField', [], {}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'default': 'None', 'null': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.IssueType']", 'related_name': "'issues'"}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'related_name': "'issues_issue+'"}) + }, + 'milestones.milestone': { + 'Meta': {'ordering': "['project', 'created_date']", 'unique_together': "[('name', 'project'), ('slug', 'project')]", 'object_name': 'Milestone'}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'default': '0.0', 'null': 'True'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {}), + 'estimated_start': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'related_name': "'owned_milestones'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'milestones'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'related_name': "'milestones_milestone+'"}) + }, + 'projects.issuestatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'IssueStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"}) + }, + 'projects.issuetype': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'IssueType'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"}) + }, + 'projects.membership': { + 'Meta': {'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']", 'unique_together': "(('user', 'project'),)", 'object_name': 'Membership'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '255', 'default': 'None', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '60', 'default': 'None', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'default': 'None', 'related_name': "'memberships'"}) + }, + 'projects.points': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'Points'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}), + 'value': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'default': 'None', 'null': 'True'}) + }, + 'projects.priority': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'Priority'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'default': '[]', 'null': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['projects.ProjectTemplate']", 'default': 'None', 'related_name': "'projects'"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.IssueStatus']", 'null': 'True'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.IssueType']", 'null': 'True'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.Points']", 'null': 'True'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.Priority']", 'null': 'True'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.Severity']", 'null': 'True'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.TaskStatus']", 'null': 'True'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'+'", 'unique': 'True', 'to': "orm['projects.UserStoryStatus']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['users.User']", 'through': "orm['projects.Membership']", 'related_name': "'projects'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'default': '[]', 'null': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'unique': 'True', 'max_length': '250'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'default': 'None', 'null': 'True'}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'dimension': '2', 'blank': 'True', 'dbtype': "'text'", 'default': '[]'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'default': '0', 'null': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'ordering': "['name']", 'object_name': 'ProjectTemplate'}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'unique': 'True', 'max_length': '250'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '250', 'null': 'True'}) + }, + 'projects.severity': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'Severity'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"}) + }, + 'projects.taskstatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'TaskStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"}) + }, + 'projects.userstorystatus': { + 'Meta': {'ordering': "['project', 'order', 'name']", 'unique_together': "(('project', 'name'),)", 'object_name': 'UserStoryStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'default': 'None', 'null': 'True'}) + }, + 'users.role': { + 'Meta': {'ordering': "['order', 'slug']", 'unique_together': "(('slug', 'project'),)", 'object_name': 'Role'}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'default': '[]', 'null': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'bio': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "'#8d6dd6'", 'max_length': '9'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'email_token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'default': 'None', 'null': 'True'}), + 'full_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '256'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75', 'null': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '500', 'null': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'default': 'None', 'null': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'userstories.rolepoints': { + 'Meta': {'ordering': "['user_story', 'role']", 'unique_together': "(('user_story', 'role'),)", 'object_name': 'RolePoints'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'points': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Points']", 'related_name': "'role_points'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'role_points'"}), + 'user_story': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['userstories.UserStory']", 'related_name': "'role_points'"}) + }, + 'userstories.userstory': { + 'Meta': {'ordering': "['project', 'order', 'ref']", 'object_name': 'UserStory'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'default': 'None', 'related_name': "'userstories_assigned_to_me'"}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'client_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finish_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'generated_from_issue': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'null': 'True', 'to': "orm['issues.Issue']", 'related_name': "'generated_user_stories'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'on_delete': 'models.SET_NULL', 'related_name': "'user_stories'", 'to': "orm['milestones.Milestone']", 'default': 'None', 'null': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'related_name': "'owned_user_stories'"}), + 'points': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['projects.Points']", 'through': "orm['userstories.RolePoints']", 'related_name': "'userstories'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'user_stories'"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'blank': 'True', 'db_index': 'True', 'default': 'None', 'null': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'null': 'True', 'to': "orm['projects.UserStoryStatus']", 'related_name': "'user_stories'"}), + 'subject': ('django.db.models.fields.TextField', [], {}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'blank': 'True', 'dbtype': "'text'", 'default': 'None', 'null': 'True'}), + 'team_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'null': 'True', 'to': "orm['users.User']", 'related_name': "'userstories_userstory+'"}) + } + } + + complete_apps = ['userstories'] \ No newline at end of file diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py index b384deaa..ad3872ee 100644 --- a/taiga/projects/userstories/models.py +++ b/taiga/projects/userstories/models.py @@ -19,6 +19,7 @@ from django.contrib.contenttypes import generic from django.conf import settings from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from taiga.base.tags import TaggedMixin from taiga.base.utils.slug import ref_uniquely @@ -75,9 +76,10 @@ class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, mod verbose_name=_("points")) order = models.PositiveSmallIntegerField(null=False, blank=False, default=100, verbose_name=_("order")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) finish_date = models.DateTimeField(null=True, blank=True, verbose_name=_("finish date")) @@ -95,6 +97,7 @@ class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, mod generated_from_issue = models.ForeignKey("issues.Issue", null=True, blank=True, related_name="generated_user_stories", verbose_name=_("generated from issue")) + _importing = None class Meta: verbose_name = "user story" @@ -105,6 +108,12 @@ class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, mod ("view_userstory", "Can view user story"), ) + def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() + + super().save(*args, **kwargs) + def __str__(self): return "({1}) {0}".format(self.ref, self.subject) diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index 365cac13..51d9a8a5 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -50,6 +50,7 @@ class UserStorySerializer(serializers.ModelSerializer): class Meta: model = models.UserStory depth = 0 + read_only_fields = ('created_date', 'modified_date') def save_object(self, obj, **kwargs): role_points = obj._related_data.pop("role_points", None) diff --git a/taiga/projects/wiki/migrations/0005_auto__chg_field_wikipage_created_date__chg_field_wikipage_modified_dat.py b/taiga/projects/wiki/migrations/0005_auto__chg_field_wikipage_created_date__chg_field_wikipage_modified_dat.py new file mode 100644 index 00000000..598747fc --- /dev/null +++ b/taiga/projects/wiki/migrations/0005_auto__chg_field_wikipage_created_date__chg_field_wikipage_modified_dat.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'WikiPage.created_date' + db.alter_column('wiki_wikipage', 'created_date', self.gf('django.db.models.fields.DateTimeField')()) + + # Changing field 'WikiPage.modified_date' + db.alter_column('wiki_wikipage', 'modified_date', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'WikiPage.created_date' + db.alter_column('wiki_wikipage', 'created_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + # Changing field 'WikiPage.modified_date' + db.alter_column('wiki_wikipage', 'modified_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True)) + + models = { + 'projects.issuestatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueType'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'unique_together': "(('user', 'project'),)", 'ordering': "['project', 'user__full_name', 'user__username', 'user__email', 'email']", 'object_name': 'Membership'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'null': 'True', 'default': 'None', 'blank': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_by_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'blank': 'True', 'max_length': '60'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'memberships'", 'default': 'None', 'blank': 'True', 'to': "orm['users.User']"}) + }, + 'projects.points': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Points'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'default': 'None', 'blank': 'True'}) + }, + 'projects.priority': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Priority'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'anon_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': '[]', 'blank': 'True', 'dbtype': "'text'"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'projects'", 'default': 'None', 'blank': 'True', 'to': "orm['projects.ProjectTemplate']"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.IssueStatus']", 'null': 'True'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.IssueType']", 'null': 'True'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.Points']", 'null': 'True'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.Priority']", 'null': 'True'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.Severity']", 'null': 'True'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.TaskStatus']", 'null': 'True'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'on_delete': 'models.SET_NULL', 'blank': 'True', 'unique': 'True', 'to': "orm['projects.UserStoryStatus']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['users.User']", 'related_name': "'projects'", 'through': "orm['projects.Membership']", 'symmetrical': 'False'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public_permissions': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': '[]', 'blank': 'True', 'dbtype': "'text'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True', 'unique': 'True'}), + 'tags': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': 'None', 'blank': 'True', 'dbtype': "'text'"}), + 'tags_colors': ('djorm_pgarray.fields.TextArrayField', [], {'default': '[]', 'dimension': '2', 'blank': 'True', 'dbtype': "'text'"}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'default': '0', 'blank': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'object_name': 'ProjectTemplate', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True', 'unique': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'max_length': '250', 'blank': 'True'}) + }, + 'projects.severity': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Severity'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'TaskStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'UserStoryStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'default': 'None', 'blank': 'True'}) + }, + 'users.role': { + 'Meta': {'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']", 'object_name': 'Role'}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('djorm_pgarray.fields.TextArrayField', [], {'null': 'True', 'default': '[]', 'blank': 'True', 'dbtype': "'text'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'bio': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'color': ('django.db.models.fields.CharField', [], {'default': "'#7a4665'", 'blank': 'True', 'max_length': '9'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'blank': 'True', 'max_length': '200'}), + 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), + 'github_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'new_email': ('django.db.models.fields.EmailField', [], {'null': 'True', 'max_length': '75', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'null': 'True', 'max_length': '500', 'blank': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'blank': 'True', 'max_length': '200'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + }, + 'wiki.wikilink': { + 'Meta': {'unique_together': "(('project', 'href'),)", 'ordering': "['project', 'order']", 'object_name': 'WikiLink'}, + 'href': ('django.db.models.fields.SlugField', [], {'max_length': '500'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wiki_links'", 'to': "orm['projects.Project']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'wiki.wikipage': { + 'Meta': {'unique_together': "(('project', 'slug'),)", 'ordering': "['project', 'slug']", 'object_name': 'WikiPage'}, + 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modifier': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'last_modified_wiki_pages'", 'blank': 'True', 'to': "orm['users.User']"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'owned_wiki_pages'", 'blank': 'True', 'to': "orm['users.User']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wiki_pages'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '500'}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'related_name': "'wiki_wikipage+'", 'to': "orm['users.User']", 'blank': 'True', 'symmetrical': 'False'}) + } + } + + complete_apps = ['wiki'] \ No newline at end of file diff --git a/taiga/projects/wiki/models.py b/taiga/projects/wiki/models.py index 5cc94325..0104c142 100644 --- a/taiga/projects/wiki/models.py +++ b/taiga/projects/wiki/models.py @@ -18,6 +18,7 @@ from django.db import models from django.contrib.contenttypes import generic from django.conf import settings from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from taiga.projects.notifications import WatchedModelMixin from taiga.projects.occ import OCCModelMixin @@ -33,11 +34,13 @@ class WikiPage(OCCModelMixin, WatchedModelMixin, models.Model): related_name="owned_wiki_pages", verbose_name=_("owner")) last_modifier = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name="last_modified_wiki_pages", verbose_name=_("last modifier")) - created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, - verbose_name=_("created date")) - modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, + created_date = models.DateTimeField(null=False, blank=False, + verbose_name=_("created date"), + default=timezone.now) + modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) attachments = generic.GenericRelation("attachments.Attachment") + _importing = None class Meta: verbose_name = "wiki page" @@ -51,6 +54,12 @@ class WikiPage(OCCModelMixin, WatchedModelMixin, models.Model): def __str__(self): return "project {0} - {1}".format(self.project_id, self.slug) + def save(self, *args, **kwargs): + if not self._importing: + self.modified_date = timezone.now() + + return super().save(*args, **kwargs) + class WikiLink(models.Model): project = models.ForeignKey("projects.Project", null=False, blank=False, diff --git a/taiga/projects/wiki/serializers.py b/taiga/projects/wiki/serializers.py index 36242e84..3ed8008c 100644 --- a/taiga/projects/wiki/serializers.py +++ b/taiga/projects/wiki/serializers.py @@ -29,6 +29,7 @@ class WikiPageSerializer(serializers.ModelSerializer): class Meta: model = models.WikiPage + read_only_fields = ('modified_date', 'created_date') def get_html(self, obj): return mdrender(obj.project, obj.content) diff --git a/taiga/timeline/migrations/0002_auto__chg_field_timeline_created.py b/taiga/timeline/migrations/0002_auto__chg_field_timeline_created.py new file mode 100644 index 00000000..d39fcc70 --- /dev/null +++ b/taiga/timeline/migrations/0002_auto__chg_field_timeline_created.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Timeline.created' + db.alter_column('timeline_timeline', 'created', self.gf('django.db.models.fields.DateTimeField')()) + + def backwards(self, orm): + + # Changing field 'Timeline.created' + db.alter_column('timeline_timeline', 'created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + models = { + 'contenttypes.contenttype': { + 'Meta': {'object_name': 'ContentType', 'db_table': "'django_content_type'", 'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'timeline.timeline': { + 'Meta': {'object_name': 'Timeline', 'index_together': "[('content_type', 'object_id', 'namespace')]"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'data': ('django_pgjson.fields.JsonField', [], {'blank': 'False', 'null': 'False'}), + 'event_type': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'namespace': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'default': "'default'"}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}) + } + } + + complete_apps = ['timeline'] \ No newline at end of file diff --git a/taiga/timeline/models.py b/taiga/timeline/models.py index 7aff7217..2b0982fa 100644 --- a/taiga/timeline/models.py +++ b/taiga/timeline/models.py @@ -16,6 +16,7 @@ from django.db import models from django_pgjson.fields import JsonField +from django.utils import timezone from django.core.exceptions import ValidationError @@ -30,7 +31,7 @@ class Timeline(models.Model): namespace = models.SlugField(default="default") event_type = models.SlugField() data = JsonField() - created = models.DateTimeField(auto_now_add=True) + created = models.DateTimeField(default=timezone.now) def save(self, *args, **kwargs): if self.id: