Initial epic model + epic status + epcic attachments + project template + project defaults
parent
75a9751136
commit
f70923c064
|
@ -300,6 +300,7 @@ INSTALLED_APPS = [
|
|||
"taiga.projects.likes",
|
||||
"taiga.projects.votes",
|
||||
"taiga.projects.milestones",
|
||||
"taiga.projects.epics",
|
||||
"taiga.projects.userstories",
|
||||
"taiga.projects.tasks",
|
||||
"taiga.projects.issues",
|
||||
|
|
|
@ -198,6 +198,10 @@ class PermissionBasedAttachmentFilterBackend(PermissionBasedFilterBackend):
|
|||
return qs.filter(content_type=ct)
|
||||
|
||||
|
||||
class CanViewEpicAttachmentFilterBackend(PermissionBasedAttachmentFilterBackend):
|
||||
permission = "view_epic"
|
||||
|
||||
|
||||
class CanViewUserStoryAttachmentFilterBackend(PermissionBasedAttachmentFilterBackend):
|
||||
permission = "view_us"
|
||||
|
||||
|
|
|
@ -22,12 +22,14 @@ from django.utils.translation import ugettext_lazy as _
|
|||
ANON_PERMISSIONS = [
|
||||
('view_project', _('View project')),
|
||||
('view_milestones', _('View milestones')),
|
||||
('view_epic', _('View epic')),
|
||||
('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')),
|
||||
]
|
||||
|
||||
MEMBERS_PERMISSIONS = [
|
||||
('view_project', _('View project')),
|
||||
# Milestone permissions
|
||||
|
@ -36,6 +38,12 @@ MEMBERS_PERMISSIONS = [
|
|||
('modify_milestone', _('Modify milestone')),
|
||||
('delete_milestone', _('Delete milestone')),
|
||||
# US permissions
|
||||
('view_epic', _('View epic')),
|
||||
('add_epic', _('Add epic')),
|
||||
('modify_epic', _('Modify epic')),
|
||||
('comment_epic', _('Comment epic')),
|
||||
('delete_epic', _('Delete epic')),
|
||||
# US permissions
|
||||
('view_us', _('View user story')),
|
||||
('add_us', _('Add user story')),
|
||||
('modify_us', _('Modify user story')),
|
||||
|
|
|
@ -40,6 +40,8 @@ from taiga.base.decorators import detail_route
|
|||
from taiga.base.utils.slug import slugify_uniquely
|
||||
|
||||
from taiga.permissions import services as permissions_services
|
||||
|
||||
from taiga.projects.epics.models import Epic
|
||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||
from taiga.projects.issues.models import Issue
|
||||
from taiga.projects.likes.mixins.viewsets import LikedResourceMixin, FansViewSetMixin
|
||||
|
@ -49,7 +51,6 @@ from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin
|
|||
from taiga.projects.mixins.ordering import BulkUpdateOrderMixin
|
||||
from taiga.projects.tasks.models import Task
|
||||
from taiga.projects.tagging.api import TagsColorsResourceMixin
|
||||
|
||||
from taiga.projects.userstories.models import UserStory, RolePoints
|
||||
|
||||
from . import filters as project_filters
|
||||
|
@ -110,17 +111,17 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
|||
now = timezone.now()
|
||||
order_by_field_name = self._get_order_by_field_name()
|
||||
if order_by_field_name == "total_fans_last_week":
|
||||
qs = qs.filter(totals_updated_datetime__gte=now-relativedelta(weeks=1))
|
||||
qs = qs.filter(totals_updated_datetime__gte=now - relativedelta(weeks=1))
|
||||
elif order_by_field_name == "total_fans_last_month":
|
||||
qs = qs.filter(totals_updated_datetime__gte=now-relativedelta(months=1))
|
||||
qs = qs.filter(totals_updated_datetime__gte=now - relativedelta(months=1))
|
||||
elif order_by_field_name == "total_fans_last_year":
|
||||
qs = qs.filter(totals_updated_datetime__gte=now-relativedelta(years=1))
|
||||
qs = qs.filter(totals_updated_datetime__gte=now - relativedelta(years=1))
|
||||
elif order_by_field_name == "total_activity_last_week":
|
||||
qs = qs.filter(totals_updated_datetime__gte=now-relativedelta(weeks=1))
|
||||
qs = qs.filter(totals_updated_datetime__gte=now - relativedelta(weeks=1))
|
||||
elif order_by_field_name == "total_activity_last_month":
|
||||
qs = qs.filter(totals_updated_datetime__gte=now-relativedelta(months=1))
|
||||
qs = qs.filter(totals_updated_datetime__gte=now - relativedelta(months=1))
|
||||
elif order_by_field_name == "total_activity_last_year":
|
||||
qs = qs.filter(totals_updated_datetime__gte=now-relativedelta(years=1))
|
||||
qs = qs.filter(totals_updated_datetime__gte=now - relativedelta(years=1))
|
||||
|
||||
return qs
|
||||
|
||||
|
@ -449,21 +450,21 @@ class ProjectWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
|||
## Custom values for selectors
|
||||
######################################################
|
||||
|
||||
class PointsViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||
ModelCrudViewSet, BulkUpdateOrderMixin):
|
||||
class EpicStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||
ModelCrudViewSet, BulkUpdateOrderMixin):
|
||||
|
||||
model = models.Points
|
||||
serializer_class = serializers.PointsSerializer
|
||||
validator_class = validators.PointsValidator
|
||||
permission_classes = (permissions.PointsPermission,)
|
||||
model = models.EpicStatus
|
||||
serializer_class = serializers.EpicStatusSerializer
|
||||
validator_class = validators.EpicStatusValidator
|
||||
permission_classes = (permissions.EpicStatusPermission,)
|
||||
filter_backends = (filters.CanViewProjectFilterBackend,)
|
||||
filter_fields = ('project',)
|
||||
bulk_update_param = "bulk_points"
|
||||
bulk_update_perm = "change_points"
|
||||
bulk_update_order_action = services.bulk_update_points_order
|
||||
move_on_destroy_related_class = RolePoints
|
||||
move_on_destroy_related_field = "points"
|
||||
move_on_destroy_project_default_field = "default_points"
|
||||
bulk_update_param = "bulk_epic_statuses"
|
||||
bulk_update_perm = "change_epicstatus"
|
||||
bulk_update_order_action = services.bulk_update_epic_status_order
|
||||
move_on_destroy_related_class = Epic
|
||||
move_on_destroy_related_field = "status"
|
||||
move_on_destroy_project_default_field = "default_epic_status"
|
||||
|
||||
|
||||
class UserStoryStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||
|
@ -483,6 +484,23 @@ class UserStoryStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
|||
move_on_destroy_project_default_field = "default_us_status"
|
||||
|
||||
|
||||
class PointsViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||
ModelCrudViewSet, BulkUpdateOrderMixin):
|
||||
|
||||
model = models.Points
|
||||
serializer_class = serializers.PointsSerializer
|
||||
validator_class = validators.PointsValidator
|
||||
permission_classes = (permissions.PointsPermission,)
|
||||
filter_backends = (filters.CanViewProjectFilterBackend,)
|
||||
filter_fields = ('project',)
|
||||
bulk_update_param = "bulk_points"
|
||||
bulk_update_perm = "change_points"
|
||||
bulk_update_order_action = services.bulk_update_points_order
|
||||
move_on_destroy_related_class = RolePoints
|
||||
move_on_destroy_related_field = "points"
|
||||
move_on_destroy_project_default_field = "default_points"
|
||||
|
||||
|
||||
class TaskStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||
ModelCrudViewSet, BulkUpdateOrderMixin):
|
||||
|
||||
|
|
|
@ -83,6 +83,12 @@ class BaseAttachmentViewSet(HistoryResourceMixin, WatchedResourceMixin,
|
|||
return obj.content_object
|
||||
|
||||
|
||||
class EpicAttachmentViewSet(BaseAttachmentViewSet):
|
||||
permission_classes = (permissions.EpicAttachmentPermission,)
|
||||
filter_backends = (filters.CanViewEpicAttachmentFilterBackend,)
|
||||
content_type = "epics.epic"
|
||||
|
||||
|
||||
class UserStoryAttachmentViewSet(BaseAttachmentViewSet):
|
||||
permission_classes = (permissions.UserStoryAttachmentPermission,)
|
||||
filter_backends = (filters.CanViewUserStoryAttachmentFilterBackend,)
|
||||
|
|
|
@ -28,6 +28,15 @@ class IsAttachmentOwnerPerm(PermissionComponent):
|
|||
return False
|
||||
|
||||
|
||||
class EpicAttachmentPermission(TaigaResourcePermission):
|
||||
retrieve_perms = HasProjectPerm('view_epic') | IsAttachmentOwnerPerm()
|
||||
create_perms = HasProjectPerm('modify_epic')
|
||||
update_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm()
|
||||
partial_update_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm()
|
||||
destroy_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm()
|
||||
list_perms = AllowAny()
|
||||
|
||||
|
||||
class UserStoryAttachmentPermission(TaigaResourcePermission):
|
||||
retrieve_perms = HasProjectPerm('view_us') | IsAttachmentOwnerPerm()
|
||||
create_perms = HasProjectPerm('modify_us')
|
||||
|
@ -67,7 +76,9 @@ class WikiAttachmentPermission(TaigaResourcePermission):
|
|||
class RawAttachmentPerm(PermissionComponent):
|
||||
def check_permissions(self, request, view, obj=None):
|
||||
is_owner = IsAttachmentOwnerPerm().check_permissions(request, view, obj)
|
||||
if obj.content_type.app_label == "userstories" and obj.content_type.model == "userstory":
|
||||
if obj.content_type.app_label == "epics" and obj.content_type.model == "epic":
|
||||
return EpicAttachmentPermission(request, view).check_permissions('retrieve', obj) or is_owner
|
||||
elif obj.content_type.app_label == "userstories" and obj.content_type.model == "userstory":
|
||||
return UserStoryAttachmentPermission(request, view).check_permissions('retrieve', obj) or is_owner
|
||||
elif obj.content_type.app_label == "tasks" and obj.content_type.model == "task":
|
||||
return TaskAttachmentPermission(request, view).check_permissions('retrieve', obj) or is_owner
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
default_app_config = "taiga.projects.epics.apps.EpicsAppConfig"
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.apps import apps
|
||||
from django.db.models import signals
|
||||
|
||||
|
||||
def connect_epics_signals():
|
||||
from taiga.projects.tagging import signals as tagging_handlers
|
||||
|
||||
# Tags
|
||||
signals.pre_save.connect(tagging_handlers.tags_normalization,
|
||||
sender=apps.get_model("epics", "Epic"),
|
||||
dispatch_uid="tags_normalization_epic")
|
||||
|
||||
|
||||
def connect_all_epics_signals():
|
||||
connect_epics_signals()
|
||||
|
||||
|
||||
def disconnect_epics_signals():
|
||||
signals.pre_save.disconnect(sender=apps.get_model("epics", "Task"),
|
||||
dispatch_uid="tags_normalization")
|
||||
|
||||
|
||||
def disconnect_all_epics_signals():
|
||||
disconnect_epics_signals()
|
||||
|
||||
|
||||
class EpicsAppConfig(AppConfig):
|
||||
name = "taiga.projects.epics"
|
||||
verbose_name = "Epics"
|
||||
|
||||
def ready(self):
|
||||
connect_all_epics_signals()
|
|
@ -0,0 +1,73 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.2 on 2016-06-29 14:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import taiga.projects.notifications.mixins
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('projects', '0049_auto_20160629_1443'),
|
||||
('userstories', '0012_auto_20160614_1201'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Epic',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('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')),
|
||||
('ref', models.BigIntegerField(blank=True, db_index=True, default=None, null=True, verbose_name='ref')),
|
||||
('is_closed', models.BooleanField(default=False)),
|
||||
('epics_order', models.IntegerField(default=10000, verbose_name='epics order')),
|
||||
('created_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='created date')),
|
||||
('modified_date', models.DateTimeField(verbose_name='modified date')),
|
||||
('finish_date', models.DateTimeField(blank=True, null=True, verbose_name='finish date')),
|
||||
('subject', models.TextField(verbose_name='subject')),
|
||||
('description', models.TextField(blank=True, verbose_name='description')),
|
||||
('client_requirement', models.BooleanField(default=False, verbose_name='is client requirement')),
|
||||
('team_requirement', models.BooleanField(default=False, verbose_name='is team requirement')),
|
||||
('assigned_to', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='epics_assigned_to_me', to=settings.AUTH_USER_MODEL, verbose_name='assigned to')),
|
||||
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owned_epics', to=settings.AUTH_USER_MODEL, verbose_name='owner')),
|
||||
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='epics', to='projects.Project', verbose_name='project')),
|
||||
('status', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='epics', to='projects.EpicStatus', verbose_name='status')),
|
||||
('user_stories', models.ManyToManyField(related_name='epics', to='userstories.UserStory', verbose_name='user stories')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'epics',
|
||||
'ordering': ['project', 'ref'],
|
||||
'verbose_name': 'epic',
|
||||
},
|
||||
bases=(taiga.projects.notifications.mixins.WatchedModelMixin, models.Model),
|
||||
),
|
||||
# Execute trigger after epic update
|
||||
migrations.RunSQL(
|
||||
"""
|
||||
DROP TRIGGER IF EXISTS update_project_tags_colors_on_epic_update ON epics_epic;
|
||||
CREATE TRIGGER update_project_tags_colors_on_epic_update
|
||||
AFTER UPDATE ON epics_epic
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_project_tags_colors();
|
||||
"""
|
||||
),
|
||||
# Execute trigger after epic insert
|
||||
migrations.RunSQL(
|
||||
"""
|
||||
DROP TRIGGER IF EXISTS update_project_tags_colors_on_epic_insert ON epics_epic;
|
||||
CREATE TRIGGER update_project_tags_colors_on_epic_insert
|
||||
AFTER INSERT ON epics_epic
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_project_tags_colors();
|
||||
"""
|
||||
),
|
||||
]
|
|
@ -0,0 +1,91 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
||||
from taiga.projects.tagging.models import TaggedMixin
|
||||
from taiga.projects.occ import OCCModelMixin
|
||||
from taiga.projects.notifications.mixins import WatchedModelMixin
|
||||
from taiga.projects.mixins.blocked import BlockedMixin
|
||||
|
||||
|
||||
class Epic(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.Model):
|
||||
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
||||
verbose_name=_("ref"))
|
||||
project = models.ForeignKey("projects.Project", null=False, blank=False,
|
||||
related_name="epics", verbose_name=_("project"))
|
||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||
related_name="owned_epics", verbose_name=_("owner"),
|
||||
on_delete=models.SET_NULL)
|
||||
status = models.ForeignKey("projects.EpicStatus", null=True, blank=True,
|
||||
related_name="epics", verbose_name=_("status"),
|
||||
on_delete=models.SET_NULL)
|
||||
is_closed = models.BooleanField(default=False)
|
||||
|
||||
epics_order = models.IntegerField(null=False, blank=False, default=10000,
|
||||
verbose_name=_("epics order"))
|
||||
|
||||
created_date = models.DateTimeField(null=False, blank=False,
|
||||
verbose_name=_("created date"),
|
||||
default=timezone.now)
|
||||
modified_date = models.DateTimeField(null=False, blank=False,
|
||||
verbose_name=_("modified date"))
|
||||
finish_date = models.DateTimeField(null=True, blank=True,
|
||||
verbose_name=_("finish date"))
|
||||
|
||||
subject = models.TextField(null=False, blank=False,
|
||||
verbose_name=_("subject"))
|
||||
description = models.TextField(null=False, blank=True, verbose_name=_("description"))
|
||||
assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
|
||||
default=None, related_name="epics_assigned_to_me",
|
||||
verbose_name=_("assigned to"))
|
||||
client_requirement = models.BooleanField(default=False, null=False, blank=True,
|
||||
verbose_name=_("is client requirement"))
|
||||
team_requirement = models.BooleanField(default=False, null=False, blank=True,
|
||||
verbose_name=_("is team requirement"))
|
||||
|
||||
user_stories = models.ManyToManyField("userstories.UserStory", related_name="epics",
|
||||
verbose_name=_("user stories"))
|
||||
|
||||
attachments = GenericRelation("attachments.Attachment")
|
||||
|
||||
_importing = None
|
||||
|
||||
class Meta:
|
||||
verbose_name = "epic"
|
||||
verbose_name_plural = "epics"
|
||||
ordering = ["project", "ref"]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self._importing or not self.modified_date:
|
||||
self.modified_date = timezone.now()
|
||||
|
||||
if not self.status:
|
||||
self.status = self.project.default_epic_status
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return "({1}) {0}".format(self.ref, self.subject)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Epic %s>" % (self.id)
|
|
@ -5,26 +5,28 @@
|
|||
"fields": {
|
||||
"name": "Scrum",
|
||||
"slug": "scrum",
|
||||
"order": 1,
|
||||
"description": "The agile product backlog in Scrum is a prioritized features list, containing short descriptions of all functionality desired in the product. When applying Scrum, it's not necessary to start a project with a lengthy, upfront effort to document all requirements. The Scrum product backlog is then allowed to grow and change as more is learned about the product and its customers",
|
||||
"order": 1,
|
||||
"created_date": "2014-04-22T14:48:43.596Z",
|
||||
"modified_date": "2014-07-25T10:02:46.479Z",
|
||||
"modified_date": "2016-06-29T14:52:11.273Z",
|
||||
"default_owner_role": "product-owner",
|
||||
"is_epics_activated": true,
|
||||
"is_backlog_activated": true,
|
||||
"is_kanban_activated": false,
|
||||
"is_wiki_activated": true,
|
||||
"is_issues_activated": true,
|
||||
"videoconferences": null,
|
||||
"videoconferences_extra_data": "",
|
||||
"default_options": "{\"severity\": \"Normal\", \"priority\": \"Normal\", \"task_status\": \"New\", \"points\": \"?\", \"us_status\": \"New\", \"issue_type\": \"Bug\", \"issue_status\": \"New\"}",
|
||||
"us_statuses": "[{\"is_archived\": false, \"slug\": \"new\", \"is_closed\": false, \"wip_limit\": null, \"order\": 1, \"name\": \"New\", \"color\": \"#999999\"}, {\"is_archived\": false, \"slug\": \"ready\", \"is_closed\": false, \"wip_limit\": null, \"order\": 2, \"name\": \"Ready\", \"color\": \"#ff8a84\"}, {\"is_archived\": false, \"slug\": \"in-progress\", \"is_closed\": false, \"wip_limit\": null, \"order\": 3, \"name\": \"In progress\", \"color\": \"#ff9900\"}, {\"is_archived\": false, \"slug\": \"ready-for-test\", \"is_closed\": false, \"wip_limit\": null, \"order\": 4, \"name\": \"Ready for test\", \"color\": \"#fcc000\"}, {\"is_archived\": false, \"slug\": \"done\", \"is_closed\": true, \"wip_limit\": null, \"order\": 5, \"name\": \"Done\", \"color\": \"#669900\"}, {\"is_archived\": true, \"slug\": \"archived\", \"is_closed\": true, \"wip_limit\": null, \"order\": 6, \"name\": \"Archived\", \"color\": \"#5c3566\"}]",
|
||||
"default_options": "{\"epic_status\": \"New\", \"severity\": \"Normal\", \"issue_type\": \"Bug\", \"us_status\": \"New\", \"points\": \"?\", \"priority\": \"Normal\", \"task_status\": \"New\", \"issue_status\": \"New\"}",
|
||||
"epic_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"Ready\", \"color\": \"#ff8a84\", \"slug\": \"ready\", \"is_closed\": false}, {\"order\": 3, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 4, \"name\": \"Ready for test\", \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"order\": 5, \"name\": \"Done\", \"color\": \"#669900\", \"slug\": \"done\", \"is_closed\": true}]",
|
||||
"us_statuses": "[{\"name\": \"New\", \"is_archived\": false, \"wip_limit\": null, \"order\": 1, \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"name\": \"Ready\", \"is_archived\": false, \"wip_limit\": null, \"order\": 2, \"color\": \"#ff8a84\", \"slug\": \"ready\", \"is_closed\": false}, {\"name\": \"In progress\", \"is_archived\": false, \"wip_limit\": null, \"order\": 3, \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"name\": \"Ready for test\", \"is_archived\": false, \"wip_limit\": null, \"order\": 4, \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"name\": \"Done\", \"is_archived\": false, \"wip_limit\": null, \"order\": 5, \"color\": \"#669900\", \"slug\": \"done\", \"is_closed\": true}, {\"name\": \"Archived\", \"is_archived\": true, \"wip_limit\": null, \"order\": 6, \"color\": \"#5c3566\", \"slug\": \"archived\", \"is_closed\": true}]",
|
||||
"points": "[{\"order\": 1, \"name\": \"?\", \"value\": null}, {\"order\": 2, \"name\": \"0\", \"value\": 0.0}, {\"order\": 3, \"name\": \"1/2\", \"value\": 0.5}, {\"order\": 4, \"name\": \"1\", \"value\": 1.0}, {\"order\": 5, \"name\": \"2\", \"value\": 2.0}, {\"order\": 6, \"name\": \"3\", \"value\": 3.0}, {\"order\": 7, \"name\": \"5\", \"value\": 5.0}, {\"order\": 8, \"name\": \"8\", \"value\": 8.0}, {\"order\": 9, \"name\": \"10\", \"value\": 10.0}, {\"order\": 10, \"name\": \"13\", \"value\": 13.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]",
|
||||
"task_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#ff9900\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#ffcc00\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#669900\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#999999\", \"is_closed\": false}]",
|
||||
"issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#8C2318\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#5E8C6A\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#88A65E\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#BFB35A\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#89BAB4\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"slug\": \"rejected\", \"color\": \"#CC0000\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"slug\": \"posponed\", \"color\": \"#666666\", \"is_closed\": false}]",
|
||||
"task_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#ffcc00\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#669900\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#999999\", \"slug\": \"needs-info\", \"is_closed\": false}]",
|
||||
"issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#8C2318\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#5E8C6A\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#88A65E\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#BFB35A\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#89BAB4\", \"slug\": \"needs-info\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"color\": \"#CC0000\", \"slug\": \"rejected\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"color\": \"#666666\", \"slug\": \"posponed\", \"is_closed\": false}]",
|
||||
"issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#89BAB4\"}, {\"order\": 2, \"name\": \"Question\", \"color\": \"#ba89a8\"}, {\"order\": 3, \"name\": \"Enhancement\", \"color\": \"#89a8ba\"}]",
|
||||
"priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]",
|
||||
"severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]",
|
||||
"roles": "[{\"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true, \"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\"]}, {\"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true, \"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\"]}, {\"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true, \"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\"]}, {\"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true, \"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\"]}, {\"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false, \"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\"]}, {\"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false, \"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\"]}]"
|
||||
"roles": "[{\"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\"], \"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true}, {\"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\"], \"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true}, {\"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\"], \"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true}, {\"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\"], \"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true}, {\"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\"], \"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false}, {\"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\"], \"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false}]"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -33,26 +35,28 @@
|
|||
"fields": {
|
||||
"name": "Kanban",
|
||||
"slug": "kanban",
|
||||
"order": 2,
|
||||
"description": "Kanban is a method for managing knowledge work with an emphasis on just-in-time delivery while not overloading the team members. In this approach, the process, from definition of a task to its delivery to the customer, is displayed for participants to see and team members pull work from a queue.",
|
||||
"order": 2,
|
||||
"created_date": "2014-04-22T14:50:19.738Z",
|
||||
"modified_date": "2014-07-25T13:11:42.754Z",
|
||||
"modified_date": "2016-06-29T14:52:15.232Z",
|
||||
"default_owner_role": "product-owner",
|
||||
"is_epics_activated": true,
|
||||
"is_backlog_activated": false,
|
||||
"is_kanban_activated": true,
|
||||
"is_wiki_activated": false,
|
||||
"is_issues_activated": false,
|
||||
"videoconferences": null,
|
||||
"videoconferences_extra_data": "",
|
||||
"default_options": "{\"severity\": \"Normal\", \"priority\": \"Normal\", \"task_status\": \"New\", \"points\": \"?\", \"us_status\": \"New\", \"issue_type\": \"Bug\", \"issue_status\": \"New\"}",
|
||||
"us_statuses": "[{\"is_archived\": false, \"slug\": \"new\", \"is_closed\": false, \"wip_limit\": null, \"order\": 1, \"name\": \"New\", \"color\": \"#999999\"}, {\"is_archived\": false, \"slug\": \"ready\", \"is_closed\": false, \"wip_limit\": null, \"order\": 2, \"name\": \"Ready\", \"color\": \"#f57900\"}, {\"is_archived\": false, \"slug\": \"in-progress\", \"is_closed\": false, \"wip_limit\": null, \"order\": 3, \"name\": \"In progress\", \"color\": \"#729fcf\"}, {\"is_archived\": false, \"slug\": \"ready-for-test\", \"is_closed\": false, \"wip_limit\": null, \"order\": 4, \"name\": \"Ready for test\", \"color\": \"#4e9a06\"}, {\"is_archived\": false, \"slug\": \"done\", \"is_closed\": true, \"wip_limit\": null, \"order\": 5, \"name\": \"Done\", \"color\": \"#cc0000\"}, {\"is_archived\": true, \"slug\": \"archived\", \"is_closed\": true, \"wip_limit\": null, \"order\": 6, \"name\": \"Archived\", \"color\": \"#5c3566\"}]",
|
||||
"default_options": "{\"epic_status\": \"New\", \"severity\": \"Normal\", \"issue_type\": \"Bug\", \"us_status\": \"New\", \"points\": \"?\", \"priority\": \"Normal\", \"task_status\": \"New\", \"issue_status\": \"New\"}",
|
||||
"epic_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"Ready\", \"color\": \"#ff8a84\", \"slug\": \"ready\", \"is_closed\": false}, {\"order\": 3, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 4, \"name\": \"Ready for test\", \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"order\": 5, \"name\": \"Done\", \"color\": \"#669900\", \"slug\": \"done\", \"is_closed\": true}]",
|
||||
"us_statuses": "[{\"name\": \"New\", \"is_archived\": false, \"wip_limit\": null, \"order\": 1, \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"name\": \"Ready\", \"is_archived\": false, \"wip_limit\": null, \"order\": 2, \"color\": \"#f57900\", \"slug\": \"ready\", \"is_closed\": false}, {\"name\": \"In progress\", \"is_archived\": false, \"wip_limit\": null, \"order\": 3, \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"name\": \"Ready for test\", \"is_archived\": false, \"wip_limit\": null, \"order\": 4, \"color\": \"#4e9a06\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"name\": \"Done\", \"is_archived\": false, \"wip_limit\": null, \"order\": 5, \"color\": \"#cc0000\", \"slug\": \"done\", \"is_closed\": true}, {\"name\": \"Archived\", \"is_archived\": true, \"wip_limit\": null, \"order\": 6, \"color\": \"#5c3566\", \"slug\": \"archived\", \"is_closed\": true}]",
|
||||
"points": "[{\"order\": 1, \"name\": \"?\", \"value\": null}, {\"order\": 2, \"name\": \"0\", \"value\": 0.0}, {\"order\": 3, \"name\": \"1/2\", \"value\": 0.5}, {\"order\": 4, \"name\": \"1\", \"value\": 1.0}, {\"order\": 5, \"name\": \"2\", \"value\": 2.0}, {\"order\": 6, \"name\": \"3\", \"value\": 3.0}, {\"order\": 7, \"name\": \"5\", \"value\": 5.0}, {\"order\": 8, \"name\": \"8\", \"value\": 8.0}, {\"order\": 9, \"name\": \"10\", \"value\": 10.0}, {\"order\": 10, \"name\": \"13\", \"value\": 13.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]",
|
||||
"task_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#729fcf\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#f57900\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#4e9a06\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#cc0000\", \"is_closed\": false}]",
|
||||
"issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#729fcf\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#f57900\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#4e9a06\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#cc0000\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"slug\": \"rejected\", \"color\": \"#d3d7cf\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"slug\": \"posponed\", \"color\": \"#75507b\", \"is_closed\": false}]",
|
||||
"task_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#f57900\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#4e9a06\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#cc0000\", \"slug\": \"needs-info\", \"is_closed\": false}]",
|
||||
"issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#f57900\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#4e9a06\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#cc0000\", \"slug\": \"needs-info\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"color\": \"#d3d7cf\", \"slug\": \"rejected\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"color\": \"#75507b\", \"slug\": \"posponed\", \"is_closed\": false}]",
|
||||
"issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#cc0000\"}, {\"order\": 2, \"name\": \"Question\", \"color\": \"#729fcf\"}, {\"order\": 3, \"name\": \"Enhancement\", \"color\": \"#4e9a06\"}]",
|
||||
"priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#999999\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#4e9a06\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]",
|
||||
"severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#999999\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#729fcf\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#4e9a06\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#f57900\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]",
|
||||
"roles": "[{\"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true, \"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\"]}, {\"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true, \"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\"]}, {\"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true, \"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\"]}, {\"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true, \"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\"]}, {\"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false, \"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\"]}, {\"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false, \"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\"]}]"
|
||||
"roles": "[{\"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\"], \"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true}, {\"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\"], \"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true}, {\"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\"], \"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true}, {\"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\"], \"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true}, {\"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\"], \"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false}, {\"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\"], \"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false}]"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -34,6 +34,7 @@ from taiga.permissions.choices import ANON_PERMISSIONS
|
|||
from taiga.projects.choices import BLOCKED_BY_STAFF
|
||||
from taiga.external_apps.models import Application, ApplicationToken
|
||||
from taiga.projects.models import *
|
||||
from taiga.projects.epics.models import *
|
||||
from taiga.projects.milestones.models import *
|
||||
from taiga.projects.notifications.choices import NotifyLevel
|
||||
from taiga.projects.services.stats import get_stats_for_project
|
||||
|
@ -109,6 +110,8 @@ NUM_PROJECTS =getattr(settings, "SAMPLE_DATA_NUM_PROJECTS", 4)
|
|||
NUM_EMPTY_PROJECTS = getattr(settings, "SAMPLE_DATA_NUM_EMPTY_PROJECTS", 2)
|
||||
NUM_BLOCKED_PROJECTS = getattr(settings, "SAMPLE_DATA_NUM_BLOCKED_PROJECTS", 1)
|
||||
NUM_MILESTONES = getattr(settings, "SAMPLE_DATA_NUM_MILESTONES", (1, 5))
|
||||
NUM_EPICS = getattr(settings, "SAMPLE_DATA_NUM_EPICS", (4, 8))
|
||||
NUM_USS_EPICS = getattr(settings, "SAMPLE_DATA_NUM_USS_EPICS", (2, 6))
|
||||
NUM_USS = getattr(settings, "SAMPLE_DATA_NUM_USS", (3, 7))
|
||||
NUM_TASKS_FINISHED = getattr(settings, "SAMPLE_DATA_NUM_TASKS_FINISHED", (1, 5))
|
||||
NUM_TASKS = getattr(settings, "SAMPLE_DATA_NUM_TASKS", (0, 4))
|
||||
|
@ -255,6 +258,11 @@ class Command(BaseCommand):
|
|||
if self.sd.boolean():
|
||||
self.create_wiki_page(project, wiki_link.href)
|
||||
|
||||
# create epics
|
||||
for y in range(self.sd.int(*NUM_EPICS)):
|
||||
epic = self.create_epic(project)
|
||||
|
||||
|
||||
|
||||
project.refresh_from_db()
|
||||
|
||||
|
@ -494,6 +502,59 @@ class Command(BaseCommand):
|
|||
|
||||
return milestone
|
||||
|
||||
def create_epic(self, project):
|
||||
epic = Epic.objects.create(subject=self.sd.choice(SUBJECT_CHOICES),
|
||||
project=project,
|
||||
owner=self.sd.db_object_from_queryset(
|
||||
project.memberships.filter(user__isnull=False)).user,
|
||||
description=self.sd.paragraph(),
|
||||
status=self.sd.db_object_from_queryset(project.epic_statuses.filter(
|
||||
is_closed=False)),
|
||||
tags=self.sd.words(1, 3).split(" "))
|
||||
|
||||
# TODO: Epic custom attributes
|
||||
#custom_attributes_values = {str(ca.id): self.get_custom_attributes_value(ca.type) for ca
|
||||
# in project.epiccustomattributes.all() if self.sd.boolean()}
|
||||
#if custom_attributes_values:
|
||||
# epic.custom_attributes_values.attributes_values = custom_attributes_values
|
||||
# epic.custom_attributes_values.save()
|
||||
|
||||
|
||||
for i in range(self.sd.int(*NUM_ATTACHMENTS)):
|
||||
attachment = self.create_attachment(epic, i+1)
|
||||
|
||||
if self.sd.choice([True, True, False, True, True]):
|
||||
epic.assigned_to = self.sd.db_object_from_queryset(project.memberships.filter(
|
||||
user__isnull=False)).user
|
||||
epic.save()
|
||||
|
||||
# TODO: Epic history
|
||||
#take_snapshot(epic,
|
||||
# comment=self.sd.paragraph(),
|
||||
# user=epic.owner)
|
||||
#
|
||||
# Add history entry
|
||||
#epic.status=self.sd.db_object_from_queryset(project.epic_statuses.filter(is_closed=False))
|
||||
#epic.save()
|
||||
#take_snapshot(epic,
|
||||
# comment=self.sd.paragraph(),
|
||||
# user=epic.owner)
|
||||
|
||||
# TODO: Epic voters
|
||||
#self.create_votes(epic)
|
||||
# TODO: Epic watchers
|
||||
#self.create_watchers(epic)
|
||||
|
||||
if self.sd.choice([True, True, False, True, True]):
|
||||
filters = {}
|
||||
if self.sd.choice([True, True, False, True, True]):
|
||||
filters = {"project": epic.project}
|
||||
n = self.sd.choice(list(range(self.sd.int(*NUM_USS_EPICS))))
|
||||
|
||||
epic.user_stories.add(*UserStory.objects.filter(**filters).order_by("?")[:n])
|
||||
|
||||
return epic
|
||||
|
||||
def create_project(self, counter, is_private=None, blocked_code=None):
|
||||
if is_private is None:
|
||||
is_private=self.sd.boolean()
|
||||
|
|
|
@ -110,6 +110,7 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
('projects', '0029_project_is_looking_for_people'),
|
||||
('likes', '0001_initial'),
|
||||
('timeline', '0004_auto_20150603_1312'),
|
||||
('likes', '0001_initial'),
|
||||
]
|
||||
|
|
|
@ -9,6 +9,9 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
('projects', '0045_merge'),
|
||||
('userstories', '0011_userstory_tribe_gig'),
|
||||
('tasks', '0009_auto_20151104_1131'),
|
||||
('issues', '0006_remove_issue_watchers'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.2 on 2016-06-29 14:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_pgjson.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('projects', '0048_auto_20160615_1508'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EpicStatus',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||
('slug', models.SlugField(blank=True, max_length=255, verbose_name='slug')),
|
||||
('order', models.IntegerField(default=10, verbose_name='order')),
|
||||
('is_closed', models.BooleanField(default=False, verbose_name='is closed')),
|
||||
('color', models.CharField(default='#999999', max_length=20, verbose_name='color')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'epic statuses',
|
||||
'ordering': ['project', 'order', 'name'],
|
||||
'verbose_name': 'epic status',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='issuestatus',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'issue status', 'verbose_name_plural': 'issue statuses'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='issuetype',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'issue type', 'verbose_name_plural': 'issue types'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='membership',
|
||||
options={'ordering': ['project', 'user__full_name', 'user__username', 'user__email', 'email'], 'verbose_name': 'membership', 'verbose_name_plural': 'memberships'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='points',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'points', 'verbose_name_plural': 'points'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='priority',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'priority', 'verbose_name_plural': 'priorities'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='severity',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'severity', 'verbose_name_plural': 'severities'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='taskstatus',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'task status', 'verbose_name_plural': 'task statuses'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='userstorystatus',
|
||||
options={'ordering': ['project', 'order', 'name'], 'verbose_name': 'user story status', 'verbose_name_plural': 'user story statuses'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='is_epics_activated',
|
||||
field=models.BooleanField(default=True, verbose_name='active epics panel'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projecttemplate',
|
||||
name='epic_statuses',
|
||||
field=django_pgjson.fields.JsonField(blank=True, null=True, verbose_name='epic statuses'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projecttemplate',
|
||||
name='is_epics_activated',
|
||||
field=models.BooleanField(default=True, verbose_name='active epics panel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='anon_permissions',
|
||||
field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_epic', 'View epic'), ('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'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='public_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_epic', 'View epic'), ('add_epic', 'Add epic'), ('modify_epic', 'Modify epic'), ('comment_epic', 'Comment epic'), ('delete_epic', 'Delete epic'), ('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'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='epicstatus',
|
||||
name='project',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='epic_statuses', to='projects.Project', verbose_name='project'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='default_epic_status',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='projects.EpicStatus', verbose_name='default epic status'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='epicstatus',
|
||||
unique_together=set([('project', 'slug'), ('project', 'name')]),
|
||||
),
|
||||
]
|
|
@ -102,19 +102,20 @@ class Membership(models.Model):
|
|||
verbose_name_plural = "memberships"
|
||||
unique_together = ("user", "project",)
|
||||
ordering = ["project", "user__full_name", "user__username", "user__email", "email"]
|
||||
permissions = (
|
||||
("view_membership", "Can view membership"),
|
||||
)
|
||||
|
||||
|
||||
class ProjectDefaults(models.Model):
|
||||
default_points = models.OneToOneField("projects.Points", on_delete=models.SET_NULL,
|
||||
related_name="+", null=True, blank=True,
|
||||
verbose_name=_("default points"))
|
||||
default_epic_status = models.OneToOneField("projects.EpicStatus",
|
||||
on_delete=models.SET_NULL, related_name="+",
|
||||
null=True, blank=True,
|
||||
verbose_name=_("default epic status"))
|
||||
default_us_status = models.OneToOneField("projects.UserStoryStatus",
|
||||
on_delete=models.SET_NULL, related_name="+",
|
||||
null=True, blank=True,
|
||||
verbose_name=_("default US status"))
|
||||
default_points = models.OneToOneField("projects.Points", on_delete=models.SET_NULL,
|
||||
related_name="+", null=True, blank=True,
|
||||
verbose_name=_("default points"))
|
||||
default_task_status = models.OneToOneField("projects.TaskStatus",
|
||||
on_delete=models.SET_NULL, related_name="+",
|
||||
null=True, blank=True,
|
||||
|
@ -164,6 +165,8 @@ class Project(ProjectDefaults, TaggedMixin, TagsColorsdMixin, models.Model):
|
|||
verbose_name=_("total of milestones"))
|
||||
total_story_points = models.FloatField(null=True, blank=True, verbose_name=_("total story points"))
|
||||
|
||||
is_epics_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||
verbose_name=_("active epics panel"))
|
||||
is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||
verbose_name=_("active backlog panel"))
|
||||
is_kanban_activated = models.BooleanField(default=False, null=False, blank=True,
|
||||
|
@ -504,6 +507,39 @@ class ProjectModulesConfig(models.Model):
|
|||
ordering = ["project"]
|
||||
|
||||
|
||||
# Epic common Models
|
||||
class EpicStatus(models.Model):
|
||||
name = models.CharField(max_length=255, null=False, blank=False,
|
||||
verbose_name=_("name"))
|
||||
slug = models.SlugField(max_length=255, null=False, blank=True,
|
||||
verbose_name=_("slug"))
|
||||
order = models.IntegerField(default=10, null=False, blank=False,
|
||||
verbose_name=_("order"))
|
||||
is_closed = models.BooleanField(default=False, null=False, blank=True,
|
||||
verbose_name=_("is closed"))
|
||||
color = models.CharField(max_length=20, null=False, blank=False, default="#999999",
|
||||
verbose_name=_("color"))
|
||||
project = models.ForeignKey("Project", null=False, blank=False,
|
||||
related_name="epic_statuses", verbose_name=_("project"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = "epic status"
|
||||
verbose_name_plural = "epic statuses"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = (("project", "name"), ("project", "slug"))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
qs = self.project.epic_statuses
|
||||
if self.id:
|
||||
qs = qs.exclude(id=self.id)
|
||||
|
||||
self.slug = slugify_uniquely_for_queryset(self.name, qs)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
# User Stories common Models
|
||||
class UserStoryStatus(models.Model):
|
||||
name = models.CharField(max_length=255, null=False, blank=False,
|
||||
|
@ -528,9 +564,6 @@ class UserStoryStatus(models.Model):
|
|||
verbose_name_plural = "user story statuses"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = (("project", "name"), ("project", "slug"))
|
||||
permissions = (
|
||||
("view_userstorystatus", "Can view user story status"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -559,9 +592,6 @@ class Points(models.Model):
|
|||
verbose_name_plural = "points"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = ("project", "name")
|
||||
permissions = (
|
||||
("view_points", "Can view points"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -588,9 +618,6 @@ class TaskStatus(models.Model):
|
|||
verbose_name_plural = "task statuses"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = (("project", "name"), ("project", "slug"))
|
||||
permissions = (
|
||||
("view_taskstatus", "Can view task status"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -621,9 +648,6 @@ class Priority(models.Model):
|
|||
verbose_name_plural = "priorities"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = ("project", "name")
|
||||
permissions = (
|
||||
("view_priority", "Can view priority"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -644,9 +668,6 @@ class Severity(models.Model):
|
|||
verbose_name_plural = "severities"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = ("project", "name")
|
||||
permissions = (
|
||||
("view_severity", "Can view severity"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -671,9 +692,6 @@ class IssueStatus(models.Model):
|
|||
verbose_name_plural = "issue statuses"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = (("project", "name"), ("project", "slug"))
|
||||
permissions = (
|
||||
("view_issuestatus", "Can view issue status"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -702,9 +720,6 @@ class IssueType(models.Model):
|
|||
verbose_name_plural = "issue types"
|
||||
ordering = ["project", "order", "name"]
|
||||
unique_together = ("project", "name")
|
||||
permissions = (
|
||||
("view_issuetype", "Can view issue type"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -728,6 +743,8 @@ class ProjectTemplate(models.Model):
|
|||
blank=False,
|
||||
verbose_name=_("default owner's role"))
|
||||
|
||||
is_epics_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||
verbose_name=_("active epics panel"))
|
||||
is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||
verbose_name=_("active backlog panel"))
|
||||
is_kanban_activated = models.BooleanField(default=False, null=False, blank=True,
|
||||
|
@ -743,6 +760,7 @@ class ProjectTemplate(models.Model):
|
|||
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"))
|
||||
|
@ -770,6 +788,7 @@ class ProjectTemplate(models.Model):
|
|||
super().save(*args, **kwargs)
|
||||
|
||||
def load_data_from_project(self, project):
|
||||
self.is_epics_activated = project.is_epics_activated
|
||||
self.is_backlog_activated = project.is_backlog_activated
|
||||
self.is_kanban_activated = project.is_kanban_activated
|
||||
self.is_wiki_activated = project.is_wiki_activated
|
||||
|
@ -779,6 +798,7 @@ class ProjectTemplate(models.Model):
|
|||
|
||||
self.default_options = {
|
||||
"points": getattr(project.default_points, "name", None),
|
||||
"epic_status": getattr(project.default_epic_status, "name", None),
|
||||
"us_status": getattr(project.default_us_status, "name", None),
|
||||
"task_status": getattr(project.default_task_status, "name", None),
|
||||
"issue_status": getattr(project.default_issue_status, "name", None),
|
||||
|
@ -787,6 +807,16 @@ class ProjectTemplate(models.Model):
|
|||
"severity": getattr(project.default_severity, "name", None)
|
||||
}
|
||||
|
||||
self.epic_statuses = []
|
||||
for epic_status in project.epic_statuses.all():
|
||||
self.epic_statuses.append({
|
||||
"name": epic_status.name,
|
||||
"slug": epic_status.slug,
|
||||
"is_closed": epic_status.is_closed,
|
||||
"color": epic_status.color,
|
||||
"order": epic_status.order,
|
||||
})
|
||||
|
||||
self.us_statuses = []
|
||||
for us_status in project.us_statuses.all():
|
||||
self.us_statuses.append({
|
||||
|
@ -874,6 +904,7 @@ class ProjectTemplate(models.Model):
|
|||
raise Exception("Project need an id (must be a saved project)")
|
||||
|
||||
project.creation_template = self
|
||||
project.is_epics_activated = self.is_epics_activated
|
||||
project.is_backlog_activated = self.is_backlog_activated
|
||||
project.is_kanban_activated = self.is_kanban_activated
|
||||
project.is_wiki_activated = self.is_wiki_activated
|
||||
|
@ -881,6 +912,16 @@ class ProjectTemplate(models.Model):
|
|||
project.videoconferences = self.videoconferences
|
||||
project.videoconferences_extra_data = self.videoconferences_extra_data
|
||||
|
||||
for epic_status in self.epic_statuses:
|
||||
EpicStatus.objects.create(
|
||||
name=epic_status["name"],
|
||||
slug=epic_status["slug"],
|
||||
is_closed=epic_status["is_closed"],
|
||||
color=epic_status["color"],
|
||||
order=epic_status["order"],
|
||||
project=project
|
||||
)
|
||||
|
||||
for us_status in self.us_statuses:
|
||||
UserStoryStatus.objects.create(
|
||||
name=us_status["name"],
|
||||
|
@ -955,12 +996,16 @@ class ProjectTemplate(models.Model):
|
|||
permissions=role['permissions']
|
||||
)
|
||||
|
||||
if self.points:
|
||||
project.default_points = Points.objects.get(name=self.default_options["points"],
|
||||
project=project)
|
||||
if self.epic_statuses:
|
||||
project.default_epic_status = EpicStatus.objects.get(name=self.default_options["epic_status"],
|
||||
project=project)
|
||||
|
||||
if self.us_statuses:
|
||||
project.default_us_status = UserStoryStatus.objects.get(name=self.default_options["us_status"],
|
||||
project=project)
|
||||
if self.points:
|
||||
project.default_points = Points.objects.get(name=self.default_options["points"],
|
||||
project=project)
|
||||
|
||||
if self.task_statuses:
|
||||
project.default_task_status = TaskStatus.objects.get(name=self.default_options["task_status"],
|
||||
|
|
|
@ -109,6 +109,18 @@ class MembershipPermission(TaigaResourcePermission):
|
|||
resend_invitation_perms = IsProjectAdmin()
|
||||
|
||||
|
||||
# Epics
|
||||
|
||||
class EpicStatusPermission(TaigaResourcePermission):
|
||||
retrieve_perms = HasProjectPerm('view_project')
|
||||
create_perms = IsProjectAdmin()
|
||||
update_perms = IsProjectAdmin()
|
||||
partial_update_perms = IsProjectAdmin()
|
||||
destroy_perms = IsProjectAdmin()
|
||||
list_perms = AllowAny()
|
||||
bulk_update_order_perms = IsProjectAdmin()
|
||||
|
||||
|
||||
# User Stories
|
||||
|
||||
class PointsPermission(TaigaResourcePermission):
|
||||
|
|
|
@ -37,11 +37,13 @@ from .notifications.choices import NotifyLevel
|
|||
# Custom values for selectors
|
||||
######################################################
|
||||
|
||||
class PointsSerializer(serializers.LightSerializer):
|
||||
class EpicStatusSerializer(serializers.LightSerializer):
|
||||
id = Field()
|
||||
name = I18NField()
|
||||
slug = Field()
|
||||
order = Field()
|
||||
value = Field()
|
||||
is_closed = Field()
|
||||
color = Field()
|
||||
project = Field(attr="project_id")
|
||||
|
||||
|
||||
|
@ -57,6 +59,14 @@ class UserStoryStatusSerializer(serializers.LightSerializer):
|
|||
project = Field(attr="project_id")
|
||||
|
||||
|
||||
class PointsSerializer(serializers.LightSerializer):
|
||||
id = Field()
|
||||
name = I18NField()
|
||||
order = Field()
|
||||
value = Field()
|
||||
project = Field(attr="project_id")
|
||||
|
||||
|
||||
class TaskStatusSerializer(serializers.LightSerializer):
|
||||
id = Field()
|
||||
name = I18NField()
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
# This makes all code that import services works and
|
||||
# is not the baddest practice ;)
|
||||
|
||||
from .bulk_update_order import update_projects_order_in_bulk
|
||||
from .bulk_update_order import apply_order_updates
|
||||
from .bulk_update_order import bulk_update_severity_order
|
||||
from .bulk_update_order import bulk_update_priority_order
|
||||
from .bulk_update_order import bulk_update_issue_type_order
|
||||
|
@ -27,7 +27,8 @@ from .bulk_update_order import bulk_update_issue_status_order
|
|||
from .bulk_update_order import bulk_update_task_status_order
|
||||
from .bulk_update_order import bulk_update_points_order
|
||||
from .bulk_update_order import bulk_update_userstory_status_order
|
||||
from .bulk_update_order import apply_order_updates
|
||||
from .bulk_update_order import bulk_update_epic_status_order
|
||||
from .bulk_update_order import update_projects_order_in_bulk
|
||||
|
||||
from .filters import get_all_tags
|
||||
|
||||
|
|
|
@ -86,6 +86,23 @@ def update_projects_order_in_bulk(bulk_data: list, field: str, user):
|
|||
db.update_attr_in_bulk_for_ids(memberships_orders, field, model=models.Membership)
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def bulk_update_epic_status_order(project, user, data):
|
||||
cursor = connection.cursor()
|
||||
|
||||
sql = """
|
||||
prepare bulk_update_order as update projects_epicstatus set "order" = $1
|
||||
where projects_epicstatus.id = $2 and
|
||||
projects_epicstatus.project_id = $3;
|
||||
"""
|
||||
cursor.execute(sql)
|
||||
for id, order in data:
|
||||
cursor.execute("EXECUTE bulk_update_order (%s, %s, %s);",
|
||||
(order, id, project.id))
|
||||
cursor.execute("DEALLOCATE bulk_update_order")
|
||||
cursor.close()
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def bulk_update_userstory_status_order(project, user, data):
|
||||
cursor = connection.cursor()
|
||||
|
|
|
@ -117,6 +117,26 @@ def attach_notify_policies(queryset, as_field="notify_policies_attr"):
|
|||
return queryset
|
||||
|
||||
|
||||
def attach_epic_statuses(queryset, as_field="epic_statuses_attr"):
|
||||
"""Attach a json epic statuses representation to each object of the queryset.
|
||||
|
||||
:param queryset: A Django projects queryset object.
|
||||
:param as_field: Attach the epic statuses as an attribute with this name.
|
||||
|
||||
:return: Queryset object with the additional `as_field` field.
|
||||
"""
|
||||
model = queryset.model
|
||||
sql = """SELECT json_agg(row_to_json(projects_epicstatus))
|
||||
FROM projects_epicstatus
|
||||
WHERE
|
||||
projects_epicstatus.project_id = {tbl}.id
|
||||
"""
|
||||
|
||||
sql = sql.format(tbl=model._meta.db_table)
|
||||
queryset = queryset.extra(select={as_field: sql})
|
||||
return queryset
|
||||
|
||||
|
||||
def attach_userstory_statuses(queryset, as_field="userstory_statuses_attr"):
|
||||
"""Attach a json userstory statuses representation to each object of the queryset.
|
||||
|
||||
|
@ -443,6 +463,7 @@ def attach_extra_info(queryset, user=None):
|
|||
queryset = attach_members(queryset)
|
||||
queryset = attach_closed_milestones(queryset)
|
||||
queryset = attach_notify_policies(queryset)
|
||||
queryset = attach_epic_statuses(queryset)
|
||||
queryset = attach_userstory_statuses(queryset)
|
||||
queryset = attach_points(queryset)
|
||||
queryset = attach_task_statuses(queryset)
|
||||
|
|
|
@ -86,9 +86,9 @@ class TaskStatusExistsValidator:
|
|||
# Custom values for selectors
|
||||
######################################################
|
||||
|
||||
class PointsValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||
class EpicStatusValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||
class Meta:
|
||||
model = models.Points
|
||||
model = models.EpicStatus
|
||||
|
||||
|
||||
class UserStoryStatusValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||
|
@ -96,6 +96,11 @@ class UserStoryStatusValidator(DuplicatedNameInProjectValidator, validators.Mode
|
|||
model = models.UserStoryStatus
|
||||
|
||||
|
||||
class PointsValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||
class Meta:
|
||||
model = models.Points
|
||||
|
||||
|
||||
class TaskStatusValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||
class Meta:
|
||||
model = models.TaskStatus
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.2 on 2016-06-29 14:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0021_auto_20160614_1201'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='role',
|
||||
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_epic', 'View epic'), ('add_epic', 'Add epic'), ('modify_epic', 'Modify epic'), ('comment_epic', 'Comment epic'), ('delete_epic', 'Delete epic'), ('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'),
|
||||
),
|
||||
]
|
|
@ -229,22 +229,17 @@ class StorageEntryFactory(Factory):
|
|||
value = factory.Sequence(lambda n: {"value": "value-{}".format(n)})
|
||||
|
||||
|
||||
class UserStoryStatusFactory(Factory):
|
||||
class EpicFactory(Factory):
|
||||
class Meta:
|
||||
model = "projects.UserStoryStatus"
|
||||
model = "epics.Epic"
|
||||
strategy = factory.CREATE_STRATEGY
|
||||
|
||||
name = factory.Sequence(lambda n: "User Story status {}".format(n))
|
||||
project = factory.SubFactory("tests.factories.ProjectFactory")
|
||||
|
||||
|
||||
class TaskStatusFactory(Factory):
|
||||
class Meta:
|
||||
model = "projects.TaskStatus"
|
||||
strategy = factory.CREATE_STRATEGY
|
||||
|
||||
name = factory.Sequence(lambda n: "Task status {}".format(n))
|
||||
ref = factory.Sequence(lambda n: n)
|
||||
project = factory.SubFactory("tests.factories.ProjectFactory")
|
||||
owner = factory.SubFactory("tests.factories.UserFactory")
|
||||
subject = factory.Sequence(lambda n: "User Story {}".format(n))
|
||||
description = factory.Sequence(lambda n: "User Story {} description".format(n))
|
||||
status = factory.SubFactory("tests.factories.EpicStatusFactory")
|
||||
|
||||
|
||||
class MilestoneFactory(Factory):
|
||||
|
@ -330,6 +325,33 @@ class WikiLinkFactory(Factory):
|
|||
order = factory.Sequence(lambda n: n)
|
||||
|
||||
|
||||
class EpicStatusFactory(Factory):
|
||||
class Meta:
|
||||
model = "projects.EpicStatus"
|
||||
strategy = factory.CREATE_STRATEGY
|
||||
|
||||
name = factory.Sequence(lambda n: "Epic status {}".format(n))
|
||||
project = factory.SubFactory("tests.factories.ProjectFactory")
|
||||
|
||||
|
||||
class UserStoryStatusFactory(Factory):
|
||||
class Meta:
|
||||
model = "projects.UserStoryStatus"
|
||||
strategy = factory.CREATE_STRATEGY
|
||||
|
||||
name = factory.Sequence(lambda n: "User Story status {}".format(n))
|
||||
project = factory.SubFactory("tests.factories.ProjectFactory")
|
||||
|
||||
|
||||
class TaskStatusFactory(Factory):
|
||||
class Meta:
|
||||
model = "projects.TaskStatus"
|
||||
strategy = factory.CREATE_STRATEGY
|
||||
|
||||
name = factory.Sequence(lambda n: "Task status {}".format(n))
|
||||
project = factory.SubFactory("tests.factories.ProjectFactory")
|
||||
|
||||
|
||||
class IssueStatusFactory(Factory):
|
||||
class Meta:
|
||||
model = "projects.IssueStatus"
|
||||
|
|
|
@ -115,6 +115,11 @@ def data():
|
|||
user=m.project_owner,
|
||||
is_admin=True)
|
||||
|
||||
m.public_epic_status = f.EpicStatusFactory(project=m.public_project)
|
||||
m.private_epic_status1 = f.EpicStatusFactory(project=m.private_project1)
|
||||
m.private_epic_status2 = f.EpicStatusFactory(project=m.private_project2)
|
||||
m.blocked_epic_status = f.EpicStatusFactory(project=m.blocked_project)
|
||||
|
||||
m.public_points = f.PointsFactory(project=m.public_project)
|
||||
m.private_points1 = f.PointsFactory(project=m.private_project1)
|
||||
m.private_points2 = f.PointsFactory(project=m.private_project2)
|
||||
|
@ -155,6 +160,10 @@ def data():
|
|||
return m
|
||||
|
||||
|
||||
#####################################################
|
||||
# Roles
|
||||
#####################################################
|
||||
|
||||
def test_roles_retrieve(client, data):
|
||||
public_url = reverse('roles-detail', kwargs={"pk": data.public_project.roles.all()[0].pk})
|
||||
private1_url = reverse('roles-detail', kwargs={"pk": data.private_project1.roles.all()[0].pk})
|
||||
|
@ -299,6 +308,198 @@ def test_roles_patch(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Epic Status
|
||||
#####################################################
|
||||
|
||||
def test_epic_status_retrieve(client, data):
|
||||
public_url = reverse('epic-statuses-detail', kwargs={"pk": data.public_epic_status.pk})
|
||||
private1_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status1.pk})
|
||||
private2_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status2.pk})
|
||||
blocked_url = reverse('epic-statuses-detail', kwargs={"pk": data.blocked_epic_status.pk})
|
||||
|
||||
users = [
|
||||
None,
|
||||
data.registered_user,
|
||||
data.project_member_without_perms,
|
||||
data.project_member_with_perms,
|
||||
data.project_owner
|
||||
]
|
||||
|
||||
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||
assert results == [200, 200, 200, 200, 200]
|
||||
results = helper_test_http_method(client, 'get', private1_url, None, users)
|
||||
assert results == [200, 200, 200, 200, 200]
|
||||
results = helper_test_http_method(client, 'get', private2_url, None, users)
|
||||
assert results == [401, 403, 403, 200, 200]
|
||||
results = helper_test_http_method(client, 'get', blocked_url, None, users)
|
||||
assert results == [401, 403, 403, 200, 200]
|
||||
|
||||
|
||||
def test_epic_status_update(client, data):
|
||||
public_url = reverse('epic-statuses-detail', kwargs={"pk": data.public_epic_status.pk})
|
||||
private1_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status1.pk})
|
||||
private2_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status2.pk})
|
||||
blocked_url = reverse('epic-statuses-detail', kwargs={"pk": data.blocked_epic_status.pk})
|
||||
|
||||
users = [
|
||||
None,
|
||||
data.registered_user,
|
||||
data.project_member_without_perms,
|
||||
data.project_member_with_perms,
|
||||
data.project_owner
|
||||
]
|
||||
|
||||
epic_status_data = serializers.EpicStatusSerializer(data.public_epic_status).data
|
||||
epic_status_data["name"] = "test"
|
||||
epic_status_data = json.dumps(epic_status_data)
|
||||
results = helper_test_http_method(client, 'put', public_url, epic_status_data, users)
|
||||
assert results == [401, 403, 403, 403, 200]
|
||||
|
||||
epic_status_data = serializers.EpicStatusSerializer(data.private_epic_status1).data
|
||||
epic_status_data["name"] = "test"
|
||||
epic_status_data = json.dumps(epic_status_data)
|
||||
results = helper_test_http_method(client, 'put', private1_url, epic_status_data, users)
|
||||
assert results == [401, 403, 403, 403, 200]
|
||||
|
||||
epic_status_data = serializers.EpicStatusSerializer(data.private_epic_status2).data
|
||||
epic_status_data["name"] = "test"
|
||||
epic_status_data = json.dumps(epic_status_data)
|
||||
results = helper_test_http_method(client, 'put', private2_url, epic_status_data, users)
|
||||
assert results == [401, 403, 403, 403, 200]
|
||||
|
||||
epic_status_data = serializers.EpicStatusSerializer(data.blocked_epic_status).data
|
||||
epic_status_data["name"] = "test"
|
||||
epic_status_data = json.dumps(epic_status_data)
|
||||
results = helper_test_http_method(client, 'put', blocked_url, epic_status_data, users)
|
||||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
def test_epic_status_delete(client, data):
|
||||
public_url = reverse('epic-statuses-detail', kwargs={"pk": data.public_epic_status.pk})
|
||||
private1_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status1.pk})
|
||||
private2_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status2.pk})
|
||||
blocked_url = reverse('epic-statuses-detail', kwargs={"pk": data.blocked_epic_status.pk})
|
||||
|
||||
users = [
|
||||
None,
|
||||
data.registered_user,
|
||||
data.project_member_without_perms,
|
||||
data.project_member_with_perms,
|
||||
data.project_owner
|
||||
]
|
||||
|
||||
results = helper_test_http_method(client, 'delete', public_url, None, users)
|
||||
assert results == [401, 403, 403, 403, 204]
|
||||
results = helper_test_http_method(client, 'delete', private1_url, None, users)
|
||||
assert results == [401, 403, 403, 403, 204]
|
||||
results = helper_test_http_method(client, 'delete', private2_url, None, users)
|
||||
assert results == [401, 403, 403, 403, 204]
|
||||
results = helper_test_http_method(client, 'delete', blocked_url, None, users)
|
||||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
def test_epic_status_list(client, data):
|
||||
url = reverse('epic-statuses-list')
|
||||
|
||||
response = client.get(url)
|
||||
projects_data = json.loads(response.content.decode('utf-8'))
|
||||
assert len(projects_data) == 2
|
||||
assert response.status_code == 200
|
||||
|
||||
client.login(data.registered_user)
|
||||
response = client.get(url)
|
||||
projects_data = json.loads(response.content.decode('utf-8'))
|
||||
assert len(projects_data) == 2
|
||||
assert response.status_code == 200
|
||||
|
||||
client.login(data.project_member_without_perms)
|
||||
response = client.get(url)
|
||||
projects_data = json.loads(response.content.decode('utf-8'))
|
||||
assert len(projects_data) == 2
|
||||
assert response.status_code == 200
|
||||
|
||||
client.login(data.project_member_with_perms)
|
||||
response = client.get(url)
|
||||
projects_data = json.loads(response.content.decode('utf-8'))
|
||||
assert len(projects_data) == 4
|
||||
assert response.status_code == 200
|
||||
|
||||
client.login(data.project_owner)
|
||||
response = client.get(url)
|
||||
projects_data = json.loads(response.content.decode('utf-8'))
|
||||
assert len(projects_data) == 4
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_epic_status_patch(client, data):
|
||||
public_url = reverse('epic-statuses-detail', kwargs={"pk": data.public_epic_status.pk})
|
||||
private1_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status1.pk})
|
||||
private2_url = reverse('epic-statuses-detail', kwargs={"pk": data.private_epic_status2.pk})
|
||||
blocked_url = reverse('epic-statuses-detail', kwargs={"pk": data.blocked_epic_status.pk})
|
||||
|
||||
users = [
|
||||
None,
|
||||
data.registered_user,
|
||||
data.project_member_without_perms,
|
||||
data.project_member_with_perms,
|
||||
data.project_owner
|
||||
]
|
||||
|
||||
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
|
||||
assert results == [401, 403, 403, 403, 200]
|
||||
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
|
||||
assert results == [401, 403, 403, 403, 200]
|
||||
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
|
||||
assert results == [401, 403, 403, 403, 200]
|
||||
results = helper_test_http_method(client, 'patch', blocked_url, '{"name": "Test"}', users)
|
||||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
def test_epic_status_action_bulk_update_order(client, data):
|
||||
url = reverse('epic-statuses-bulk-update-order')
|
||||
|
||||
users = [
|
||||
None,
|
||||
data.registered_user,
|
||||
data.project_member_without_perms,
|
||||
data.project_member_with_perms,
|
||||
data.project_owner
|
||||
]
|
||||
|
||||
post_data = json.dumps({
|
||||
"bulk_epic_statuses": [(1, 2)],
|
||||
"project": data.public_project.pk
|
||||
})
|
||||
results = helper_test_http_method(client, 'post', url, post_data, users)
|
||||
assert results == [401, 403, 403, 403, 204]
|
||||
|
||||
post_data = json.dumps({
|
||||
"bulk_epic_statuses": [(1, 2)],
|
||||
"project": data.private_project1.pk
|
||||
})
|
||||
results = helper_test_http_method(client, 'post', url, post_data, users)
|
||||
assert results == [401, 403, 403, 403, 204]
|
||||
|
||||
post_data = json.dumps({
|
||||
"bulk_epic_statuses": [(1, 2)],
|
||||
"project": data.private_project2.pk
|
||||
})
|
||||
results = helper_test_http_method(client, 'post', url, post_data, users)
|
||||
assert results == [401, 403, 403, 403, 204]
|
||||
|
||||
post_data = json.dumps({
|
||||
"bulk_epic_statuses": [(1, 2)],
|
||||
"project": data.blocked_project.pk
|
||||
})
|
||||
results = helper_test_http_method(client, 'post', url, post_data, users)
|
||||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Points
|
||||
#####################################################
|
||||
|
||||
def test_points_retrieve(client, data):
|
||||
public_url = reverse('points-detail', kwargs={"pk": data.public_points.pk})
|
||||
private1_url = reverse('points-detail', kwargs={"pk": data.private_points1.pk})
|
||||
|
@ -483,6 +684,10 @@ def test_points_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# User Story Status
|
||||
#####################################################
|
||||
|
||||
def test_user_story_status_retrieve(client, data):
|
||||
public_url = reverse('userstory-statuses-detail', kwargs={"pk": data.public_user_story_status.pk})
|
||||
private1_url = reverse('userstory-statuses-detail', kwargs={"pk": data.private_user_story_status1.pk})
|
||||
|
@ -570,7 +775,6 @@ def test_user_story_status_delete(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
|
||||
def test_user_story_status_list(client, data):
|
||||
url = reverse('userstory-statuses-list')
|
||||
|
||||
|
@ -668,6 +872,10 @@ def test_user_story_status_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Task Status
|
||||
#####################################################
|
||||
|
||||
def test_task_status_retrieve(client, data):
|
||||
public_url = reverse('task-statuses-detail', kwargs={"pk": data.public_task_status.pk})
|
||||
private1_url = reverse('task-statuses-detail', kwargs={"pk": data.private_task_status1.pk})
|
||||
|
@ -755,7 +963,6 @@ def test_task_status_delete(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
|
||||
def test_task_status_list(client, data):
|
||||
url = reverse('task-statuses-list')
|
||||
|
||||
|
@ -853,6 +1060,10 @@ def test_task_status_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Issue Status
|
||||
#####################################################
|
||||
|
||||
def test_issue_status_retrieve(client, data):
|
||||
public_url = reverse('issue-statuses-detail', kwargs={"pk": data.public_issue_status.pk})
|
||||
private1_url = reverse('issue-statuses-detail', kwargs={"pk": data.private_issue_status1.pk})
|
||||
|
@ -1037,6 +1248,10 @@ def test_issue_status_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Issue Type
|
||||
#####################################################
|
||||
|
||||
def test_issue_type_retrieve(client, data):
|
||||
public_url = reverse('issue-types-detail', kwargs={"pk": data.public_issue_type.pk})
|
||||
private1_url = reverse('issue-types-detail', kwargs={"pk": data.private_issue_type1.pk})
|
||||
|
@ -1221,6 +1436,10 @@ def test_issue_type_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Priority
|
||||
#####################################################
|
||||
|
||||
def test_priority_retrieve(client, data):
|
||||
public_url = reverse('priorities-detail', kwargs={"pk": data.public_priority.pk})
|
||||
private1_url = reverse('priorities-detail', kwargs={"pk": data.private_priority1.pk})
|
||||
|
@ -1283,6 +1502,7 @@ def test_priority_update(client, data):
|
|||
results = helper_test_http_method(client, 'put', blocked_url, priority_data, users)
|
||||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
def test_priority_delete(client, data):
|
||||
public_url = reverse('priorities-detail', kwargs={"pk": data.public_priority.pk})
|
||||
private1_url = reverse('priorities-detail', kwargs={"pk": data.private_priority1.pk})
|
||||
|
@ -1404,6 +1624,10 @@ def test_priority_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Severity
|
||||
#####################################################
|
||||
|
||||
def test_severity_retrieve(client, data):
|
||||
public_url = reverse('severities-detail', kwargs={"pk": data.public_severity.pk})
|
||||
private1_url = reverse('severities-detail', kwargs={"pk": data.private_severity1.pk})
|
||||
|
@ -1588,6 +1812,10 @@ def test_severity_action_bulk_update_order(client, data):
|
|||
assert results == [401, 403, 403, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Memberships
|
||||
#####################################################
|
||||
|
||||
def test_membership_retrieve(client, data):
|
||||
public_url = reverse('memberships-detail', kwargs={"pk": data.public_membership.pk})
|
||||
private1_url = reverse('memberships-detail', kwargs={"pk": data.private_membership1.pk})
|
||||
|
@ -1859,6 +2087,10 @@ def test_membership_action_resend_invitation(client, data):
|
|||
assert results == [404, 404, 404, 403, 451]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Project Templates
|
||||
#####################################################
|
||||
|
||||
def test_project_template_retrieve(client, data):
|
||||
url = reverse('project-templates-detail', kwargs={"pk": data.project_template.pk})
|
||||
|
||||
|
@ -1935,6 +2167,10 @@ def test_project_template_patch(client, data):
|
|||
assert results == [401, 403, 200]
|
||||
|
||||
|
||||
#####################################################
|
||||
# Tags
|
||||
#####################################################
|
||||
|
||||
def test_create_tag(client, data):
|
||||
users = [
|
||||
None,
|
||||
|
|
Loading…
Reference in New Issue