From 32d2d0a07c3057ef11b273d54303015f26108ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 19 Oct 2016 11:32:43 +0200 Subject: [PATCH] Upgrade requirements annd change json to jsonb --- requirements-devel.txt | 12 +- requirements.txt | 36 +- settings/common.py | 1 + taiga/base/api/serializers.py | 34 +- taiga/base/api/urlpatterns.py | 4 +- taiga/base/db/__init__.py | 0 taiga/base/db/models/__init__.py | 0 taiga/base/db/models/fields/__init__.py | 20 + taiga/base/db/models/fields/json.py | 82 ++ taiga/base/fields.py | 6 +- taiga/base/management/commands/test_emails.py | 53 +- taiga/base/routers.py | 4 +- taiga/export_import/validators/fields.py | 8 +- taiga/export_import/validators/mixins.py | 4 +- taiga/export_import/validators/validators.py | 6 +- ...svalues_userstorycustomattributesvalues.py | 8 +- .../migrations/0005_auto_20150505_1639.py | 8 +- .../migrations/0009_auto_20160728_1002.py | 4 +- .../migrations/0011_json_to_jsonb.py | 90 ++ taiga/projects/custom_attributes/models.py | 4 +- .../projects/custom_attributes/serializers.py | 2 +- .../projects/custom_attributes/validators.py | 4 +- .../fixtures/initial_project_templates.json | 1116 ++++++++++++++++- .../history/migrations/0001_initial.py | 10 +- .../migrations/0003_auto_20140917_1405.py | 4 +- .../0006_fix_json_field_not_null.py | 2 +- .../migrations/0008_auto_20150508_1028.py | 8 +- .../migrations/0009_auto_20160512_1110.py | 4 +- .../0013_historyentry_values_diff_cache.py | 4 +- .../history/migrations/0014_json_to_jsonb.py | 29 + taiga/projects/history/models.py | 16 +- taiga/projects/history/serializers.py | 6 +- .../issues/migrations/0001_initial.py | 4 +- .../0002_issue_external_reference.py | 4 +- taiga/projects/migrations/0001_initial.py | 11 +- .../migrations/0002_auto_20140903_0920.py | 20 +- .../migrations/0010_project_modules_config.py | 4 +- .../migrations/0011_auto_20141028_2057.py | 4 +- .../0016_fix_json_field_not_null.py | 2 +- .../migrations/0019_auto_20150311_0821.py | 4 +- .../migrations/0024_auto_20150810_1247.py | 4 +- .../migrations/0025_auto_20150901_1600.py | 4 +- .../migrations/0041_auto_20160519_1058.py | 7 +- .../migrations/0049_auto_20160629_1443.py | 6 +- .../projects/migrations/0055_json_to_jsonb.py | 39 + taiga/projects/models.py | 24 +- .../projects/tasks/migrations/0001_initial.py | 4 +- .../0003_task_external_reference.py | 4 +- .../userstories/migrations/0001_initial.py | 4 +- .../0007_userstory_external_reference.py | 4 +- taiga/projects/validators.py | 20 +- .../management/commands/rebuild_timeline.py | 49 +- taiga/timeline/migrations/0001_initial.py | 4 +- .../migrations/0002_auto_20150327_1056.py | 4 +- .../timeline/migrations/0006_json_to_jsonb.py | 28 + taiga/timeline/models.py | 4 +- taiga/urls.py | 2 +- taiga/users/migrations/0001_initial.py | 1 - .../migrations/0002_auto_20140903_0916.py | 4 +- .../migrations/0007_auto_20150209_1611.py | 4 +- .../migrations/0012_auto_20150812_1142.py | 5 +- .../migrations/0013_auto_20150901_1600.py | 5 +- .../migrations/0019_auto_20160519_1058.py | 7 +- taiga/users/migrations/0023_json_to_jsonb.py | 28 + taiga/users/models.py | 4 +- taiga/userstorage/migrations/0001_initial.py | 4 +- .../0002_fix_json_field_not_null.py | 2 +- .../migrations/0003_json_to_jsonb.py | 28 + taiga/userstorage/models.py | 4 +- taiga/webhooks/migrations/0001_initial.py | 4 +- .../migrations/0003_auto_20150122_1021.py | 12 +- .../migrations/0005_auto_20150505_1639.py | 8 +- .../webhooks/migrations/0006_json_to_jsonb.py | 25 + taiga/webhooks/models.py | 8 +- .../test_projects_resource.py | 6 + tests/integration/test_history.py | 5 +- tests/integration/test_notifications.py | 7 +- .../integration/test_references_sequences.py | 10 + tests/integration/test_roles.py | 24 - tests/integration/test_timeline.py | 16 +- tests/integration/test_totals_projects.py | 8 +- tests/unit/test_export.py | 4 +- tests/unit/test_mdrender.py | 9 +- 83 files changed, 1780 insertions(+), 315 deletions(-) create mode 100644 taiga/base/db/__init__.py create mode 100644 taiga/base/db/models/__init__.py create mode 100644 taiga/base/db/models/fields/__init__.py create mode 100644 taiga/base/db/models/fields/json.py create mode 100644 taiga/projects/custom_attributes/migrations/0011_json_to_jsonb.py create mode 100644 taiga/projects/history/migrations/0014_json_to_jsonb.py create mode 100644 taiga/projects/migrations/0055_json_to_jsonb.py create mode 100644 taiga/timeline/migrations/0006_json_to_jsonb.py create mode 100644 taiga/users/migrations/0023_json_to_jsonb.py create mode 100644 taiga/userstorage/migrations/0003_json_to_jsonb.py create mode 100644 taiga/webhooks/migrations/0006_json_to_jsonb.py diff --git a/requirements-devel.txt b/requirements-devel.txt index e01aa38a..8fd114df 100644 --- a/requirements-devel.txt +++ b/requirements-devel.txt @@ -1,13 +1,13 @@ -r requirements.txt -factory_boy==2.6.1 +factory_boy==2.7.0 py==1.4.31 -pytest==2.8.7 -pytest-django==2.9.1 -pytest-pythonpath==0.7 +pytest==3.0.3 +pytest-django==3.0.0 +pytest-pythonpath==0.7.1 -coverage==4.0.3 +coverage==4.2 coveralls==1.1 django-slowdown==0.0.1 -transifex-client==0.11.1.beta +transifex-client==0.12.2 diff --git a/requirements.txt b/requirements.txt index 47ad6c46..761b1627 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,37 +1,35 @@ -Django==1.9.2 +Django==1.10.3 #djangorestframework==2.3.13 # It's not necessary since Taiga 1.7 django-picklefield==0.3.2 django-sampledatahelper==0.4.0 -gunicorn==19.4.5 -psycopg2==2.6.1 -Pillow==3.1.1 -pytz==2015.7 +gunicorn==19.6.0 +psycopg2==2.6.2 +Pillow==3.4.2 +pytz==2016.7 six==1.10.0 -amqp==1.4.9 -djmail==0.12.0.post1 -django-pgjson==0.3.1 -djorm-pgarray==1.2 # Use until Taiga 2.1. Keep compatibility with old migrations -django-jinja==2.1.2 +amqp==2.1.1 +djmail==0.13.0 +django-jinja==2.2.1 jinja2==2.8 -pygments==2.0.2 +pygments==2.1.3 django-sites==0.9 -Markdown==2.6.5 +Markdown==2.6.7 fn==0.4.3 diff-match-patch==20121119 requests==2.9.1 django-sr==0.0.4 easy-thumbnails==2.3 -celery==3.1.20 +celery==3.1.24 redis==2.10.5 Unidecode==0.04.19 raven==5.10.2 bleach==1.4.3 -django-ipware==1.1.3 -premailer==2.9.7 +django-ipware==1.1.6 +premailer==3.0.1 cssutils==1.0.1 # Compatible with python 3.5 -lxml==3.5.0 -git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea -pyjwkest==1.1.5 -python-dateutil==2.4.2 +lxml==3.6.4 +git+https://github.com/Xof/django-pglocks.git +pyjwkest==1.3.1 +python-dateutil==2.5.3 netaddr==0.7.18 serpy==0.1.1 diff --git a/settings/common.py b/settings/common.py index 87d0d904..641d888f 100644 --- a/settings/common.py +++ b/settings/common.py @@ -282,6 +282,7 @@ INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.staticfiles", "django.contrib.sitemaps", + "django.contrib.postgres", "taiga.base", "taiga.base.api", diff --git a/taiga/base/api/serializers.py b/taiga/base/api/serializers.py index 2ee05db8..5de685a6 100644 --- a/taiga/base/api/serializers.py +++ b/taiga/base/api/serializers.py @@ -817,8 +817,18 @@ class ModelSerializer((six.with_metaclass(SerializerMetaclass, BaseSerializer))) else: # Reverse relationships are only included if they are explicitly # present in the `fields` option on the serializer - reverse_rels = opts.get_all_related_objects() - reverse_rels += opts.get_all_related_many_to_many_objects() + + # NOTE: Rewrite after Django 1.10 upgrade. + # See https://docs.djangoproject.com/es/1.10/ref/models/meta/#migrating-from-the-old-api + reverse_rels = [ + f for f in opts.get_fields() + if (f.one_to_many or f.one_to_one) + and f.auto_created and not f.concrete + ] + reverse_rels += [ + f for f in opts.get_fields(include_hidden=True) + if f.many_to_many and f.auto_created + ] for relation in reverse_rels: accessor_name = relation.get_accessor_name() @@ -1024,16 +1034,32 @@ class ModelSerializer((six.with_metaclass(SerializerMetaclass, BaseSerializer))) m2m_data = {} related_data = {} nested_forward_relations = {} + model = self.opts.model meta = self.opts.model._meta # Reverse fk or one-to-one relations - for (obj, model) in meta.get_all_related_objects_with_model(): + # NOTE: Rewrite after Django 1.10 upgrade. + # See https://docs.djangoproject.com/es/1.10/ref/models/meta/#migrating-from-the-old-api + related_objes_with_models = [ + (f, f.model if f.model != model else None) + for f in meta.get_fields() + if (f.one_to_many or f.one_to_one) + and f.auto_created and not f.concrete + ] + for (obj, model) in related_objes_with_models: field_name = obj.get_accessor_name() if field_name in attrs: related_data[field_name] = attrs.pop(field_name) # Reverse m2m relations - for (obj, model) in meta.get_all_related_m2m_objects_with_model(): + # NOTE: Rewrite after Django 1.10 upgrade. + # See https://docs.djangoproject.com/es/1.10/ref/models/meta/#migrating-from-the-old-api + related_m2m_objects_with_model = [ + (f, f.model if f.model != model else None) + for f in meta.get_fields(include_hidden=True) + if f.many_to_many and f.auto_created + ] + for (obj, model) in related_m2m_objects_with_model: field_name = obj.get_accessor_name() if field_name in attrs: m2m_data[field_name] = attrs.pop(field_name) diff --git a/taiga/base/api/urlpatterns.py b/taiga/base/api/urlpatterns.py index 33548a07..c99f5b8d 100644 --- a/taiga/base/api/urlpatterns.py +++ b/taiga/base/api/urlpatterns.py @@ -44,7 +44,7 @@ from django.core.urlresolvers import RegexURLResolver -from django.conf.urls import patterns, url, include +from django.conf.urls import url, include from .settings import api_settings @@ -67,7 +67,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): else: # Regular URL pattern regex = urlpattern.regex.pattern.rstrip("$") + suffix_pattern - view = urlpattern._callback or urlpattern._callback_str + view = urlpattern.callback kwargs = urlpattern.default_args name = urlpattern.name # Add in both the existing and the new urlpattern diff --git a/taiga/base/db/__init__.py b/taiga/base/db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/base/db/models/__init__.py b/taiga/base/db/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/base/db/models/fields/__init__.py b/taiga/base/db/models/fields/__init__.py new file mode 100644 index 00000000..ad16357e --- /dev/null +++ b/taiga/base/db/models/fields/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino +# Copyright (C) 2014-2016 David Barragán +# Copyright (C) 2014-2016 Alejandro Alonso +# 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 .json import JSONField diff --git a/taiga/base/db/models/fields/json.py b/taiga/base/db/models/fields/json.py new file mode 100644 index 00000000..fd1d63a7 --- /dev/null +++ b/taiga/base/db/models/fields/json.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino +# Copyright (C) 2014-2016 David Barragán +# Copyright (C) 2014-2016 Alejandro Alonso +# 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.core.serializers.json import DjangoJSONEncoder +from django.contrib.postgres.fields import JSONField as DjangoJSONField + +# NOTE: After upgrade Django to the future release (1.11) change +# class JSONField(FutureDjangoJSONField): +# to +# class JSONField(DjangoJSONField): +# and remove the classes JsonAdapter and FutureDjangoJSONField + +import json +from psycopg2.extras import Json +from django.core import exceptions + + +class JsonAdapter(Json): + """ + Customized psycopg2.extras.Json to allow for a custom encoder. + """ + def __init__(self, adapted, dumps=None, encoder=None): + self.encoder = encoder + super().__init__(adapted, dumps=dumps) + + def dumps(self, obj): + options = {'cls': self.encoder} if self.encoder else {} + return json.dumps(obj, **options) + + +class FutureDjangoJSONField(DjangoJSONField): + def __init__(self, verbose_name=None, name=None, encoder=None, **kwargs): + if encoder and not callable(encoder): + raise ValueError("The encoder parameter must be a callable object.") + self.encoder = encoder + super().__init__(verbose_name, name, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.encoder is not None: + kwargs['encoder'] = self.encoder + return name, path, args, kwargs + + def get_prep_value(self, value): + if value is not None: + return JsonAdapter(value, encoder=self.encoder) + return value + + def validate(self, value, model_instance): + super().validate(value, model_instance) + options = {'cls': self.encoder} if self.encoder else {} + try: + json.dumps(value, **options) + except TypeError: + raise exceptions.ValidationError( + self.error_messages['invalid'], + code='invalid', + params={'value': value}, + ) + + +__all__ = ["JSONField"] + +class JSONField(FutureDjangoJSONField): + def __init__(self, verbose_name=None, name=None, encoder=DjangoJSONEncoder, **kwargs): + super().__init__(verbose_name, name, encoder, **kwargs) diff --git a/taiga/base/fields.py b/taiga/base/fields.py index 3b19f15f..8017c10b 100644 --- a/taiga/base/fields.py +++ b/taiga/base/fields.py @@ -30,7 +30,7 @@ import serpy # NOTE: This should be in other place, for example taiga.base.api.serializers -class JsonField(serializers.WritableField): +class JSONField(serializers.WritableField): """ Json objects serializer. """ @@ -95,12 +95,12 @@ class I18NField(Field): return _(ret) -class I18NJsonField(Field): +class I18NJSONField(Field): """ Json objects serializer. """ def __init__(self, i18n_fields=(), *args, **kwargs): - super(I18NJsonField, self).__init__(*args, **kwargs) + super(I18NJSONField, self).__init__(*args, **kwargs) self.i18n_fields = i18n_fields def translate_values(self, d): diff --git a/taiga/base/management/commands/test_emails.py b/taiga/base/management/commands/test_emails.py index 631bcd42..f40dd7db 100644 --- a/taiga/base/management/commands/test_emails.py +++ b/taiga/base/management/commands/test_emails.py @@ -18,8 +18,6 @@ import datetime -from optparse import make_option - from django.apps import apps from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand @@ -33,28 +31,27 @@ from taiga.projects.history.services import get_history_queryset_by_model_instan class Command(BaseCommand): - args = '' - option_list = BaseCommand.option_list + ( - make_option('--locale', '-l', default=None, dest='locale', - help='Send emails in an specific language.'), - ) - help = 'Send an example of all emails' - def handle(self, *args, **options): - if len(args) != 1: - print("Usage: ./manage.py test_emails ") - return + def add_arguments(self, parser): + parser.add_argument('--locale', '-l', + default=None, + dest='locale', + help='Send emails in an specific language.') + parser.add_argument('email', + help='Emeil address to send sample emails.') + + def handle(self, *args, **options): locale = options.get('locale') - test_email = args[0] + email_address = options.get('email') # Register email context = {"lang": locale, "user": get_user_model().objects.all().order_by("?").first(), "cancel_token": "cancel-token"} - email = mail_builder.registered_user(test_email, context) + email = mail_builder.registered_user(email_address, context) email.send() # Membership invitation @@ -63,13 +60,13 @@ class Command(BaseCommand): membership.invitation_extra_text = "Text example, Text example,\nText example,\n\nText example" context = {"lang": locale, "membership": membership} - email = mail_builder.membership_invitation(test_email, context) + email = mail_builder.membership_invitation(email_address, context) email.send() # Membership notification context = {"lang": locale, "membership": Membership.objects.order_by("?").filter(user__isnull=False).first()} - email = mail_builder.membership_notification(test_email, context) + email = mail_builder.membership_notification(email_address, context) email.send() # Feedback @@ -85,17 +82,17 @@ class Command(BaseCommand): "key2": "value2", }, } - email = mail_builder.feedback_notification(test_email, context) + email = mail_builder.feedback_notification(email_address, context) email.send() # Password recovery context = {"lang": locale, "user": get_user_model().objects.all().order_by("?").first()} - email = mail_builder.password_recovery(test_email, context) + email = mail_builder.password_recovery(email_address, context) email.send() # Change email context = {"lang": locale, "user": get_user_model().objects.all().order_by("?").first()} - email = mail_builder.change_email(test_email, context) + email = mail_builder.change_email(email_address, context) email.send() # Export/Import emails @@ -106,7 +103,7 @@ class Command(BaseCommand): "error_subject": "Error generating project dump", "error_message": "Error generating project dump", } - email = mail_builder.export_error(test_email, context) + email = mail_builder.export_error(email_address, context) email.send() context = { "lang": locale, @@ -114,7 +111,7 @@ class Command(BaseCommand): "error_subject": "Error importing project dump", "error_message": "Error importing project dump", } - email = mail_builder.import_error(test_email, context) + email = mail_builder.import_error(email_address, context) email.send() deletion_date = timezone.now() + datetime.timedelta(seconds=60*60*24) @@ -125,7 +122,7 @@ class Command(BaseCommand): "project": Project.objects.all().order_by("?").first(), "deletion_date": deletion_date, } - email = mail_builder.dump_project(test_email, context) + email = mail_builder.dump_project(email_address, context) email.send() context = { @@ -133,7 +130,7 @@ class Command(BaseCommand): "user": get_user_model().objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(), } - email = mail_builder.load_dump(test_email, context) + email = mail_builder.load_dump(email_address, context) email.send() # Notification emails @@ -187,7 +184,7 @@ class Command(BaseCommand): cls = type("InlineCSSTemplateMail", (InlineCSSTemplateMail,), {"name": notification_email[1]}) email = cls() - email.send(test_email, context) + email.send(email_address, context) # Transfer Emails @@ -195,7 +192,7 @@ class Command(BaseCommand): "project": Project.objects.all().order_by("?").first(), "requester": User.objects.all().order_by("?").first(), } - email = mail_builder.transfer_request(test_email, context) + email = mail_builder.transfer_request(email_address, context) email.send() context = { @@ -204,7 +201,7 @@ class Command(BaseCommand): "token": "test-token", "reason": "Test reason" } - email = mail_builder.transfer_start(test_email, context) + email = mail_builder.transfer_start(email_address, context) email.send() context = { @@ -213,7 +210,7 @@ class Command(BaseCommand): "new_owner": User.objects.all().order_by("?").first(), "reason": "Test reason" } - email = mail_builder.transfer_accept(test_email, context) + email = mail_builder.transfer_accept(email_address, context) email.send() context = { @@ -221,5 +218,5 @@ class Command(BaseCommand): "rejecter": User.objects.all().order_by("?").first(), "reason": "Test reason" } - email = mail_builder.transfer_reject(test_email, context) + email = mail_builder.transfer_reject(email_address, context) email.send() diff --git a/taiga/base/routers.py b/taiga/base/routers.py index a7ccbdc4..119375e9 100644 --- a/taiga/base/routers.py +++ b/taiga/base/routers.py @@ -20,7 +20,7 @@ import itertools from collections import namedtuple -from django.conf.urls import patterns, url +from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import NoReverseMatch @@ -79,7 +79,7 @@ class BaseRouter(object): @property def urls(self): if not hasattr(self, '_urls'): - self._urls = patterns('', *self.get_urls()) + self._urls = self.get_urls() return self._urls diff --git a/taiga/export_import/validators/fields.py b/taiga/export_import/validators/fields.py index e3d33c7a..e3644351 100644 --- a/taiga/export_import/validators/fields.py +++ b/taiga/export_import/validators/fields.py @@ -26,7 +26,7 @@ from django.contrib.contenttypes.models import ContentType from taiga.base.api import serializers from taiga.base.exceptions import ValidationError -from taiga.base.fields import JsonField +from taiga.base.fields import JSONField from taiga.mdrender.service import render as mdrender from taiga.users import models as users_models @@ -144,7 +144,7 @@ class ProjectRelatedField(serializers.RelatedField): raise ValidationError(_("{}=\"{}\" not found in this project".format(self.slug_field, data))) -class HistoryUserField(JsonField): +class HistoryUserField(JSONField): def from_native(self, data): if data is None: return {} @@ -162,7 +162,7 @@ class HistoryUserField(JsonField): return {"pk": pk, "name": data[1]} -class HistoryValuesField(JsonField): +class HistoryValuesField(JSONField): def from_native(self, data): if data is None: return [] @@ -171,7 +171,7 @@ class HistoryValuesField(JsonField): return data -class HistoryDiffField(JsonField): +class HistoryDiffField(JSONField): def from_native(self, data): if data is None: return [] diff --git a/taiga/export_import/validators/mixins.py b/taiga/export_import/validators/mixins.py index d07334b6..f43ea5b6 100644 --- a/taiga/export_import/validators/mixins.py +++ b/taiga/export_import/validators/mixins.py @@ -28,13 +28,13 @@ from taiga.projects.notifications import services as notifications_services from taiga.projects.history import services as history_service from .fields import (UserRelatedField, HistoryUserField, HistoryDiffField, - JsonField, HistoryValuesField, CommentField, FileField) + JSONField, HistoryValuesField, CommentField, FileField) class HistoryExportValidator(validators.ModelValidator): user = HistoryUserField() diff = HistoryDiffField(required=False) - snapshot = JsonField(required=False) + snapshot = JSONField(required=False) values = HistoryValuesField(required=False) comment = CommentField(required=False) delete_comment_date = serializers.DateTimeField(required=False) diff --git a/taiga/export_import/validators/validators.py b/taiga/export_import/validators/validators.py index c821b531..4aeb0e1a 100644 --- a/taiga/export_import/validators/validators.py +++ b/taiga/export_import/validators/validators.py @@ -20,7 +20,7 @@ from django.utils.translation import ugettext as _ from taiga.base.api import serializers from taiga.base.api import validators -from taiga.base.fields import JsonField, PgArrayField +from taiga.base.fields import JSONField, PgArrayField from taiga.base.exceptions import ValidationError from taiga.projects import models as projects_models @@ -133,7 +133,7 @@ class IssueCustomAttributeExportValidator(validators.ModelValidator): class BaseCustomAttributesValuesExportValidator(validators.ModelValidator): - attributes_values = JsonField(source="attributes_values", required=True) + attributes_values = JSONField(source="attributes_values", required=True) _custom_attribute_model = None _container_field = None @@ -378,7 +378,7 @@ class ProjectExportValidator(WatcheableObjectModelValidatorMixin): issue_statuses = IssueStatusExportValidator(many=True, required=False) priorities = PriorityExportValidator(many=True, required=False) severities = SeverityExportValidator(many=True, required=False) - tags_colors = JsonField(required=False) + tags_colors = JSONField(required=False) creation_template = serializers.SlugRelatedField(slug_field="slug", required=False) default_points = serializers.SlugRelatedField(slug_field="name", required=False) default_us_status = serializers.SlugRelatedField(slug_field="name", required=False) diff --git a/taiga/projects/custom_attributes/migrations/0002_issuecustomattributesvalues_taskcustomattributesvalues_userstorycustomattributesvalues.py b/taiga/projects/custom_attributes/migrations/0002_issuecustomattributesvalues_taskcustomattributesvalues_userstorycustomattributesvalues.py index 8c1848db..a6c5084e 100644 --- a/taiga/projects/custom_attributes/migrations/0002_issuecustomattributesvalues_taskcustomattributesvalues_userstorycustomattributesvalues.py +++ b/taiga/projects/custom_attributes/migrations/0002_issuecustomattributesvalues_taskcustomattributesvalues_userstorycustomattributesvalues.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -20,7 +20,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), ('version', models.IntegerField(default=1, verbose_name='version')), - ('attributes_values', django_pgjson.fields.JsonField(default={}, verbose_name='attributes_values')), + ('attributes_values', taiga.base.db.models.fields.JSONField(default={}, verbose_name='attributes_values')), ('issue', models.OneToOneField(verbose_name='issue', to='issues.Issue', related_name='custom_attributes_values')), ], options={ @@ -36,7 +36,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), ('version', models.IntegerField(default=1, verbose_name='version')), - ('attributes_values', django_pgjson.fields.JsonField(default={}, verbose_name='attributes_values')), + ('attributes_values', taiga.base.db.models.fields.JSONField(default={}, verbose_name='attributes_values')), ('task', models.OneToOneField(verbose_name='task', to='tasks.Task', related_name='custom_attributes_values')), ], options={ @@ -52,7 +52,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), ('version', models.IntegerField(default=1, verbose_name='version')), - ('attributes_values', django_pgjson.fields.JsonField(default={}, verbose_name='attributes_values')), + ('attributes_values', taiga.base.db.models.fields.JSONField(default={}, verbose_name='attributes_values')), ('user_story', models.OneToOneField(verbose_name='user story', to='userstories.UserStory', related_name='custom_attributes_values')), ], options={ diff --git a/taiga/projects/custom_attributes/migrations/0005_auto_20150505_1639.py b/taiga/projects/custom_attributes/migrations/0005_auto_20150505_1639.py index 4fba06c5..4c6503e0 100644 --- a/taiga/projects/custom_attributes/migrations/0005_auto_20150505_1639.py +++ b/taiga/projects/custom_attributes/migrations/0005_auto_20150505_1639.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -15,19 +15,19 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='issuecustomattributesvalues', name='attributes_values', - field=django_pgjson.fields.JsonField(verbose_name='values', default={}), + field=taiga.base.db.models.fields.JSONField(verbose_name='values', default={}), preserve_default=True, ), migrations.AlterField( model_name='taskcustomattributesvalues', name='attributes_values', - field=django_pgjson.fields.JsonField(verbose_name='values', default={}), + field=taiga.base.db.models.fields.JSONField(verbose_name='values', default={}), preserve_default=True, ), migrations.AlterField( model_name='userstorycustomattributesvalues', name='attributes_values', - field=django_pgjson.fields.JsonField(verbose_name='values', default={}), + field=taiga.base.db.models.fields.JSONField(verbose_name='values', default={}), preserve_default=True, ), ] diff --git a/taiga/projects/custom_attributes/migrations/0009_auto_20160728_1002.py b/taiga/projects/custom_attributes/migrations/0009_auto_20160728_1002.py index 313e22fd..a5e84f6d 100644 --- a/taiga/projects/custom_attributes/migrations/0009_auto_20160728_1002.py +++ b/taiga/projects/custom_attributes/migrations/0009_auto_20160728_1002.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion import django.utils.timezone -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -55,7 +55,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('version', models.IntegerField(default=1, verbose_name='version')), - ('attributes_values', django_pgjson.fields.JsonField(default={}, verbose_name='values')), + ('attributes_values', taiga.base.db.models.fields.JSONField(default={}, verbose_name='values')), ('epic', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_attributes_values', to='epics.Epic', verbose_name='epic')), ], options={ diff --git a/taiga/projects/custom_attributes/migrations/0011_json_to_jsonb.py b/taiga/projects/custom_attributes/migrations/0011_json_to_jsonb.py new file mode 100644 index 00000000..e9a17bcc --- /dev/null +++ b/taiga/projects/custom_attributes/migrations/0011_json_to_jsonb.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:34 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + +class Migration(migrations.Migration): + + dependencies = [ + ('custom_attributes', '0010_auto_20160928_0540'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="custom_attributes_epiccustomattributesvalues", + column_name="attributes_values", + ), + reverse_sql=migrations.RunSQL.noop + ), + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="custom_attributes_userstorycustomattributesvalues", + column_name="attributes_values", + ), + reverse_sql=migrations.RunSQL.noop + ), + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="custom_attributes_taskcustomattributesvalues", + column_name="attributes_values", + ), + reverse_sql=migrations.RunSQL.noop + ), + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="custom_attributes_issuecustomattributesvalues", + column_name="attributes_values", + ), + reverse_sql=migrations.RunSQL.noop + ), + + # Function: Remove a key in a json field + migrations.RunSQL( + """ + CREATE OR REPLACE FUNCTION "json_object_delete_keys"("json" jsonb, VARIADIC "keys_to_delete" text[]) + RETURNS jsonb + LANGUAGE sql + IMMUTABLE + STRICT + AS $function$ + SELECT COALESCE ((SELECT ('{' || string_agg(to_json("key") || ':' || "value", ',') || '}') + FROM jsonb_each("json") + WHERE "key" <> ALL ("keys_to_delete")), + '{}')::text::jsonb $function$; + """, + reverse_sql=""" + CREATE OR REPLACE FUNCTION "json_object_delete_keys"("json" json, VARIADIC "keys_to_delete" text[]) + RETURNS json + LANGUAGE sql + IMMUTABLE + STRICT + AS $function$ + SELECT COALESCE ((SELECT ('{' || string_agg(to_json("key") || ':' || "value", ',') || '}') + FROM json_each("json") + WHERE "key" <> ALL ("keys_to_delete")), + '{}')::json $function$;""" + ), + ] diff --git a/taiga/projects/custom_attributes/models.py b/taiga/projects/custom_attributes/models.py index 6467f97e..45269051 100644 --- a/taiga/projects/custom_attributes/models.py +++ b/taiga/projects/custom_attributes/models.py @@ -20,7 +20,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils import timezone -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from taiga.base.utils.time import timestamp_ms from taiga.projects.occ.mixins import OCCModelMixin @@ -92,7 +92,7 @@ class IssueCustomAttribute(AbstractCustomAttribute): ####################################################### class AbstractCustomAttributesValues(OCCModelMixin, models.Model): - attributes_values = JsonField(null=False, blank=False, default={}, verbose_name=_("values")) + attributes_values = JSONField(null=False, blank=False, default={}, verbose_name=_("values")) class Meta: abstract = True diff --git a/taiga/projects/custom_attributes/serializers.py b/taiga/projects/custom_attributes/serializers.py index 10e9c756..bf31792a 100644 --- a/taiga/projects/custom_attributes/serializers.py +++ b/taiga/projects/custom_attributes/serializers.py @@ -17,7 +17,7 @@ # along with this program. If not, see . -from taiga.base.fields import JsonField, Field +from taiga.base.fields import JSONField, Field from taiga.base.api import serializers diff --git a/taiga/projects/custom_attributes/validators.py b/taiga/projects/custom_attributes/validators.py index 4169eee6..98fd494b 100644 --- a/taiga/projects/custom_attributes/validators.py +++ b/taiga/projects/custom_attributes/validators.py @@ -19,7 +19,7 @@ from django.utils.translation import ugettext_lazy as _ -from taiga.base.fields import JsonField +from taiga.base.fields import JSONField from taiga.base.exceptions import ValidationError from taiga.base.api.validators import ModelValidator @@ -92,7 +92,7 @@ class IssueCustomAttributeValidator(BaseCustomAttributeValidator): class BaseCustomAttributesValuesValidator(ModelValidator): - attributes_values = JsonField(source="attributes_values", label="attributes values") + attributes_values = JSONField(source="attributes_values", label="attributes values") _custom_attribute_model = None _container_field = None diff --git a/taiga/projects/fixtures/initial_project_templates.json b/taiga/projects/fixtures/initial_project_templates.json index 6137792f..e4c92cd0 100644 --- a/taiga/projects/fixtures/initial_project_templates.json +++ b/taiga/projects/fixtures/initial_project_templates.json @@ -17,16 +17,554 @@ "is_issues_activated": true, "videoconferences": null, "videoconferences_extra_data": "", - "default_options": "{\"epic_status\": \"New\", \"issue_status\": \"New\", \"task_status\": \"New\", \"points\": \"?\", \"issue_type\": \"Bug\", \"severity\": \"Normal\", \"priority\": \"Normal\", \"us_status\": \"New\"}", - "epic_statuses": "[{\"is_closed\": false, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"order\": 1}, {\"is_closed\": false, \"name\": \"Ready\", \"color\": \"#ff8a84\", \"slug\": \"ready\", \"order\": 2}, {\"is_closed\": false, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"order\": 3}, {\"is_closed\": false, \"name\": \"Ready for test\", \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"order\": 4}, {\"is_closed\": true, \"name\": \"Done\", \"color\": \"#669900\", \"slug\": \"done\", \"order\": 5}]", - "us_statuses": "[{\"is_archived\": false, \"name\": \"New\", \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"Ready\", \"slug\": \"ready\", \"order\": 2, \"color\": \"#ff8a84\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"In progress\", \"slug\": \"in-progress\", \"order\": 3, \"color\": \"#ff9900\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"order\": 4, \"color\": \"#fcc000\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"Done\", \"slug\": \"done\", \"order\": 5, \"color\": \"#669900\", \"wip_limit\": null, \"is_closed\": true}, {\"is_archived\": true, \"name\": \"Archived\", \"slug\": \"archived\", \"order\": 6, \"color\": \"#5c3566\", \"wip_limit\": null, \"is_closed\": true}]", - "points": "[{\"value\": null, \"name\": \"?\", \"order\": 1}, {\"value\": 0.0, \"name\": \"0\", \"order\": 2}, {\"value\": 0.5, \"name\": \"1/2\", \"order\": 3}, {\"value\": 1.0, \"name\": \"1\", \"order\": 4}, {\"value\": 2.0, \"name\": \"2\", \"order\": 5}, {\"value\": 3.0, \"name\": \"3\", \"order\": 6}, {\"value\": 5.0, \"name\": \"5\", \"order\": 7}, {\"value\": 8.0, \"name\": \"8\", \"order\": 8}, {\"value\": 10.0, \"name\": \"10\", \"order\": 9}, {\"value\": 13.0, \"name\": \"13\", \"order\": 10}, {\"value\": 20.0, \"name\": \"20\", \"order\": 11}, {\"value\": 40.0, \"name\": \"40\", \"order\": 12}]", - "task_statuses": "[{\"is_closed\": false, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"order\": 1}, {\"is_closed\": false, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"order\": 2}, {\"is_closed\": true, \"name\": \"Ready for test\", \"color\": \"#ffcc00\", \"slug\": \"ready-for-test\", \"order\": 3}, {\"is_closed\": true, \"name\": \"Closed\", \"color\": \"#669900\", \"slug\": \"closed\", \"order\": 4}, {\"is_closed\": false, \"name\": \"Needs Info\", \"color\": \"#999999\", \"slug\": \"needs-info\", \"order\": 5}]", - "issue_statuses": "[{\"is_closed\": false, \"name\": \"New\", \"color\": \"#8C2318\", \"slug\": \"new\", \"order\": 1}, {\"is_closed\": false, \"name\": \"In progress\", \"color\": \"#5E8C6A\", \"slug\": \"in-progress\", \"order\": 2}, {\"is_closed\": true, \"name\": \"Ready for test\", \"color\": \"#88A65E\", \"slug\": \"ready-for-test\", \"order\": 3}, {\"is_closed\": true, \"name\": \"Closed\", \"color\": \"#BFB35A\", \"slug\": \"closed\", \"order\": 4}, {\"is_closed\": false, \"name\": \"Needs Info\", \"color\": \"#89BAB4\", \"slug\": \"needs-info\", \"order\": 5}, {\"is_closed\": true, \"name\": \"Rejected\", \"color\": \"#CC0000\", \"slug\": \"rejected\", \"order\": 6}, {\"is_closed\": false, \"name\": \"Postponed\", \"color\": \"#666666\", \"slug\": \"posponed\", \"order\": 7}]", - "issue_types": "[{\"name\": \"Bug\", \"color\": \"#89BAB4\", \"order\": 1}, {\"name\": \"Question\", \"color\": \"#ba89a8\", \"order\": 2}, {\"name\": \"Enhancement\", \"color\": \"#89a8ba\", \"order\": 3}]", - "priorities": "[{\"name\": \"Low\", \"color\": \"#666666\", \"order\": 1}, {\"name\": \"Normal\", \"color\": \"#669933\", \"order\": 3}, {\"name\": \"High\", \"color\": \"#CC0000\", \"order\": 5}]", - "severities": "[{\"name\": \"Wishlist\", \"color\": \"#666666\", \"order\": 1}, {\"name\": \"Minor\", \"color\": \"#669933\", \"order\": 2}, {\"name\": \"Normal\", \"color\": \"#0000FF\", \"order\": 3}, {\"name\": \"Important\", \"color\": \"#FFA500\", \"order\": 4}, {\"name\": \"Critical\", \"color\": \"#CC0000\", \"order\": 5}]", - "roles": "[{\"name\": \"UX\", \"computable\": true, \"slug\": \"ux\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 10}, {\"name\": \"Design\", \"computable\": true, \"slug\": \"design\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 20}, {\"name\": \"Front\", \"computable\": true, \"slug\": \"front\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 30}, {\"name\": \"Back\", \"computable\": true, \"slug\": \"back\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 40}, {\"name\": \"Product Owner\", \"computable\": false, \"slug\": \"product-owner\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 50}, {\"name\": \"Stakeholder\", \"computable\": false, \"slug\": \"stakeholder\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 60}]" + "default_options": { + "points": "?", + "priority": "Normal", + "us_status": "New", + "issue_type": "Bug", + "epic_status": "New", + "severity": "Normal", + "task_status": "New", + "issue_status": "New" + }, + "epic_statuses": [ + { + "slug": "new", + "color": "#999999", + "name": "New", + "order": 1, + "is_closed": false + }, + { + "slug": "ready", + "color": "#ff8a84", + "name": "Ready", + "order": 2, + "is_closed": false + }, + { + "slug": "in-progress", + "color": "#ff9900", + "name": "In progress", + "order": 3, + "is_closed": false + }, + { + "slug": "ready-for-test", + "color": "#fcc000", + "name": "Ready for test", + "order": 4, + "is_closed": false + }, + { + "slug": "done", + "color": "#669900", + "name": "Done", + "order": 5, + "is_closed": true + } + ], + "us_statuses": [ + { + "slug": "new", + "name": "New", + "order": 1, + "is_archived": false, + "color": "#999999", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "ready", + "name": "Ready", + "order": 2, + "is_archived": false, + "color": "#ff8a84", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "in-progress", + "name": "In progress", + "order": 3, + "is_archived": false, + "color": "#ff9900", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "ready-for-test", + "name": "Ready for test", + "order": 4, + "is_archived": false, + "color": "#fcc000", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "done", + "name": "Done", + "order": 5, + "is_archived": false, + "color": "#669900", + "is_closed": true, + "wip_limit": null + }, + { + "slug": "archived", + "name": "Archived", + "order": 6, + "is_archived": true, + "color": "#5c3566", + "is_closed": true, + "wip_limit": null + } + ], + "points": [ + { + "name": "?", + "order": 1, + "value": null + }, + { + "name": "0", + "order": 2, + "value": 0.0 + }, + { + "name": "1/2", + "order": 3, + "value": 0.5 + }, + { + "name": "1", + "order": 4, + "value": 1.0 + }, + { + "name": "2", + "order": 5, + "value": 2.0 + }, + { + "name": "3", + "order": 6, + "value": 3.0 + }, + { + "name": "5", + "order": 7, + "value": 5.0 + }, + { + "name": "8", + "order": 8, + "value": 8.0 + }, + { + "name": "10", + "order": 9, + "value": 10.0 + }, + { + "name": "13", + "order": 10, + "value": 13.0 + }, + { + "name": "20", + "order": 11, + "value": 20.0 + }, + { + "name": "40", + "order": 12, + "value": 40.0 + } + ], + "task_statuses": [ + { + "slug": "new", + "color": "#999999", + "name": "New", + "order": 1, + "is_closed": false + }, + { + "slug": "in-progress", + "color": "#ff9900", + "name": "In progress", + "order": 2, + "is_closed": false + }, + { + "slug": "ready-for-test", + "color": "#ffcc00", + "name": "Ready for test", + "order": 3, + "is_closed": true + }, + { + "slug": "closed", + "color": "#669900", + "name": "Closed", + "order": 4, + "is_closed": true + }, + { + "slug": "needs-info", + "color": "#999999", + "name": "Needs Info", + "order": 5, + "is_closed": false + } + ], + "issue_statuses": [ + { + "slug": "new", + "color": "#8C2318", + "name": "New", + "order": 1, + "is_closed": false + }, + { + "slug": "in-progress", + "color": "#5E8C6A", + "name": "In progress", + "order": 2, + "is_closed": false + }, + { + "slug": "ready-for-test", + "color": "#88A65E", + "name": "Ready for test", + "order": 3, + "is_closed": true + }, + { + "slug": "closed", + "color": "#BFB35A", + "name": "Closed", + "order": 4, + "is_closed": true + }, + { + "slug": "needs-info", + "color": "#89BAB4", + "name": "Needs Info", + "order": 5, + "is_closed": false + }, + { + "slug": "rejected", + "color": "#CC0000", + "name": "Rejected", + "order": 6, + "is_closed": true + }, + { + "slug": "posponed", + "color": "#666666", + "name": "Postponed", + "order": 7, + "is_closed": false + } + ], + "issue_types": [ + { + "color": "#89BAB4", + "name": "Bug", + "order": 1 + }, + { + "color": "#ba89a8", + "name": "Question", + "order": 2 + }, + { + "color": "#89a8ba", + "name": "Enhancement", + "order": 3 + } + ], + "priorities": [ + { + "color": "#666666", + "name": "Low", + "order": 1 + }, + { + "color": "#669933", + "name": "Normal", + "order": 3 + }, + { + "color": "#CC0000", + "name": "High", + "order": 5 + } + ], + "severities": [ + { + "color": "#666666", + "name": "Wishlist", + "order": 1 + }, + { + "color": "#669933", + "name": "Minor", + "order": 2 + }, + { + "color": "#0000FF", + "name": "Normal", + "order": 3 + }, + { + "color": "#FFA500", + "name": "Important", + "order": 4 + }, + { + "color": "#CC0000", + "name": "Critical", + "order": 5 + } + ], + "roles": [ + { + "slug": "ux", + "computable": true, + "name": "UX", + "order": 10, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "design", + "computable": true, + "name": "Design", + "order": 20, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "front", + "computable": true, + "name": "Front", + "order": 30, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "back", + "computable": true, + "name": "Back", + "order": 40, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "product-owner", + "computable": false, + "name": "Product Owner", + "order": 50, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "stakeholder", + "computable": false, + "name": "Stakeholder", + "order": 60, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "view_milestones", + "view_project", + "view_tasks", + "view_us", + "modify_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + } + ] } }, { @@ -47,16 +585,554 @@ "is_issues_activated": false, "videoconferences": null, "videoconferences_extra_data": "", - "default_options": "{\"epic_status\": \"New\", \"issue_status\": \"New\", \"task_status\": \"New\", \"points\": \"?\", \"issue_type\": \"Bug\", \"severity\": \"Normal\", \"priority\": \"Normal\", \"us_status\": \"New\"}", - "epic_statuses": "[{\"is_closed\": false, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"order\": 1}, {\"is_closed\": false, \"name\": \"Ready\", \"color\": \"#ff8a84\", \"slug\": \"ready\", \"order\": 2}, {\"is_closed\": false, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"order\": 3}, {\"is_closed\": false, \"name\": \"Ready for test\", \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"order\": 4}, {\"is_closed\": true, \"name\": \"Done\", \"color\": \"#669900\", \"slug\": \"done\", \"order\": 5}]", - "us_statuses": "[{\"is_archived\": false, \"name\": \"New\", \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"Ready\", \"slug\": \"ready\", \"order\": 2, \"color\": \"#f57900\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"In progress\", \"slug\": \"in-progress\", \"order\": 3, \"color\": \"#729fcf\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"order\": 4, \"color\": \"#4e9a06\", \"wip_limit\": null, \"is_closed\": false}, {\"is_archived\": false, \"name\": \"Done\", \"slug\": \"done\", \"order\": 5, \"color\": \"#cc0000\", \"wip_limit\": null, \"is_closed\": true}, {\"is_archived\": true, \"name\": \"Archived\", \"slug\": \"archived\", \"order\": 6, \"color\": \"#5c3566\", \"wip_limit\": null, \"is_closed\": true}]", - "points": "[{\"value\": null, \"name\": \"?\", \"order\": 1}, {\"value\": 0.0, \"name\": \"0\", \"order\": 2}, {\"value\": 0.5, \"name\": \"1/2\", \"order\": 3}, {\"value\": 1.0, \"name\": \"1\", \"order\": 4}, {\"value\": 2.0, \"name\": \"2\", \"order\": 5}, {\"value\": 3.0, \"name\": \"3\", \"order\": 6}, {\"value\": 5.0, \"name\": \"5\", \"order\": 7}, {\"value\": 8.0, \"name\": \"8\", \"order\": 8}, {\"value\": 10.0, \"name\": \"10\", \"order\": 9}, {\"value\": 13.0, \"name\": \"13\", \"order\": 10}, {\"value\": 20.0, \"name\": \"20\", \"order\": 11}, {\"value\": 40.0, \"name\": \"40\", \"order\": 12}]", - "task_statuses": "[{\"is_closed\": false, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"order\": 1}, {\"is_closed\": false, \"name\": \"In progress\", \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"order\": 2}, {\"is_closed\": true, \"name\": \"Ready for test\", \"color\": \"#f57900\", \"slug\": \"ready-for-test\", \"order\": 3}, {\"is_closed\": true, \"name\": \"Closed\", \"color\": \"#4e9a06\", \"slug\": \"closed\", \"order\": 4}, {\"is_closed\": false, \"name\": \"Needs Info\", \"color\": \"#cc0000\", \"slug\": \"needs-info\", \"order\": 5}]", - "issue_statuses": "[{\"is_closed\": false, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"order\": 1}, {\"is_closed\": false, \"name\": \"In progress\", \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"order\": 2}, {\"is_closed\": true, \"name\": \"Ready for test\", \"color\": \"#f57900\", \"slug\": \"ready-for-test\", \"order\": 3}, {\"is_closed\": true, \"name\": \"Closed\", \"color\": \"#4e9a06\", \"slug\": \"closed\", \"order\": 4}, {\"is_closed\": false, \"name\": \"Needs Info\", \"color\": \"#cc0000\", \"slug\": \"needs-info\", \"order\": 5}, {\"is_closed\": true, \"name\": \"Rejected\", \"color\": \"#d3d7cf\", \"slug\": \"rejected\", \"order\": 6}, {\"is_closed\": false, \"name\": \"Postponed\", \"color\": \"#75507b\", \"slug\": \"posponed\", \"order\": 7}]", - "issue_types": "[{\"name\": \"Bug\", \"color\": \"#cc0000\", \"order\": 1}, {\"name\": \"Question\", \"color\": \"#729fcf\", \"order\": 2}, {\"name\": \"Enhancement\", \"color\": \"#4e9a06\", \"order\": 3}]", - "priorities": "[{\"name\": \"Low\", \"color\": \"#999999\", \"order\": 1}, {\"name\": \"Normal\", \"color\": \"#4e9a06\", \"order\": 3}, {\"name\": \"High\", \"color\": \"#CC0000\", \"order\": 5}]", - "severities": "[{\"name\": \"Wishlist\", \"color\": \"#999999\", \"order\": 1}, {\"name\": \"Minor\", \"color\": \"#729fcf\", \"order\": 2}, {\"name\": \"Normal\", \"color\": \"#4e9a06\", \"order\": 3}, {\"name\": \"Important\", \"color\": \"#f57900\", \"order\": 4}, {\"name\": \"Critical\", \"color\": \"#CC0000\", \"order\": 5}]", - "roles": "[{\"name\": \"UX\", \"computable\": true, \"slug\": \"ux\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 10}, {\"name\": \"Design\", \"computable\": true, \"slug\": \"design\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 20}, {\"name\": \"Front\", \"computable\": true, \"slug\": \"front\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 30}, {\"name\": \"Back\", \"computable\": true, \"slug\": \"back\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 40}, {\"name\": \"Product Owner\", \"computable\": false, \"slug\": \"product-owner\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 50}, {\"name\": \"Stakeholder\", \"computable\": false, \"slug\": \"stakeholder\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"comment_epic\", \"comment_us\", \"comment_task\", \"comment_issue\", \"comment_wiki_page\"], \"order\": 60}]" + "default_options": { + "points": "?", + "priority": "Normal", + "us_status": "New", + "issue_type": "Bug", + "epic_status": "New", + "severity": "Normal", + "task_status": "New", + "issue_status": "New" + }, + "epic_statuses": [ + { + "slug": "new", + "color": "#999999", + "name": "New", + "order": 1, + "is_closed": false + }, + { + "slug": "ready", + "color": "#ff8a84", + "name": "Ready", + "order": 2, + "is_closed": false + }, + { + "slug": "in-progress", + "color": "#ff9900", + "name": "In progress", + "order": 3, + "is_closed": false + }, + { + "slug": "ready-for-test", + "color": "#fcc000", + "name": "Ready for test", + "order": 4, + "is_closed": false + }, + { + "slug": "done", + "color": "#669900", + "name": "Done", + "order": 5, + "is_closed": true + } + ], + "us_statuses": [ + { + "slug": "new", + "name": "New", + "order": 1, + "is_archived": false, + "color": "#999999", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "ready", + "name": "Ready", + "order": 2, + "is_archived": false, + "color": "#f57900", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "in-progress", + "name": "In progress", + "order": 3, + "is_archived": false, + "color": "#729fcf", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "ready-for-test", + "name": "Ready for test", + "order": 4, + "is_archived": false, + "color": "#4e9a06", + "is_closed": false, + "wip_limit": null + }, + { + "slug": "done", + "name": "Done", + "order": 5, + "is_archived": false, + "color": "#cc0000", + "is_closed": true, + "wip_limit": null + }, + { + "slug": "archived", + "name": "Archived", + "order": 6, + "is_archived": true, + "color": "#5c3566", + "is_closed": true, + "wip_limit": null + } + ], + "points": [ + { + "name": "?", + "order": 1, + "value": null + }, + { + "name": "0", + "order": 2, + "value": 0.0 + }, + { + "name": "1/2", + "order": 3, + "value": 0.5 + }, + { + "name": "1", + "order": 4, + "value": 1.0 + }, + { + "name": "2", + "order": 5, + "value": 2.0 + }, + { + "name": "3", + "order": 6, + "value": 3.0 + }, + { + "name": "5", + "order": 7, + "value": 5.0 + }, + { + "name": "8", + "order": 8, + "value": 8.0 + }, + { + "name": "10", + "order": 9, + "value": 10.0 + }, + { + "name": "13", + "order": 10, + "value": 13.0 + }, + { + "name": "20", + "order": 11, + "value": 20.0 + }, + { + "name": "40", + "order": 12, + "value": 40.0 + } + ], + "task_statuses": [ + { + "slug": "new", + "color": "#999999", + "name": "New", + "order": 1, + "is_closed": false + }, + { + "slug": "in-progress", + "color": "#729fcf", + "name": "In progress", + "order": 2, + "is_closed": false + }, + { + "slug": "ready-for-test", + "color": "#f57900", + "name": "Ready for test", + "order": 3, + "is_closed": true + }, + { + "slug": "closed", + "color": "#4e9a06", + "name": "Closed", + "order": 4, + "is_closed": true + }, + { + "slug": "needs-info", + "color": "#cc0000", + "name": "Needs Info", + "order": 5, + "is_closed": false + } + ], + "issue_statuses": [ + { + "slug": "new", + "color": "#999999", + "name": "New", + "order": 1, + "is_closed": false + }, + { + "slug": "in-progress", + "color": "#729fcf", + "name": "In progress", + "order": 2, + "is_closed": false + }, + { + "slug": "ready-for-test", + "color": "#f57900", + "name": "Ready for test", + "order": 3, + "is_closed": true + }, + { + "slug": "closed", + "color": "#4e9a06", + "name": "Closed", + "order": 4, + "is_closed": true + }, + { + "slug": "needs-info", + "color": "#cc0000", + "name": "Needs Info", + "order": 5, + "is_closed": false + }, + { + "slug": "rejected", + "color": "#d3d7cf", + "name": "Rejected", + "order": 6, + "is_closed": true + }, + { + "slug": "posponed", + "color": "#75507b", + "name": "Postponed", + "order": 7, + "is_closed": false + } + ], + "issue_types": [ + { + "color": "#cc0000", + "name": "Bug", + "order": 1 + }, + { + "color": "#729fcf", + "name": "Question", + "order": 2 + }, + { + "color": "#4e9a06", + "name": "Enhancement", + "order": 3 + } + ], + "priorities": [ + { + "color": "#999999", + "name": "Low", + "order": 1 + }, + { + "color": "#4e9a06", + "name": "Normal", + "order": 3 + }, + { + "color": "#CC0000", + "name": "High", + "order": 5 + } + ], + "severities": [ + { + "color": "#999999", + "name": "Wishlist", + "order": 1 + }, + { + "color": "#729fcf", + "name": "Minor", + "order": 2 + }, + { + "color": "#4e9a06", + "name": "Normal", + "order": 3 + }, + { + "color": "#f57900", + "name": "Important", + "order": 4 + }, + { + "color": "#CC0000", + "name": "Critical", + "order": 5 + } + ], + "roles": [ + { + "slug": "ux", + "computable": true, + "name": "UX", + "order": 10, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "design", + "computable": true, + "name": "Design", + "order": 20, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "front", + "computable": true, + "name": "Front", + "order": 30, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "back", + "computable": true, + "name": "Back", + "order": 40, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "product-owner", + "computable": false, + "name": "Product Owner", + "order": 50, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "add_milestone", + "modify_milestone", + "delete_milestone", + "view_milestones", + "view_project", + "add_task", + "modify_task", + "delete_task", + "view_tasks", + "add_us", + "modify_us", + "delete_us", + "view_us", + "add_wiki_page", + "modify_wiki_page", + "delete_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "add_epic", + "modify_epic", + "delete_epic", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + }, + { + "slug": "stakeholder", + "computable": false, + "name": "Stakeholder", + "order": 60, + "permissions": [ + "add_issue", + "modify_issue", + "delete_issue", + "view_issues", + "view_milestones", + "view_project", + "view_tasks", + "view_us", + "modify_wiki_page", + "view_wiki_pages", + "add_wiki_link", + "delete_wiki_link", + "view_wiki_links", + "view_epics", + "comment_epic", + "comment_us", + "comment_task", + "comment_issue", + "comment_wiki_page" + ] + } + ] } } ] diff --git a/taiga/projects/history/migrations/0001_initial.py b/taiga/projects/history/migrations/0001_initial.py index 053149d7..5113e110 100644 --- a/taiga/projects/history/migrations/0001_initial.py +++ b/taiga/projects/history/migrations/0001_initial.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import models, migrations import taiga.projects.history.models import django.utils.timezone -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -17,14 +17,14 @@ class Migration(migrations.Migration): name='HistoryEntry', fields=[ ('id', models.CharField(primary_key=True, unique=True, max_length=255, serialize=False, default=taiga.projects.history.models._generate_uuid, editable=False)), - ('user', django_pgjson.fields.JsonField(default=None, blank=True, null=True)), + ('user', taiga.base.db.models.fields.JSONField(default=None, blank=True, null=True)), ('created_at', models.DateTimeField(default=django.utils.timezone.now)), ('type', models.SmallIntegerField(choices=[(1, 'Change'), (2, 'Create'), (3, 'Delete')])), ('is_snapshot', models.BooleanField(default=False)), ('key', models.CharField(max_length=255, default=None, blank=True, null=True)), - ('diff', django_pgjson.fields.JsonField(default=None, null=True)), - ('snapshot', django_pgjson.fields.JsonField(default=None, null=True)), - ('values', django_pgjson.fields.JsonField(default=None, null=True)), + ('diff', taiga.base.db.models.fields.JSONField(default=None, null=True)), + ('snapshot', taiga.base.db.models.fields.JSONField(default=None, null=True)), + ('values', taiga.base.db.models.fields.JSONField(default=None, null=True)), ('comment', models.TextField(blank=True)), ('comment_html', models.TextField(blank=True)), ], diff --git a/taiga/projects/history/migrations/0003_auto_20140917_1405.py b/taiga/projects/history/migrations/0003_auto_20140917_1405.py index bb4378fa..13d22518 100644 --- a/taiga/projects/history/migrations/0003_auto_20140917_1405.py +++ b/taiga/projects/history/migrations/0003_auto_20140917_1405.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings -import django_pgjson.fields +import taiga.base.db.models.fields def change_fk_with_tuple_pk_and_name(apps, schema_editor): HistoryEntry = apps.get_model("history", "HistoryEntry") @@ -31,7 +31,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='historyentry', name='delete_comment_user', - field=django_pgjson.fields.JsonField(null=True, blank=True, default=None), + field=taiga.base.db.models.fields.JSONField(null=True, blank=True, default=None), preserve_default=True, ), diff --git a/taiga/projects/history/migrations/0006_fix_json_field_not_null.py b/taiga/projects/history/migrations/0006_fix_json_field_not_null.py index 8c8ae187..a72d1cce 100644 --- a/taiga/projects/history/migrations/0006_fix_json_field_not_null.py +++ b/taiga/projects/history/migrations/0006_fix_json_field_not_null.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from django.db import models, migrations diff --git a/taiga/projects/history/migrations/0008_auto_20150508_1028.py b/taiga/projects/history/migrations/0008_auto_20150508_1028.py index bf82d6b3..5b4b07fa 100644 --- a/taiga/projects/history/migrations/0008_auto_20150508_1028.py +++ b/taiga/projects/history/migrations/0008_auto_20150508_1028.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -15,19 +15,19 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='historyentry', name='diff', - field=django_pgjson.fields.JsonField(null=True, default=None, blank=True), + field=taiga.base.db.models.fields.JSONField(null=True, default=None, blank=True), preserve_default=True, ), migrations.AlterField( model_name='historyentry', name='snapshot', - field=django_pgjson.fields.JsonField(null=True, default=None, blank=True), + field=taiga.base.db.models.fields.JSONField(null=True, default=None, blank=True), preserve_default=True, ), migrations.AlterField( model_name='historyentry', name='values', - field=django_pgjson.fields.JsonField(null=True, default=None, blank=True), + field=taiga.base.db.models.fields.JSONField(null=True, default=None, blank=True), preserve_default=True, ), ] diff --git a/taiga/projects/history/migrations/0009_auto_20160512_1110.py b/taiga/projects/history/migrations/0009_auto_20160512_1110.py index 0cf39023..0e293806 100644 --- a/taiga/projects/history/migrations/0009_auto_20160512_1110.py +++ b/taiga/projects/history/migrations/0009_auto_20160512_1110.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import migrations, models -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -16,7 +16,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='historyentry', name='comment_versions', - field=django_pgjson.fields.JsonField(blank=True, default=None, null=True), + field=taiga.base.db.models.fields.JSONField(blank=True, default=None, null=True), ), migrations.AddField( model_name='historyentry', diff --git a/taiga/projects/history/migrations/0013_historyentry_values_diff_cache.py b/taiga/projects/history/migrations/0013_historyentry_values_diff_cache.py index 1fe73a96..625d2343 100644 --- a/taiga/projects/history/migrations/0013_historyentry_values_diff_cache.py +++ b/taiga/projects/history/migrations/0013_historyentry_values_diff_cache.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='historyentry', name='values_diff_cache', - field=django_pgjson.fields.JsonField(blank=True, default=None, null=True), + field=taiga.base.db.models.fields.JSONField(blank=True, default=None, null=True), ), ] diff --git a/taiga/projects/history/migrations/0014_json_to_jsonb.py b/taiga/projects/history/migrations/0014_json_to_jsonb.py new file mode 100644 index 00000000..7906f0ac --- /dev/null +++ b/taiga/projects/history/migrations/0014_json_to_jsonb.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:34 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + + +class Migration(migrations.Migration): + + dependencies = [ + ('history', '0013_historyentry_values_diff_cache'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "history_historyentry" + ALTER COLUMN "delete_comment_user" TYPE jsonb USING to_json("delete_comment_user"::text)::jsonb, + ALTER COLUMN "comment_versions" TYPE jsonb USING to_json("comment_versions"::text)::jsonb, + ALTER COLUMN "values_diff_cache" TYPE jsonb USING to_json("values_diff_cache"::text)::jsonb, + ALTER COLUMN "user" TYPE jsonb USING to_json("user"::text)::jsonb, + ALTER COLUMN "diff" TYPE jsonb USING to_json("diff"::text)::jsonb, + ALTER COLUMN "snapshot" TYPE jsonb USING to_json("snapshot"::text)::jsonb, + ALTER COLUMN "values" TYPE jsonb USING to_json("values"::text)::jsonb; + """, + reverse_sql=migrations.RunSQL.noop + ), + ] diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index c4155eee..9f60b0ab 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -22,7 +22,7 @@ from django.utils import timezone from django.db import models from django.contrib.auth import get_user_model from django.utils.functional import cached_property -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from taiga.mdrender.service import get_diff_of_htmls @@ -52,32 +52,32 @@ class HistoryEntry(models.Model): editable=False, default=_generate_uuid) project = models.ForeignKey("projects.Project") - user = JsonField(null=True, blank=True, default=None) + user = JSONField(null=True, blank=True, default=None) created_at = models.DateTimeField(default=timezone.now) type = models.SmallIntegerField(choices=HISTORY_TYPE_CHOICES) key = models.CharField(max_length=255, null=True, default=None, blank=True, db_index=True) # Stores the last diff - diff = JsonField(null=True, blank=True, default=None) + diff = JSONField(null=True, blank=True, default=None) # Stores the values_diff cache - values_diff_cache = JsonField(null=True, blank=True, default=None) + values_diff_cache = JSONField(null=True, blank=True, default=None) # Stores the last complete frozen object snapshot - snapshot = JsonField(null=True, blank=True, default=None) + snapshot = JSONField(null=True, blank=True, default=None) # Stores a values of all identifiers used in - values = JsonField(null=True, blank=True, default=None) + values = JSONField(null=True, blank=True, default=None) # Stores a comment comment = models.TextField(blank=True) comment_html = models.TextField(blank=True) delete_comment_date = models.DateTimeField(null=True, blank=True, default=None) - delete_comment_user = JsonField(null=True, blank=True, default=None) + delete_comment_user = JSONField(null=True, blank=True, default=None) # Historic version of comments - comment_versions = JsonField(null=True, blank=True, default=None) + comment_versions = JSONField(null=True, blank=True, default=None) edit_comment_date = models.DateTimeField(null=True, blank=True, default=None) # Flag for mark some history entries as diff --git a/taiga/projects/history/serializers.py b/taiga/projects/history/serializers.py index 0f2dc658..3bacd6dd 100644 --- a/taiga/projects/history/serializers.py +++ b/taiga/projects/history/serializers.py @@ -17,7 +17,7 @@ # along with this program. If not, see . from taiga.base.api import serializers -from taiga.base.fields import I18NJsonField, Field, MethodField +from taiga.base.fields import I18NJSONField, Field, MethodField from taiga.users.services import get_user_photo_url from taiga.users.gravatar import get_user_gravatar_id @@ -35,8 +35,8 @@ class HistoryEntrySerializer(serializers.LightSerializer): diff = Field() snapshot = Field() values = Field() - values_diff = I18NJsonField() - comment = I18NJsonField() + values_diff = I18NJSONField() + comment = I18NJSONField() comment_html = Field() delete_comment_date = Field() delete_comment_user = Field() diff --git a/taiga/projects/issues/migrations/0001_initial.py b/taiga/projects/issues/migrations/0001_initial.py index ba8814ff..afb9cca2 100644 --- a/taiga/projects/issues/migrations/0001_initial.py +++ b/taiga/projects/issues/migrations/0001_initial.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings import django.utils.timezone -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -20,7 +20,7 @@ class Migration(migrations.Migration): name='Issue', fields=[ ('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), - ('tags', djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='tags')), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), blank=True, default=[], null=True, size=None, verbose_name='tags')), ('version', models.IntegerField(default=1, verbose_name='version')), ('is_blocked', models.BooleanField(default=False, verbose_name='is blocked')), ('blocked_note', models.TextField(blank=True, default='', verbose_name='blocked note')), diff --git a/taiga/projects/issues/migrations/0002_issue_external_reference.py b/taiga/projects/issues/migrations/0002_issue_external_reference.py index e88ddab2..60b8845c 100644 --- a/taiga/projects/issues/migrations/0002_issue_external_reference.py +++ b/taiga/projects/issues/migrations/0002_issue_external_reference.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='issue', name='external_reference', - field=djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='external reference'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=False, null=False), blank=True, default=None, null=True, size=None, verbose_name='external reference'), preserve_default=True, ), ] diff --git a/taiga/projects/migrations/0001_initial.py b/taiga/projects/migrations/0001_initial.py index 5a64c370..deee3a62 100644 --- a/taiga/projects/migrations/0001_initial.py +++ b/taiga/projects/migrations/0001_initial.py @@ -3,10 +3,9 @@ from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings -import django_pgjson.fields +import django.contrib.postgres.fields import django.utils.timezone import django.db.models.deletion -import djorm_pgarray.fields import taiga.projects.history.models @@ -40,7 +39,7 @@ class Migration(migrations.Migration): name='Project', fields=[ ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), - ('tags', djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='tags')), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), blank=True, default=[], null=True, size=None, verbose_name='tags')), ('name', models.CharField(max_length=250, unique=True, verbose_name='name')), ('slug', models.SlugField(max_length=250, unique=True, verbose_name='slug', blank=True)), ('description', models.TextField(verbose_name='description')), @@ -54,10 +53,10 @@ class Migration(migrations.Migration): ('is_issues_activated', models.BooleanField(default=True, verbose_name='active issues panel')), ('videoconferences', models.CharField(max_length=250, null=True, choices=[('appear-in', 'AppearIn'), ('talky', 'Talky')], verbose_name='videoconference system', blank=True)), ('videoconferences_salt', models.CharField(max_length=250, null=True, verbose_name='videoconference room salt', blank=True)), - ('anon_permissions', djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_us', 'View user stories'), ('view_tasks', 'View tasks'), ('view_issues', 'View issues'), ('view_wiki_pages', 'View wiki pages'), ('view_wiki_links', 'View wiki links')], dbtype='text', default=[], verbose_name='anonymous permissions')), - ('public_permissions', djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_us', 'View user stories'), ('view_issues', 'View issues'), ('vote_issues', 'Vote issues'), ('view_tasks', 'View tasks'), ('view_wiki_pages', 'View wiki pages'), ('view_wiki_links', 'View wiki links'), ('request_membership', 'Request membership'), ('add_us_to_project', 'Add user story to project'), ('add_comments_to_us', 'Add comments to user stories'), ('add_comments_to_task', 'Add comments to tasks'), ('add_issue', 'Add issues'), ('add_comments_issue', 'Add comments to issues'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link')], dbtype='text', default=[], verbose_name='user permissions')), + ('anon_permissions', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_us', 'View user stories'), ('view_tasks', 'View tasks'), ('view_issues', 'View issues'), ('view_wiki_pages', 'View wiki pages'), ('view_wiki_links', 'View wiki links')]), blank=True, default=[], null=True, size=None, verbose_name='anonymous permissions')), + ('public_permissions', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_us', 'View user stories'), ('view_issues', 'View issues'), ('vote_issues', 'Vote issues'), ('view_tasks', 'View tasks'), ('view_wiki_pages', 'View wiki pages'), ('view_wiki_links', 'View wiki links'), ('request_membership', 'Request membership'), ('add_us_to_project', 'Add user story to project'), ('add_comments_to_us', 'Add comments to user stories'), ('add_comments_to_task', 'Add comments to tasks'), ('add_issue', 'Add issues'), ('add_comments_issue', 'Add comments to issues'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions')), ('is_private', models.BooleanField(default=False, verbose_name='is private')), - ('tags_colors', djorm_pgarray.fields.TextArrayField(dbtype='text', dimension=2, default=[], null=False, verbose_name='tags colors')), + ('tags_colors', django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=True, null=True), size=2), blank=True, default=[], null=True, size=None, verbose_name='tags colors')), ], options={ 'ordering': ['name'], diff --git a/taiga/projects/migrations/0002_auto_20140903_0920.py b/taiga/projects/migrations/0002_auto_20140903_0920.py index d9868ec7..93cf59da 100644 --- a/taiga/projects/migrations/0002_auto_20140903_0920.py +++ b/taiga/projects/migrations/0002_auto_20140903_0920.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations import django.utils.timezone -import django_pgjson.fields +import taiga.base.db.models.fields import django.db.models.deletion import taiga.projects.history.models @@ -100,15 +100,15 @@ class Migration(migrations.Migration): ('is_issues_activated', models.BooleanField(verbose_name='active issues panel', default=True)), ('videoconferences', models.CharField(max_length=250, null=True, choices=[('appear-in', 'AppearIn'), ('talky', 'Talky')], verbose_name='videoconference system', blank=True)), ('videoconferences_salt', models.CharField(max_length=250, null=True, verbose_name='videoconference room salt', blank=True)), - ('default_options', django_pgjson.fields.JsonField(null=True, verbose_name='default options', blank=True)), - ('us_statuses', django_pgjson.fields.JsonField(null=True, verbose_name='us statuses', blank=True)), - ('points', django_pgjson.fields.JsonField(null=True, verbose_name='points', blank=True)), - ('task_statuses', django_pgjson.fields.JsonField(null=True, verbose_name='task statuses', blank=True)), - ('issue_statuses', django_pgjson.fields.JsonField(null=True, verbose_name='issue statuses', blank=True)), - ('issue_types', django_pgjson.fields.JsonField(null=True, verbose_name='issue types', blank=True)), - ('priorities', django_pgjson.fields.JsonField(null=True, verbose_name='priorities', blank=True)), - ('severities', django_pgjson.fields.JsonField(null=True, verbose_name='severities', blank=True)), - ('roles', django_pgjson.fields.JsonField(null=True, verbose_name='roles', blank=True)), + ('default_options', taiga.base.db.models.fields.JSONField(null=True, verbose_name='default options', blank=True)), + ('us_statuses', taiga.base.db.models.fields.JSONField(null=True, verbose_name='us statuses', blank=True)), + ('points', taiga.base.db.models.fields.JSONField(null=True, verbose_name='points', blank=True)), + ('task_statuses', taiga.base.db.models.fields.JSONField(null=True, verbose_name='task statuses', blank=True)), + ('issue_statuses', taiga.base.db.models.fields.JSONField(null=True, verbose_name='issue statuses', blank=True)), + ('issue_types', taiga.base.db.models.fields.JSONField(null=True, verbose_name='issue types', blank=True)), + ('priorities', taiga.base.db.models.fields.JSONField(null=True, verbose_name='priorities', blank=True)), + ('severities', taiga.base.db.models.fields.JSONField(null=True, verbose_name='severities', blank=True)), + ('roles', taiga.base.db.models.fields.JSONField(null=True, verbose_name='roles', blank=True)), ], options={ 'verbose_name_plural': 'project templates', diff --git a/taiga/projects/migrations/0010_project_modules_config.py b/taiga/projects/migrations/0010_project_modules_config.py index 49eaedb7..b2f14201 100644 --- a/taiga/projects/migrations/0010_project_modules_config.py +++ b/taiga/projects/migrations/0010_project_modules_config.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='project', name='modules_config', - field=django_pgjson.fields.JsonField(blank=True, null=True, verbose_name='modules config'), + field=taiga.base.db.models.fields.JSONField(blank=True, null=True, verbose_name='modules config'), preserve_default=True, ), ] diff --git a/taiga/projects/migrations/0011_auto_20141028_2057.py b/taiga/projects/migrations/0011_auto_20141028_2057.py index fd9a0a33..037b6b9c 100644 --- a/taiga/projects/migrations/0011_auto_20141028_2057.py +++ b/taiga/projects/migrations/0011_auto_20141028_2057.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -16,7 +16,7 @@ class Migration(migrations.Migration): name='ProjectModulesConfig', fields=[ ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), - ('config', django_pgjson.fields.JsonField(null=True, verbose_name='modules config', blank=True)), + ('config', taiga.base.db.models.fields.JSONField(null=True, verbose_name='modules config', blank=True)), ('project', models.OneToOneField(to='projects.Project', verbose_name='project', related_name='modules_config')), ], options={ diff --git a/taiga/projects/migrations/0016_fix_json_field_not_null.py b/taiga/projects/migrations/0016_fix_json_field_not_null.py index aaa04543..4c280951 100644 --- a/taiga/projects/migrations/0016_fix_json_field_not_null.py +++ b/taiga/projects/migrations/0016_fix_json_field_not_null.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from django.db import models, migrations diff --git a/taiga/projects/migrations/0019_auto_20150311_0821.py b/taiga/projects/migrations/0019_auto_20150311_0821.py index 034bd3ff..2b5c42ac 100644 --- a/taiga/projects/migrations/0019_auto_20150311_0821.py +++ b/taiga/projects/migrations/0019_auto_20150311_0821.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='project', name='public_permissions', - field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('vote_issues', 'Vote issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], verbose_name='user permissions', dbtype='text', default=[]), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('vote_issues', 'Vote issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions'), preserve_default=True, ), ] diff --git a/taiga/projects/migrations/0024_auto_20150810_1247.py b/taiga/projects/migrations/0024_auto_20150810_1247.py index f057816b..5eef1454 100644 --- a/taiga/projects/migrations/0024_auto_20150810_1247.py +++ b/taiga/projects/migrations/0024_auto_20150810_1247.py @@ -2,8 +2,8 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields from django.conf import settings +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -17,7 +17,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='project', name='public_permissions', - field=djorm_pgarray.fields.TextArrayField(default=[], dbtype='text', choices=[('view_project', 'View project'), ('star_project', 'Star project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('vote_us', 'Vote user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('vote_task', 'Vote task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('vote_issue', 'Vote issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], verbose_name='user permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('star_project', 'Star project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('vote_us', 'Vote user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('vote_task', 'Vote task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('vote_issue', 'Vote issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions'), preserve_default=True, ), ] diff --git a/taiga/projects/migrations/0025_auto_20150901_1600.py b/taiga/projects/migrations/0025_auto_20150901_1600.py index 8859b14e..ec02ef9b 100644 --- a/taiga/projects/migrations/0025_auto_20150901_1600.py +++ b/taiga/projects/migrations/0025_auto_20150901_1600.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='project', name='public_permissions', - field=djorm_pgarray.fields.TextArrayField(default=[], choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', verbose_name='user permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions'), preserve_default=True, ), ] diff --git a/taiga/projects/migrations/0041_auto_20160519_1058.py b/taiga/projects/migrations/0041_auto_20160519_1058.py index c4b0a2fd..cffe98d5 100644 --- a/taiga/projects/migrations/0041_auto_20160519_1058.py +++ b/taiga/projects/migrations/0041_auto_20160519_1058.py @@ -2,8 +2,8 @@ # Generated by Django 1.9.2 on 2016-05-19 10:58 from __future__ import unicode_literals -from django.db import migrations -import djorm_pgarray.fields +from django.db import migrations, models +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -16,6 +16,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='project', name='public_permissions', - field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', default=[], verbose_name='user permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions'), + ), ] diff --git a/taiga/projects/migrations/0049_auto_20160629_1443.py b/taiga/projects/migrations/0049_auto_20160629_1443.py index 8c2117b0..724cb1a9 100644 --- a/taiga/projects/migrations/0049_auto_20160629_1443.py +++ b/taiga/projects/migrations/0049_auto_20160629_1443.py @@ -2,10 +2,10 @@ # Generated by Django 1.9.2 on 2016-06-29 14:43 from __future__ import unicode_literals -import django.contrib.postgres.fields +import taiga.base.db.models.fields from django.db import migrations, models import django.db.models.deletion -import django_pgjson.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -71,7 +71,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='projecttemplate', name='epic_statuses', - field=django_pgjson.fields.JsonField(blank=True, null=True, verbose_name='epic statuses'), + field=taiga.base.db.models.fields.JSONField(blank=True, null=True, verbose_name='epic statuses'), ), migrations.AddField( model_name='projecttemplate', diff --git a/taiga/projects/migrations/0055_json_to_jsonb.py b/taiga/projects/migrations/0055_json_to_jsonb.py new file mode 100644 index 00000000..5b9a0b8e --- /dev/null +++ b/taiga/projects/migrations/0055_json_to_jsonb.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:34 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0054_auto_20160928_0540'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "projects_projectmodulesconfig" + ALTER COLUMN "config" TYPE jsonb USING to_json("config"::text)::jsonb; + """, + reverse_sql=migrations.RunSQL.noop + ), + migrations.RunSQL( + """ + ALTER TABLE "projects_projecttemplate" + ALTER COLUMN "roles" TYPE jsonb USING to_json("roles"::text)::jsonb, + ALTER COLUMN "default_options" TYPE jsonb USING to_json("default_options"::text)::jsonb, + ALTER COLUMN "epic_statuses" TYPE jsonb USING to_json("epic_statuses"::text)::jsonb, + ALTER COLUMN "us_statuses" TYPE jsonb USING to_json("us_statuses"::text)::jsonb, + ALTER COLUMN "points" TYPE jsonb USING to_json("points"::text)::jsonb, + ALTER COLUMN "task_statuses" TYPE jsonb USING to_json("task_statuses"::text)::jsonb, + ALTER COLUMN "issue_statuses" TYPE jsonb USING to_json("issue_statuses"::text)::jsonb, + ALTER COLUMN "issue_types" TYPE jsonb USING to_json("issue_types"::text)::jsonb, + ALTER COLUMN "priorities" TYPE jsonb USING to_json("priorities"::text)::jsonb, + ALTER COLUMN "severities" TYPE jsonb USING to_json("severities"::text)::jsonb; + """, + reverse_sql=migrations.RunSQL.noop + ), + ] diff --git a/taiga/projects/models.py b/taiga/projects/models.py index c23fcc4b..67f9e50f 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -29,7 +29,7 @@ from django.utils.functional import cached_property from django_pglocks import advisory_lock -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from taiga.base.utils.time import timestamp_ms from taiga.projects.tagging.models import TaggedMixin @@ -491,7 +491,7 @@ class Project(ProjectDefaults, TaggedMixin, TagsColorsdMixin, models.Model): class ProjectModulesConfig(models.Model): project = models.OneToOneField("Project", null=False, blank=False, related_name="modules_config", verbose_name=_("project")) - config = JsonField(null=True, blank=True, verbose_name=_("modules config")) + config = JSONField(null=True, blank=True, verbose_name=_("modules config")) class Meta: verbose_name = "project modules config" @@ -751,16 +751,16 @@ class ProjectTemplate(models.Model): videoconferences_extra_data = models.CharField(max_length=250, null=True, blank=True, verbose_name=_("videoconference extra data")) - default_options = JsonField(null=True, blank=True, verbose_name=_("default options")) - epic_statuses = JsonField(null=True, blank=True, verbose_name=_("epic statuses")) - us_statuses = JsonField(null=True, blank=True, verbose_name=_("us statuses")) - points = JsonField(null=True, blank=True, verbose_name=_("points")) - task_statuses = JsonField(null=True, blank=True, verbose_name=_("task statuses")) - issue_statuses = JsonField(null=True, blank=True, verbose_name=_("issue statuses")) - issue_types = JsonField(null=True, blank=True, verbose_name=_("issue types")) - 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")) + default_options = JSONField(null=True, blank=True, verbose_name=_("default options")) + epic_statuses = JSONField(null=True, blank=True, verbose_name=_("epic statuses")) + us_statuses = JSONField(null=True, blank=True, verbose_name=_("us statuses")) + points = JSONField(null=True, blank=True, verbose_name=_("points")) + task_statuses = JSONField(null=True, blank=True, verbose_name=_("task statuses")) + issue_statuses = JSONField(null=True, blank=True, verbose_name=_("issue statuses")) + issue_types = JSONField(null=True, blank=True, verbose_name=_("issue types")) + 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: diff --git a/taiga/projects/tasks/migrations/0001_initial.py b/taiga/projects/tasks/migrations/0001_initial.py index 99537a7c..b07ae15f 100644 --- a/taiga/projects/tasks/migrations/0001_initial.py +++ b/taiga/projects/tasks/migrations/0001_initial.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields from django.conf import settings import django.utils.timezone @@ -21,7 +21,7 @@ class Migration(migrations.Migration): name='Task', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), - ('tags', djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='tags')), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), blank=True, default=[], null=True, size=None, verbose_name='tags')), ('version', models.IntegerField(default=1, verbose_name='version')), ('is_blocked', models.BooleanField(verbose_name='is blocked', default=False)), ('blocked_note', models.TextField(blank=True, verbose_name='blocked note', default='')), diff --git a/taiga/projects/tasks/migrations/0003_task_external_reference.py b/taiga/projects/tasks/migrations/0003_task_external_reference.py index 8222116e..9de35ca6 100644 --- a/taiga/projects/tasks/migrations/0003_task_external_reference.py +++ b/taiga/projects/tasks/migrations/0003_task_external_reference.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='task', name='external_reference', - field=djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='external reference'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=False, null=False), blank=True, default=None, null=True, size=None, verbose_name='external reference'), preserve_default=True, ), ] diff --git a/taiga/projects/userstories/migrations/0001_initial.py b/taiga/projects/userstories/migrations/0001_initial.py index 688a9050..3672aca5 100644 --- a/taiga/projects/userstories/migrations/0001_initial.py +++ b/taiga/projects/userstories/migrations/0001_initial.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings import django.utils.timezone -import djorm_pgarray.fields +import django.contrib.postgres.fields import django.db.models.deletion @@ -38,7 +38,7 @@ class Migration(migrations.Migration): name='UserStory', fields=[ ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')), - ('tags', djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='tags')), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), blank=True, default=[], null=True, size=None, verbose_name='tags')), ('version', models.IntegerField(default=1, verbose_name='version')), ('is_blocked', models.BooleanField(default=False, verbose_name='is blocked')), ('blocked_note', models.TextField(default='', blank=True, verbose_name='blocked note')), diff --git a/taiga/projects/userstories/migrations/0007_userstory_external_reference.py b/taiga/projects/userstories/migrations/0007_userstory_external_reference.py index 3cbbceeb..fbfd1f3f 100644 --- a/taiga/projects/userstories/migrations/0007_userstory_external_reference.py +++ b/taiga/projects/userstories/migrations/0007_userstory_external_reference.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='userstory', name='external_reference', - field=djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='external reference'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=False, null=False), blank=True, default=None, null=True, size=None, verbose_name='external reference'), preserve_default=True, ), ] diff --git a/taiga/projects/validators.py b/taiga/projects/validators.py index 54a43178..ec97aadd 100644 --- a/taiga/projects/validators.py +++ b/taiga/projects/validators.py @@ -22,7 +22,7 @@ from django.utils.translation import ugettext as _ from taiga.base.api import serializers from taiga.base.api import validators from taiga.base.exceptions import ValidationError -from taiga.base.fields import JsonField +from taiga.base.fields import JSONField from taiga.base.fields import PgArrayField from taiga.users.models import Role @@ -222,15 +222,15 @@ class ProjectValidator(validators.ModelValidator): ###################################################### class ProjectTemplateValidator(validators.ModelValidator): - default_options = JsonField(required=False, label=_("Default options")) - us_statuses = JsonField(required=False, label=_("User story's statuses")) - points = JsonField(required=False, label=_("Points")) - task_statuses = JsonField(required=False, label=_("Task's statuses")) - issue_statuses = JsonField(required=False, label=_("Issue's statuses")) - issue_types = JsonField(required=False, label=_("Issue's types")) - priorities = JsonField(required=False, label=_("Priorities")) - severities = JsonField(required=False, label=_("Severities")) - roles = JsonField(required=False, label=_("Roles")) + default_options = JSONField(required=False, label=_("Default options")) + us_statuses = JSONField(required=False, label=_("User story's statuses")) + points = JSONField(required=False, label=_("Points")) + task_statuses = JSONField(required=False, label=_("Task's statuses")) + issue_statuses = JSONField(required=False, label=_("Issue's statuses")) + issue_types = JSONField(required=False, label=_("Issue's types")) + priorities = JSONField(required=False, label=_("Priorities")) + severities = JSONField(required=False, label=_("Severities")) + roles = JSONField(required=False, label=_("Roles")) class Meta: model = models.ProjectTemplate diff --git a/taiga/timeline/management/commands/rebuild_timeline.py b/taiga/timeline/management/commands/rebuild_timeline.py index 674f6b9d..cd14795a 100644 --- a/taiga/timeline/management/commands/rebuild_timeline.py +++ b/taiga/timeline/management/commands/rebuild_timeline.py @@ -46,7 +46,7 @@ class BulkCreator(object): def create_element(self, element): self.timeline_objects.append(element) - if len(self.timeline_objects) > 1000: + if len(self.timeline_objects) > 999: self.flush() def flush(self): @@ -140,31 +140,28 @@ def generate_timeline(initial_date, final_date, project_id): class Command(BaseCommand): help = 'Regenerate project timeline' - option_list = BaseCommand.option_list + ( - make_option('--purge', - action='store_true', - dest='purge', - default=False, - help='Purge existing timelines'), - ) + ( - make_option('--initial_date', - action='store', - dest='initial_date', - default=None, - help='Initial date for timeline generation'), - ) + ( - make_option('--final_date', - action='store', - dest='final_date', - default=None, - help='Final date for timeline generation'), - ) + ( - make_option('--project', - action='store', - dest='project', - default=None, - help='Selected project id for timeline generation'), - ) + + def add_arguments(self, parser): + parser.add_argument('--purge', + action='store_true', + dest='purge', + default=False, + help='Purge existing timelines') + parser.add_argument('--initial_date', + action='store', + dest='initial_date', + default=None, + help='Initial date for timeline generation') + parser.add_argument('--final_date', + action='store', + dest='final_date', + default=None, + help='Final date for timeline generation') + parser.add_argument('--project', + action='store', + dest='project', + default=None, + help='Selected project id for timeline generation') @override_settings(DEBUG=False) def handle(self, *args, **options): diff --git a/taiga/timeline/migrations/0001_initial.py b/taiga/timeline/migrations/0001_initial.py index 5a2a5fb6..2f6fa746 100644 --- a/taiga/timeline/migrations/0001_initial.py +++ b/taiga/timeline/migrations/0001_initial.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields import django.utils.timezone @@ -20,7 +20,7 @@ class Migration(migrations.Migration): ('object_id', models.PositiveIntegerField()), ('namespace', models.SlugField(default='default')), ('event_type', models.SlugField()), - ('data', django_pgjson.fields.JsonField()), + ('data', taiga.base.db.models.fields.JSONField()), ('created', models.DateTimeField(default=django.utils.timezone.now)), ('content_type', models.ForeignKey(to='contenttypes.ContentType')), ], diff --git a/taiga/timeline/migrations/0002_auto_20150327_1056.py b/taiga/timeline/migrations/0002_auto_20150327_1056.py index 6c8a2066..ee6f2199 100644 --- a/taiga/timeline/migrations/0002_auto_20150327_1056.py +++ b/taiga/timeline/migrations/0002_auto_20150327_1056.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields from django.utils import timezone class Migration(migrations.Migration): @@ -27,7 +27,7 @@ class Migration(migrations.Migration): ('namespace', models.SlugField(default='default')), ('event_type', models.SlugField()), ('project', models.ForeignKey(to='projects.Project')), - ('data', django_pgjson.fields.JsonField()), + ('data', taiga.base.db.models.fields.JSONField()), ('data_content_type', models.ForeignKey(to='contenttypes.ContentType', related_name='data_timelines')), ('created', models.DateTimeField(default=timezone.now)), ('content_type', models.ForeignKey(to='contenttypes.ContentType', related_name='content_type_timelines')), diff --git a/taiga/timeline/migrations/0006_json_to_jsonb.py b/taiga/timeline/migrations/0006_json_to_jsonb.py new file mode 100644 index 00000000..72a63968 --- /dev/null +++ b/taiga/timeline/migrations/0006_json_to_jsonb.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:35 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + + +class Migration(migrations.Migration): + + dependencies = [ + ('timeline', '0005_auto_20160706_0723'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="timeline_timeline", + column_name="data", + ), + reverse_sql=migrations.RunSQL.noop + ), + ] diff --git a/taiga/timeline/models.py b/taiga/timeline/models.py index ebee7da5..52fe5aca 100644 --- a/taiga/timeline/models.py +++ b/taiga/timeline/models.py @@ -17,7 +17,7 @@ # along with this program. If not, see . from django.db import models -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from django.utils import timezone from django.contrib.contenttypes.models import ContentType @@ -33,7 +33,7 @@ class Timeline(models.Model): namespace = models.CharField(max_length=250, default="default", db_index=True) event_type = models.CharField(max_length=250, db_index=True) project = models.ForeignKey(Project, null=True) - data = JsonField() + data = JSONField() data_content_type = models.ForeignKey(ContentType, related_name="data_timelines") created = models.DateTimeField(default=timezone.now, db_index=True) diff --git a/taiga/urls.py b/taiga/urls.py index 95b27ce8..46735e60 100644 --- a/taiga/urls.py +++ b/taiga/urls.py @@ -17,7 +17,7 @@ # along with this program. If not, see . from django.conf import settings -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.contrib import admin from .routers import router diff --git a/taiga/users/migrations/0001_initial.py b/taiga/users/migrations/0001_initial.py index ad8d2071..d81f77fa 100644 --- a/taiga/users/migrations/0001_initial.py +++ b/taiga/users/migrations/0001_initial.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields import django.utils.timezone import re import django.core.validators diff --git a/taiga/users/migrations/0002_auto_20140903_0916.py b/taiga/users/migrations/0002_auto_20140903_0916.py index 40dd3086..fd663d44 100644 --- a/taiga/users/migrations/0002_auto_20140903_0916.py +++ b/taiga/users/migrations/0002_auto_20140903_0916.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -18,7 +18,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), ('name', models.CharField(verbose_name='name', max_length=200)), ('slug', models.SlugField(verbose_name='slug', max_length=250, blank=True)), - ('permissions', djorm_pgarray.fields.TextArrayField(dbtype='text', verbose_name='permissions', choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('vote_issues', 'Vote issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], default=[])), + ('permissions', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('vote_issues', 'Vote issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='permissions')), ('order', models.IntegerField(verbose_name='order', default=10)), ('computable', models.BooleanField(default=True)), ], diff --git a/taiga/users/migrations/0007_auto_20150209_1611.py b/taiga/users/migrations/0007_auto_20150209_1611.py index 4f1bc2ca..27f1cd3d 100644 --- a/taiga/users/migrations/0007_auto_20150209_1611.py +++ b/taiga/users/migrations/0007_auto_20150209_1611.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings -import django_pgjson.fields +import taiga.base.db.models.fields def migrate_github_id(apps, schema_editor): @@ -27,7 +27,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)), ('key', models.SlugField()), ('value', models.CharField(max_length=300)), - ('extra', django_pgjson.fields.JsonField()), + ('extra', taiga.base.db.models.fields.JSONField()), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], options={ diff --git a/taiga/users/migrations/0012_auto_20150812_1142.py b/taiga/users/migrations/0012_auto_20150812_1142.py index fff8c17b..8d5354b0 100644 --- a/taiga/users/migrations/0012_auto_20150812_1142.py +++ b/taiga/users/migrations/0012_auto_20150812_1142.py @@ -2,8 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields - +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +14,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='role', name='permissions', - field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('star_project', 'Star project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('vote_us', 'Vote user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('vote_task', 'Vote task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('vote_issue', 'Vote issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], verbose_name='permissions', default=[], dbtype='text'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('star_project', 'Star project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('vote_us', 'Vote user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('vote_task', 'Vote task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('vote_issue', 'Vote issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='permissions'), preserve_default=True, ), ] diff --git a/taiga/users/migrations/0013_auto_20150901_1600.py b/taiga/users/migrations/0013_auto_20150901_1600.py index 8d2e4143..15492e03 100644 --- a/taiga/users/migrations/0013_auto_20150901_1600.py +++ b/taiga/users/migrations/0013_auto_20150901_1600.py @@ -2,8 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import djorm_pgarray.fields - +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -15,7 +14,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='role', name='permissions', - field=djorm_pgarray.fields.TextArrayField(default=[], choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', verbose_name='permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='permissions'), preserve_default=True, ), ] diff --git a/taiga/users/migrations/0019_auto_20160519_1058.py b/taiga/users/migrations/0019_auto_20160519_1058.py index 69780084..a83dbcea 100644 --- a/taiga/users/migrations/0019_auto_20160519_1058.py +++ b/taiga/users/migrations/0019_auto_20160519_1058.py @@ -2,9 +2,8 @@ # Generated by Django 1.9.2 on 2016-05-19 10:58 from __future__ import unicode_literals -from django.db import migrations -import djorm_pgarray.fields - +from django.db import models, migrations +import django.contrib.postgres.fields class Migration(migrations.Migration): @@ -16,6 +15,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='role', name='permissions', - field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', default=[], verbose_name='permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='permissions'), ), ] diff --git a/taiga/users/migrations/0023_json_to_jsonb.py b/taiga/users/migrations/0023_json_to_jsonb.py new file mode 100644 index 00000000..34782897 --- /dev/null +++ b/taiga/users/migrations/0023_json_to_jsonb.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:35 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0022_auto_20160629_1443'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="users_authdata", + column_name="extra", + ), + reverse_sql=migrations.RunSQL.noop + ), + ] diff --git a/taiga/users/models.py b/taiga/users/models.py index 45908a10..e0f6a8c9 100644 --- a/taiga/users/models.py +++ b/taiga/users/models.py @@ -34,7 +34,7 @@ from django.dispatch import receiver from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from django_pglocks import advisory_lock from taiga.auth.tokens import get_token_for_user @@ -323,7 +323,7 @@ class AuthData(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="auth_data") key = models.SlugField(max_length=50) value = models.CharField(max_length=300) - extra = JsonField() + extra = JSONField() class Meta: unique_together = ["key", "value"] diff --git a/taiga/userstorage/migrations/0001_initial.py b/taiga/userstorage/migrations/0001_initial.py index 1857d669..1776be95 100644 --- a/taiga/userstorage/migrations/0001_initial.py +++ b/taiga/userstorage/migrations/0001_initial.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields from django.conf import settings @@ -20,7 +20,7 @@ class Migration(migrations.Migration): ('created_date', models.DateTimeField(auto_now_add=True, verbose_name='created date')), ('modified_date', models.DateTimeField(verbose_name='modified date', auto_now=True)), ('key', models.CharField(max_length=255, verbose_name='key')), - ('value', django_pgjson.fields.JsonField(verbose_name='value', blank=True, default=None, null=True)), + ('value', taiga.base.db.models.fields.JSONField(verbose_name='value', blank=True, default=None, null=True)), ('owner', models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='owner', related_name='storage_entries')), ], options={ diff --git a/taiga/userstorage/migrations/0002_fix_json_field_not_null.py b/taiga/userstorage/migrations/0002_fix_json_field_not_null.py index f993919a..1aa5c568 100644 --- a/taiga/userstorage/migrations/0002_fix_json_field_not_null.py +++ b/taiga/userstorage/migrations/0002_fix_json_field_not_null.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField from django.db import models, migrations diff --git a/taiga/userstorage/migrations/0003_json_to_jsonb.py b/taiga/userstorage/migrations/0003_json_to_jsonb.py new file mode 100644 index 00000000..9e1f5243 --- /dev/null +++ b/taiga/userstorage/migrations/0003_json_to_jsonb.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:35 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + + +class Migration(migrations.Migration): + + dependencies = [ + ('userstorage', '0002_fix_json_field_not_null'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "{table_name}" + ALTER COLUMN "{column_name}" + TYPE jsonb + USING to_json("{column_name}"::text)::jsonb; + """.format( + table_name="userstorage_storageentry", + column_name="value", + ), + reverse_sql=migrations.RunSQL.noop + ), + ] diff --git a/taiga/userstorage/models.py b/taiga/userstorage/models.py index 9888ea7f..c1e94960 100644 --- a/taiga/userstorage/models.py +++ b/taiga/userstorage/models.py @@ -19,7 +19,7 @@ from django.db import models from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField class StorageEntry(models.Model): @@ -30,7 +30,7 @@ class StorageEntry(models.Model): modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, verbose_name=_("modified date")) key = models.CharField(max_length=255, null=False, blank=False, verbose_name=_("key")) - value = JsonField(blank=True, default=None, null=True, verbose_name=_("value")) + value = JSONField(blank=True, default=None, null=True, verbose_name=_("value")) class Meta: verbose_name = "storage entry" diff --git a/taiga/webhooks/migrations/0001_initial.py b/taiga/webhooks/migrations/0001_initial.py index 79ec8ba9..366bf848 100644 --- a/taiga/webhooks/migrations/0001_initial.py +++ b/taiga/webhooks/migrations/0001_initial.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -30,7 +30,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), ('url', models.URLField(verbose_name='URL')), ('status', models.IntegerField(verbose_name='Status code')), - ('request_data', django_pgjson.fields.JsonField(verbose_name='Request data')), + ('request_data', taiga.base.db.models.fields.JSONField(verbose_name='Request data')), ('response_data', models.TextField(verbose_name='Response data')), ('webhook', models.ForeignKey(related_name='logs', to='webhooks.Webhook')), ], diff --git a/taiga/webhooks/migrations/0003_auto_20150122_1021.py b/taiga/webhooks/migrations/0003_auto_20150122_1021.py index fa565ba3..a1f7a34d 100644 --- a/taiga/webhooks/migrations/0003_auto_20150122_1021.py +++ b/taiga/webhooks/migrations/0003_auto_20150122_1021.py @@ -3,7 +3,9 @@ from __future__ import unicode_literals from django.db import models, migrations import datetime -import django_pgjson.fields +import taiga.base.db.models.fields + +from django.utils import timezone class Migration(migrations.Migration): @@ -16,7 +18,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='webhooklog', name='created', - field=models.DateTimeField(default=datetime.datetime(2015, 1, 22, 10, 21, 17, 188643), auto_now_add=True), + field=models.DateTimeField( + default=datetime.datetime(2015, 1, 22, 10, 21, 17, 188643, timezone.get_default_timezone()), + auto_now_add=True), preserve_default=False, ), migrations.AddField( @@ -28,13 +32,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='webhooklog', name='request_headers', - field=django_pgjson.fields.JsonField(default={}, verbose_name='Request headers'), + field=taiga.base.db.models.fields.JSONField(default={}, verbose_name='Request headers'), preserve_default=True, ), migrations.AddField( model_name='webhooklog', name='response_headers', - field=django_pgjson.fields.JsonField(default={}, verbose_name='Response headers'), + field=taiga.base.db.models.fields.JSONField(default={}, verbose_name='Response headers'), preserve_default=True, ), ] diff --git a/taiga/webhooks/migrations/0005_auto_20150505_1639.py b/taiga/webhooks/migrations/0005_auto_20150505_1639.py index 2e8df5e4..8f143b4a 100644 --- a/taiga/webhooks/migrations/0005_auto_20150505_1639.py +++ b/taiga/webhooks/migrations/0005_auto_20150505_1639.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import django_pgjson.fields +import taiga.base.db.models.fields class Migration(migrations.Migration): @@ -21,13 +21,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='webhooklog', name='request_data', - field=django_pgjson.fields.JsonField(verbose_name='request data'), + field=taiga.base.db.models.fields.JSONField(verbose_name='request data'), preserve_default=True, ), migrations.AlterField( model_name='webhooklog', name='request_headers', - field=django_pgjson.fields.JsonField(verbose_name='request headers', default={}), + field=taiga.base.db.models.fields.JSONField(verbose_name='request headers', default={}), preserve_default=True, ), migrations.AlterField( @@ -39,7 +39,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='webhooklog', name='response_headers', - field=django_pgjson.fields.JsonField(verbose_name='response headers', default={}), + field=taiga.base.db.models.fields.JSONField(verbose_name='response headers', default={}), preserve_default=True, ), migrations.AlterField( diff --git a/taiga/webhooks/migrations/0006_json_to_jsonb.py b/taiga/webhooks/migrations/0006_json_to_jsonb.py new file mode 100644 index 00000000..9f40b860 --- /dev/null +++ b/taiga/webhooks/migrations/0006_json_to_jsonb.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-26 11:35 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.postgres.fields import JSONField + + +class Migration(migrations.Migration): + + dependencies = [ + ('webhooks', '0005_auto_20150505_1639'), + ] + + operations = [ + migrations.RunSQL( + """ + ALTER TABLE "webhooks_webhooklog" + ALTER COLUMN "request_headers" TYPE jsonb USING to_json("request_headers"::text)::jsonb, + ALTER COLUMN "request_data" TYPE jsonb USING to_json("request_data"::text)::jsonb, + ALTER COLUMN "response_headers" TYPE jsonb USING to_json("response_headers"::text)::jsonb; + """, + reverse_sql=migrations.RunSQL.noop + ), + ] diff --git a/taiga/webhooks/models.py b/taiga/webhooks/models.py index 53969525..b73d26d6 100644 --- a/taiga/webhooks/models.py +++ b/taiga/webhooks/models.py @@ -19,7 +19,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ -from django_pgjson.fields import JsonField +from taiga.base.db.models.fields import JSONField class Webhook(models.Model): @@ -39,10 +39,10 @@ class WebhookLog(models.Model): related_name="logs") url = models.URLField(null=False, blank=False, verbose_name=_("URL")) status = models.IntegerField(null=False, blank=False, verbose_name=_("status code")) - request_data = JsonField(null=False, blank=False, verbose_name=_("request data")) - request_headers = JsonField(null=False, blank=False, verbose_name=_("request headers"), default={}) + request_data = JSONField(null=False, blank=False, verbose_name=_("request data")) + request_headers = JSONField(null=False, blank=False, verbose_name=_("request headers"), default={}) response_data = models.TextField(null=False, blank=False, verbose_name=_("response data")) - response_headers = JsonField(null=False, blank=False, verbose_name=_("response headers"), default={}) + response_headers = JSONField(null=False, blank=False, verbose_name=_("response headers"), default={}) duration = models.FloatField(null=False, blank=False, verbose_name=_("duration"), default=0) created = models.DateTimeField(auto_now_add=True) diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 3d62dc6e..46059004 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -163,12 +163,18 @@ def test_project_update(client, data): ] project_data = ProjectSerializer(data.private_project2).data + # Because in serializer is a dict anin model is a list of lists + project_data["tags_colors"] = list(map(list, project_data["tags_colors"].items())) project_data["is_private"] = False + results = helper_test_http_method(client, 'put', url, json.dumps(project_data), users) assert results == [401, 403, 403, 200] project_data = ProjectSerializer(data.blocked_project).data + # Because in serializer is a dict anin model is a list of lists + project_data["tags_colors"] = list(map(list, project_data["tags_colors"].items())) project_data["is_private"] = False + results = helper_test_http_method(client, 'put', blocked_url, json.dumps(project_data), users) assert results == [401, 403, 403, 451] diff --git a/tests/integration/test_history.py b/tests/integration/test_history.py index cc6d8d43..c34d9092 100644 --- a/tests/integration/test_history.py +++ b/tests/integration/test_history.py @@ -18,11 +18,12 @@ # along with this program. If not, see . import pytest -import datetime from unittest.mock import patch from django.core.urlresolvers import reverse +from django.utils import timezone + from .. import factories as f from taiga.base.utils import json @@ -286,7 +287,7 @@ def test_get_comment_versions(client): key=key, diff={}, user={"pk": project.owner.id}, - edit_comment_date=datetime.datetime.now(), + edit_comment_date=timezone.now(), comment_versions = [{ "comment_html": "

test

", "date": "2016-05-09T09:34:27.221Z", diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index d8943e71..fd8f3aa8 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -25,10 +25,13 @@ import datetime import hashlib import binascii import struct +import pytz from unittest.mock import MagicMock, patch from django.core.urlresolvers import reverse +from django.utils import timezone + from django.apps import apps from .. import factories as f @@ -931,7 +934,7 @@ def test_retrieve_notify_policies_by_anonymous_user(client): def test_ms_thread_id(): id = '' - now = datetime.datetime.now() + now = timezone.now() index = services.make_ms_thread_index(id, now) parsed = parse_ms_thread_index(index) @@ -953,7 +956,7 @@ def parse_ms_thread_index(index): # guid = '%08X-%04X-%04X-%04X-%12X' % (guid[0], guid[1], guid[2], (guid[3] >> 48) & 0xFFFF, guid[3] & 0xFFFFFFFFFFFF) f = struct.unpack('>Q', s[:6] + b'\0\0')[0] - ts = [datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=f//10)] + ts = [datetime.datetime(1601, 1, 1, tzinfo=pytz.utc) + datetime.timedelta(microseconds=f//10)] # for the 5 byte appendixes that we won't use for n in range(22, len(s), 5): diff --git a/tests/integration/test_references_sequences.py b/tests/integration/test_references_sequences.py index 2c38900e..b0084604 100644 --- a/tests/integration/test_references_sequences.py +++ b/tests/integration/test_references_sequences.py @@ -67,6 +67,8 @@ def test_sequences(seq): @pytest.mark.django_db def test_unique_reference_per_project(seq, refmodels): + refmodels.Reference.objects.all().delete() + project = factories.ProjectFactory.create() seqname = refmodels.make_sequence_name(project) @@ -82,6 +84,8 @@ def test_unique_reference_per_project(seq, refmodels): @pytest.mark.django_db def test_regenerate_us_reference_on_project_change(seq, refmodels): + refmodels.Reference.objects.all().delete() + project1 = factories.ProjectFactory.create() seqname1 = refmodels.make_sequence_name(project1) project2 = factories.ProjectFactory.create() @@ -104,6 +108,8 @@ def test_regenerate_us_reference_on_project_change(seq, refmodels): @pytest.mark.django_db def test_regenerate_task_reference_on_project_change(seq, refmodels): + refmodels.Reference.objects.all().delete() + project1 = factories.ProjectFactory.create() seqname1 = refmodels.make_sequence_name(project1) project2 = factories.ProjectFactory.create() @@ -126,6 +132,8 @@ def test_regenerate_task_reference_on_project_change(seq, refmodels): @pytest.mark.django_db def test_regenerate_issue_reference_on_project_change(seq, refmodels): + refmodels.Reference.objects.all().delete() + project1 = factories.ProjectFactory.create() seqname1 = refmodels.make_sequence_name(project1) project2 = factories.ProjectFactory.create() @@ -149,6 +157,8 @@ def test_regenerate_issue_reference_on_project_change(seq, refmodels): @pytest.mark.django_db def test_params_validation_in_api_request(client, refmodels): + refmodels.Reference.objects.all().delete() + user = factories.UserFactory.create() project = factories.ProjectFactory.create(owner=user) seqname1 = refmodels.make_sequence_name(project) diff --git a/tests/integration/test_roles.py b/tests/integration/test_roles.py index e3b47f7d..7ad000ef 100644 --- a/tests/integration/test_roles.py +++ b/tests/integration/test_roles.py @@ -55,27 +55,3 @@ def test_destroy_role_and_reassign_members(client): qs = Membership.objects.filter(project=project, role_id=role1.pk) assert qs.count() == 2 - - -def test_destroy_role_and_reassign_members_with_deleted_project(client): - """ - Regression test, that fixes some 500 errors on production - """ - - user1 = f.UserFactory.create() - user2 = f.UserFactory.create() - project = f.ProjectFactory.create(owner=user1) - role1 = f.RoleFactory.create(project=project) - role2 = f.RoleFactory.create(project=project) - f.MembershipFactory.create(project=project, user=user1, role=role1) - f.MembershipFactory.create(project=project, user=user2, role=role2) - - Project.objects.filter(pk=project.id).delete() - - url = reverse("roles-detail", args=[role2.pk]) + "?moveTo={}".format(role1.pk) - client.login(user1) - - response = client.delete(url) - - # FIXME: really should return 403? I think it should be 404 - assert response.status_code == 403, response.content diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index e0353d15..e898a1fa 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -35,7 +35,7 @@ def test_add_to_object_timeline(): user1 = factories.UserFactory() task = factories.TaskFactory() - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task, "test", task.created_date) @@ -54,7 +54,7 @@ def test_get_timeline(): task3= factories.TaskFactory() task4= factories.TaskFactory() - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) service._add_to_object_timeline(user1, task2, "test", task2.created_date) @@ -73,7 +73,7 @@ def test_filter_timeline_no_privileges(): user2 = factories.UserFactory() task1= factories.TaskFactory() - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") timeline = service.filter_timeline_for_user(timeline, user2) @@ -88,7 +88,7 @@ def test_filter_timeline_public_project(): task1= factories.TaskFactory() task2= factories.TaskFactory.create(project=project) - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") @@ -104,7 +104,7 @@ def test_filter_timeline_private_project_anon_permissions(): task1= factories.TaskFactory() task2= factories.TaskFactory.create(project=project) - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") @@ -123,7 +123,7 @@ def test_filter_timeline_private_project_member_permissions(): task1= factories.TaskFactory() task2= factories.TaskFactory.create(project=project) - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") @@ -140,7 +140,7 @@ def test_filter_timeline_private_project_member_admin(): task1= factories.TaskFactory() task2= factories.TaskFactory.create(project=project) - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") @@ -157,7 +157,7 @@ def test_filter_timeline_private_project_member_superuser(): task1= factories.TaskFactory() task2= factories.TaskFactory.create(project=project) - service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) + service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: id(x)) service._add_to_object_timeline(user1, task1, "test", task1.created_date) service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") diff --git a/tests/integration/test_totals_projects.py b/tests/integration/test_totals_projects.py index 8d29d950..fb7f1e58 100644 --- a/tests/integration/test_totals_projects.py +++ b/tests/integration/test_totals_projects.py @@ -27,13 +27,15 @@ from taiga.projects.history.choices import HistoryType from taiga.projects.models import Project from django.core.urlresolvers import reverse +from django.utils import timezone pytestmark = pytest.mark.django_db + def test_project_totals_updated_on_activity(client): project = f.create_project() totals_updated_datetime = project.totals_updated_datetime - now = datetime.datetime.now() + now = timezone.now() assert project.total_activity == 0 totals_updated_datetime = project.totals_updated_datetime @@ -120,10 +122,10 @@ def test_project_totals_updated_on_like(client): f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) totals_updated_datetime = project.totals_updated_datetime - now = datetime.datetime.now() + now = timezone.now() assert project.total_activity == 0 - now = datetime.datetime.now() + now = timezone.now() totals_updated_datetime = project.totals_updated_datetime us = f.UserStoryFactory.create(project=project, owner=project.owner) diff --git a/tests/unit/test_export.py b/tests/unit/test_export.py index 1088550f..18ae7d90 100644 --- a/tests/unit/test_export.py +++ b/tests/unit/test_export.py @@ -27,7 +27,7 @@ pytestmark = pytest.mark.django_db def test_export_issue_finish_date(client): - issue = f.IssueFactory.create(finished_date="2014-10-22") + issue = f.IssueFactory.create(finished_date="2014-10-22T00:00:00+0000") output = io.BytesIO() render_project(issue.project, output) project_data = json.loads(output.getvalue()) @@ -36,7 +36,7 @@ def test_export_issue_finish_date(client): def test_export_user_story_finish_date(client): - user_story = f.UserStoryFactory.create(finish_date="2014-10-22") + user_story = f.UserStoryFactory.create(finish_date="2014-10-22T00:00:00+0000") output = io.BytesIO() render_project(user_story.project, output) project_data = json.loads(output.getvalue()) diff --git a/tests/unit/test_mdrender.py b/tests/unit/test_mdrender.py index 90bbe001..08fb9009 100644 --- a/tests/unit/test_mdrender.py +++ b/tests/unit/test_mdrender.py @@ -23,6 +23,7 @@ from taiga.mdrender.extensions import emojify from taiga.mdrender.service import render, cache_by_sha, get_diff_of_htmls, render_and_extract from datetime import datetime +import pytz dummy_project = MagicMock() dummy_project.id = 1 @@ -206,19 +207,21 @@ def test_render_relative_image(): def test_render_triple_quote_code(): - expected_result = "
print(\"test\")\n
" + expected_result = '
print("test")\n
' + assert render(dummy_project, "```python\nprint(\"test\")\n```") == expected_result def test_render_triple_quote_and_lang_code(): - expected_result = "
print(\"test\")\n
" + expected_result = '
print("test")\n
' + assert render(dummy_project, "```python\nprint(\"test\")\n```") == expected_result def test_cache_by_sha(): @cache_by_sha def test_cache(project, text): - return datetime.now() + return datetime.now(pytz.utc) result1 = test_cache(dummy_project, "test") result2 = test_cache(dummy_project, "test2")