Add task & issues due dates configuration
parent
758109ac22
commit
6a5e870564
|
@ -596,7 +596,7 @@ class UserStoryDueDateViewSet(BlockedByProjectMixin, ModelCrudViewSet):
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
project_id = request.DATA.get("project", 0)
|
project_id = request.DATA.get("project", 0)
|
||||||
with advisory_lock("user-story-duedate-creation-{}".format(project_id)):
|
with advisory_lock("user-story-due-date-creation-{}".format(project_id)):
|
||||||
return super().create(request, *args, **kwargs)
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -622,6 +622,21 @@ class TaskStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||||
return super().create(request, *args, **kwargs)
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDueDateViewSet(BlockedByProjectMixin, ModelCrudViewSet):
|
||||||
|
|
||||||
|
model = models.TaskDueDate
|
||||||
|
serializer_class = serializers.TaskDueDateSerializer
|
||||||
|
validator_class = validators.TaskDueDateValidator
|
||||||
|
permission_classes = (permissions.TaskDueDatePermission,)
|
||||||
|
filter_backends = (filters.CanViewProjectFilterBackend,)
|
||||||
|
filter_fields = ('project',)
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
project_id = request.DATA.get("project", 0)
|
||||||
|
with advisory_lock("task-due-date-creation-{}".format(project_id)):
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SeverityViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
class SeverityViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||||
ModelCrudViewSet, BulkUpdateOrderMixin):
|
ModelCrudViewSet, BulkUpdateOrderMixin):
|
||||||
|
|
||||||
|
@ -707,6 +722,21 @@ class IssueStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||||
return super().create(request, *args, **kwargs)
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class IssueDueDateViewSet(BlockedByProjectMixin, ModelCrudViewSet):
|
||||||
|
|
||||||
|
model = models.IssueDueDate
|
||||||
|
serializer_class = serializers.IssueDueDateSerializer
|
||||||
|
validator_class = validators.IssueDueDateValidator
|
||||||
|
permission_classes = (permissions.IssueDueDatePermission,)
|
||||||
|
filter_backends = (filters.CanViewProjectFilterBackend,)
|
||||||
|
filter_fields = ('project',)
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
project_id = request.DATA.get("project", 0)
|
||||||
|
with advisory_lock("issue-due-date-creation-{}".format(project_id)):
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
## Project Template
|
## Project Template
|
||||||
######################################################
|
######################################################
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2018-06-06 10:34
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('projects', '0060_auto_20180605_1042'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='IssueDueDate',
|
||||||
|
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')),
|
||||||
|
('by_default', models.BooleanField(default=False, verbose_name='by default')),
|
||||||
|
('color', models.CharField(default='#999999', max_length=20, verbose_name='color')),
|
||||||
|
('days_to_due', models.IntegerField(blank=True, default=None, null=True, verbose_name='days to due')),
|
||||||
|
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_duedates', to='projects.Project', verbose_name='project')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'issue due date',
|
||||||
|
'verbose_name_plural': 'issue due dates',
|
||||||
|
'ordering': ['project', 'order', 'name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TaskDueDate',
|
||||||
|
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')),
|
||||||
|
('by_default', models.BooleanField(default=False, verbose_name='by default')),
|
||||||
|
('color', models.CharField(default='#999999', max_length=20, verbose_name='color')),
|
||||||
|
('days_to_due', models.IntegerField(blank=True, default=None, null=True, verbose_name='days to due')),
|
||||||
|
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_duedates', to='projects.Project', verbose_name='project')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'task due date',
|
||||||
|
'verbose_name_plural': 'task due dates',
|
||||||
|
'ordering': ['project', 'order', 'name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='taskduedate',
|
||||||
|
unique_together=set([('project', 'name'), ('project', 'slug')]),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='issueduedate',
|
||||||
|
unique_together=set([('project', 'name'), ('project', 'slug')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -677,6 +677,40 @@ class TaskStatus(models.Model):
|
||||||
return super().save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDueDate(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"))
|
||||||
|
by_default = models.BooleanField(default=False, null=False, blank=True,
|
||||||
|
verbose_name=_("by default"))
|
||||||
|
color = models.CharField(max_length=20, null=False, blank=False, default="#999999",
|
||||||
|
verbose_name=_("color"))
|
||||||
|
days_to_due = models.IntegerField(null=True, blank=True, default=None,
|
||||||
|
verbose_name=_("days to due"))
|
||||||
|
project = models.ForeignKey("Project", null=False, blank=False,
|
||||||
|
related_name="task_duedates", verbose_name=_("project"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "task due date"
|
||||||
|
verbose_name_plural = "task due dates"
|
||||||
|
ordering = ["project", "order", "name"]
|
||||||
|
unique_together = (("project", "name"), ("project", "slug"))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
qs = self.project.task_duedates
|
||||||
|
if self.id:
|
||||||
|
qs = qs.exclude(id=self.id)
|
||||||
|
|
||||||
|
self.slug = slugify_uniquely_for_queryset(self.name, qs)
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Issue common Models
|
# Issue common Models
|
||||||
|
|
||||||
class Priority(models.Model):
|
class Priority(models.Model):
|
||||||
|
@ -771,6 +805,40 @@ class IssueType(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class IssueDueDate(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"))
|
||||||
|
by_default = models.BooleanField(default=False, null=False, blank=True,
|
||||||
|
verbose_name=_("by default"))
|
||||||
|
color = models.CharField(max_length=20, null=False, blank=False, default="#999999",
|
||||||
|
verbose_name=_("color"))
|
||||||
|
days_to_due = models.IntegerField(null=True, blank=True, default=None,
|
||||||
|
verbose_name=_("days to due"))
|
||||||
|
project = models.ForeignKey("Project", null=False, blank=False,
|
||||||
|
related_name="issue_duedates", verbose_name=_("project"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "issue due date"
|
||||||
|
verbose_name_plural = "issue due dates"
|
||||||
|
ordering = ["project", "order", "name"]
|
||||||
|
unique_together = (("project", "name"), ("project", "slug"))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
qs = self.project.issue_duedates
|
||||||
|
if self.id:
|
||||||
|
qs = qs.exclude(id=self.id)
|
||||||
|
|
||||||
|
self.slug = slugify_uniquely_for_queryset(self.name, qs)
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ProjectTemplate(TaggedMixin, TagsColorsMixin, models.Model):
|
class ProjectTemplate(TaggedMixin, TagsColorsMixin, models.Model):
|
||||||
name = models.CharField(max_length=250, null=False, blank=False,
|
name = models.CharField(max_length=250, null=False, blank=False,
|
||||||
verbose_name=_("name"))
|
verbose_name=_("name"))
|
||||||
|
|
|
@ -167,6 +167,15 @@ class TaskStatusPermission(TaigaResourcePermission):
|
||||||
bulk_update_order_perms = IsProjectAdmin()
|
bulk_update_order_perms = IsProjectAdmin()
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDueDatePermission(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()
|
||||||
|
|
||||||
# Issues
|
# Issues
|
||||||
|
|
||||||
class SeverityPermission(TaigaResourcePermission):
|
class SeverityPermission(TaigaResourcePermission):
|
||||||
|
@ -209,6 +218,16 @@ class IssueTypePermission(TaigaResourcePermission):
|
||||||
bulk_update_order_perms = IsProjectAdmin()
|
bulk_update_order_perms = IsProjectAdmin()
|
||||||
|
|
||||||
|
|
||||||
|
class IssueDueDatePermission(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()
|
||||||
|
|
||||||
|
|
||||||
# Project Templates
|
# Project Templates
|
||||||
|
|
||||||
class ProjectTemplatePermission(TaigaResourcePermission):
|
class ProjectTemplatePermission(TaigaResourcePermission):
|
||||||
|
|
|
@ -67,7 +67,7 @@ class PointsSerializer(serializers.LightSerializer):
|
||||||
project = Field(attr="project_id")
|
project = Field(attr="project_id")
|
||||||
|
|
||||||
|
|
||||||
class UserStoryDueDateSerializer(serializers.LightSerializer):
|
class BaseDueDateSerializer(serializers.LightSerializer):
|
||||||
id = Field()
|
id = Field()
|
||||||
name = I18NField()
|
name = I18NField()
|
||||||
slug = Field()
|
slug = Field()
|
||||||
|
@ -78,6 +78,10 @@ class UserStoryDueDateSerializer(serializers.LightSerializer):
|
||||||
project = Field(attr="project_id")
|
project = Field(attr="project_id")
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryDueDateSerializer(BaseDueDateSerializer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TaskStatusSerializer(serializers.LightSerializer):
|
class TaskStatusSerializer(serializers.LightSerializer):
|
||||||
id = Field()
|
id = Field()
|
||||||
name = I18NField()
|
name = I18NField()
|
||||||
|
@ -88,6 +92,10 @@ class TaskStatusSerializer(serializers.LightSerializer):
|
||||||
project = Field(attr="project_id")
|
project = Field(attr="project_id")
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDueDateSerializer(BaseDueDateSerializer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SeveritySerializer(serializers.LightSerializer):
|
class SeveritySerializer(serializers.LightSerializer):
|
||||||
id = Field()
|
id = Field()
|
||||||
name = I18NField()
|
name = I18NField()
|
||||||
|
@ -122,6 +130,10 @@ class IssueTypeSerializer(serializers.LightSerializer):
|
||||||
project = Field(attr="project_id")
|
project = Field(attr="project_id")
|
||||||
|
|
||||||
|
|
||||||
|
class IssueDueDateSerializer(BaseDueDateSerializer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
# Members
|
# Members
|
||||||
######################################################
|
######################################################
|
||||||
|
|
|
@ -94,6 +94,11 @@ class TaskStatusValidator(DuplicatedNameInProjectValidator, validators.ModelVali
|
||||||
model = models.TaskStatus
|
model = models.TaskStatus
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDueDateValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||||
|
class Meta:
|
||||||
|
model = models.TaskDueDate
|
||||||
|
|
||||||
|
|
||||||
class SeverityValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
class SeverityValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Severity
|
model = models.Severity
|
||||||
|
@ -114,6 +119,11 @@ class IssueTypeValidator(DuplicatedNameInProjectValidator, validators.ModelValid
|
||||||
model = models.IssueType
|
model = models.IssueType
|
||||||
|
|
||||||
|
|
||||||
|
class IssueDueDateValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||||
|
class Meta:
|
||||||
|
model = models.IssueDueDate
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
# Members
|
# Members
|
||||||
######################################################
|
######################################################
|
||||||
|
|
|
@ -60,8 +60,10 @@ from taiga.projects.api import UserStoryStatusViewSet
|
||||||
from taiga.projects.api import PointsViewSet
|
from taiga.projects.api import PointsViewSet
|
||||||
from taiga.projects.api import UserStoryDueDateViewSet
|
from taiga.projects.api import UserStoryDueDateViewSet
|
||||||
from taiga.projects.api import TaskStatusViewSet
|
from taiga.projects.api import TaskStatusViewSet
|
||||||
|
from taiga.projects.api import TaskDueDateViewSet
|
||||||
from taiga.projects.api import IssueStatusViewSet
|
from taiga.projects.api import IssueStatusViewSet
|
||||||
from taiga.projects.api import IssueTypeViewSet
|
from taiga.projects.api import IssueTypeViewSet
|
||||||
|
from taiga.projects.api import IssueDueDateViewSet
|
||||||
from taiga.projects.api import PriorityViewSet
|
from taiga.projects.api import PriorityViewSet
|
||||||
from taiga.projects.api import SeverityViewSet
|
from taiga.projects.api import SeverityViewSet
|
||||||
from taiga.projects.api import ProjectTemplateViewSet
|
from taiga.projects.api import ProjectTemplateViewSet
|
||||||
|
@ -77,8 +79,10 @@ router.register(r"userstory-statuses", UserStoryStatusViewSet, base_name="userst
|
||||||
router.register(r"points", PointsViewSet, base_name="points")
|
router.register(r"points", PointsViewSet, base_name="points")
|
||||||
router.register(r"userstory-due-dates", UserStoryDueDateViewSet, base_name="userstory-due-dates")
|
router.register(r"userstory-due-dates", UserStoryDueDateViewSet, base_name="userstory-due-dates")
|
||||||
router.register(r"task-statuses", TaskStatusViewSet, base_name="task-statuses")
|
router.register(r"task-statuses", TaskStatusViewSet, base_name="task-statuses")
|
||||||
|
router.register(r"task-due-dates", TaskDueDateViewSet, base_name="task-due-dates")
|
||||||
router.register(r"issue-statuses", IssueStatusViewSet, base_name="issue-statuses")
|
router.register(r"issue-statuses", IssueStatusViewSet, base_name="issue-statuses")
|
||||||
router.register(r"issue-types", IssueTypeViewSet, base_name="issue-types")
|
router.register(r"issue-types", IssueTypeViewSet, base_name="issue-types")
|
||||||
|
router.register(r"issue-due-dates", IssueDueDateViewSet, base_name="issue-due-dates")
|
||||||
router.register(r"priorities", PriorityViewSet, base_name="priorities")
|
router.register(r"priorities", PriorityViewSet, base_name="priorities")
|
||||||
router.register(r"severities",SeverityViewSet , base_name="severities")
|
router.register(r"severities",SeverityViewSet , base_name="severities")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue