Add US duedates configuration
parent
4dd525972b
commit
758109ac22
|
@ -585,6 +585,21 @@ class PointsViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||||
return super().create(request, *args, **kwargs)
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryDueDateViewSet(BlockedByProjectMixin, ModelCrudViewSet):
|
||||||
|
|
||||||
|
model = models.UserStoryDueDate
|
||||||
|
serializer_class = serializers.UserStoryDueDateSerializer
|
||||||
|
validator_class = validators.UserStoryDueDateValidator
|
||||||
|
permission_classes = (permissions.UserStoryDueDatePermission,)
|
||||||
|
filter_backends = (filters.CanViewProjectFilterBackend,)
|
||||||
|
filter_fields = ('project',)
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
project_id = request.DATA.get("project", 0)
|
||||||
|
with advisory_lock("user-story-duedate-creation-{}".format(project_id)):
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class TaskStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
class TaskStatusViewSet(MoveOnDestroyMixin, BlockedByProjectMixin,
|
||||||
ModelCrudViewSet, BulkUpdateOrderMixin):
|
ModelCrudViewSet, BulkUpdateOrderMixin):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2018-06-05 10:42
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('projects', '0059_auto_20170116_1633'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserStoryDueDate',
|
||||||
|
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='us_duedates', to='projects.Project', verbose_name='project')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user story due date',
|
||||||
|
'verbose_name_plural': 'user story due dates',
|
||||||
|
'ordering': ['project', 'order', 'name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='project',
|
||||||
|
name='default_us_duedate',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='projects.UserStoryDueDate', verbose_name='default US duedate'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='userstoryduedate',
|
||||||
|
unique_together=set([('project', 'slug'), ('project', 'name')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -120,6 +120,11 @@ class ProjectDefaults(models.Model):
|
||||||
default_points = models.OneToOneField("projects.Points", on_delete=models.SET_NULL,
|
default_points = models.OneToOneField("projects.Points", on_delete=models.SET_NULL,
|
||||||
related_name="+", null=True, blank=True,
|
related_name="+", null=True, blank=True,
|
||||||
verbose_name=_("default points"))
|
verbose_name=_("default points"))
|
||||||
|
default_us_duedate = models.OneToOneField("projects.UserStoryDueDate",
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="+",
|
||||||
|
null=True, blank=True,
|
||||||
|
verbose_name=_("default US duedate"))
|
||||||
default_task_status = models.OneToOneField("projects.TaskStatus",
|
default_task_status = models.OneToOneField("projects.TaskStatus",
|
||||||
on_delete=models.SET_NULL, related_name="+",
|
on_delete=models.SET_NULL, related_name="+",
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
|
@ -605,8 +610,41 @@ class Points(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
# Tasks common models
|
class UserStoryDueDate(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="us_duedates", verbose_name=_("project"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "user story due date"
|
||||||
|
verbose_name_plural = "user story 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.us_duedates
|
||||||
|
if self.id:
|
||||||
|
qs = qs.exclude(id=self.id)
|
||||||
|
|
||||||
|
self.slug = slugify_uniquely_for_queryset(self.name, qs)
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# Tasks common models
|
||||||
class TaskStatus(models.Model):
|
class TaskStatus(models.Model):
|
||||||
name = models.CharField(max_length=255, null=False, blank=False,
|
name = models.CharField(max_length=255, null=False, blank=False,
|
||||||
verbose_name=_("name"))
|
verbose_name=_("name"))
|
||||||
|
|
|
@ -145,6 +145,16 @@ class UserStoryStatusPermission(TaigaResourcePermission):
|
||||||
bulk_update_order_perms = IsProjectAdmin()
|
bulk_update_order_perms = IsProjectAdmin()
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryDueDatePermission(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()
|
||||||
|
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
|
|
||||||
class TaskStatusPermission(TaigaResourcePermission):
|
class TaskStatusPermission(TaigaResourcePermission):
|
||||||
|
|
|
@ -67,6 +67,17 @@ class PointsSerializer(serializers.LightSerializer):
|
||||||
project = Field(attr="project_id")
|
project = Field(attr="project_id")
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryDueDateSerializer(serializers.LightSerializer):
|
||||||
|
id = Field()
|
||||||
|
name = I18NField()
|
||||||
|
slug = Field()
|
||||||
|
order = Field()
|
||||||
|
by_default = Field()
|
||||||
|
days_to_due = Field()
|
||||||
|
color = Field()
|
||||||
|
project = Field(attr="project_id")
|
||||||
|
|
||||||
|
|
||||||
class TaskStatusSerializer(serializers.LightSerializer):
|
class TaskStatusSerializer(serializers.LightSerializer):
|
||||||
id = Field()
|
id = Field()
|
||||||
name = I18NField()
|
name = I18NField()
|
||||||
|
|
|
@ -84,6 +84,11 @@ class PointsValidator(DuplicatedNameInProjectValidator, validators.ModelValidato
|
||||||
model = models.Points
|
model = models.Points
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryDueDateValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||||
|
class Meta:
|
||||||
|
model = models.UserStoryDueDate
|
||||||
|
|
||||||
|
|
||||||
class TaskStatusValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
class TaskStatusValidator(DuplicatedNameInProjectValidator, validators.ModelValidator):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.TaskStatus
|
model = models.TaskStatus
|
||||||
|
|
|
@ -58,6 +58,7 @@ from taiga.projects.api import InvitationViewSet
|
||||||
from taiga.projects.api import EpicStatusViewSet
|
from taiga.projects.api import EpicStatusViewSet
|
||||||
from taiga.projects.api import UserStoryStatusViewSet
|
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 TaskStatusViewSet
|
from taiga.projects.api import TaskStatusViewSet
|
||||||
from taiga.projects.api import IssueStatusViewSet
|
from taiga.projects.api import IssueStatusViewSet
|
||||||
from taiga.projects.api import IssueTypeViewSet
|
from taiga.projects.api import IssueTypeViewSet
|
||||||
|
@ -74,6 +75,7 @@ router.register(r"invitations", InvitationViewSet, base_name="invitations")
|
||||||
router.register(r"epic-statuses", EpicStatusViewSet, base_name="epic-statuses")
|
router.register(r"epic-statuses", EpicStatusViewSet, base_name="epic-statuses")
|
||||||
router.register(r"userstory-statuses", UserStoryStatusViewSet, base_name="userstory-statuses")
|
router.register(r"userstory-statuses", UserStoryStatusViewSet, base_name="userstory-statuses")
|
||||||
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"task-statuses", TaskStatusViewSet, base_name="task-statuses")
|
router.register(r"task-statuses", TaskStatusViewSet, base_name="task-statuses")
|
||||||
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")
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2018-06-05 10:31
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0026_auto_20180514_1513'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='read_new_terms',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='new terms read'),
|
||||||
|
),
|
||||||
|
]
|
Loading…
Reference in New Issue