Skip to content

Commit

Permalink
feat(project_management): Add project milestones
Browse files Browse the repository at this point in the history
ref: #285
  • Loading branch information
jon-nfc committed Sep 14, 2024
1 parent a373247 commit 94c5b8e
Show file tree
Hide file tree
Showing 16 changed files with 606 additions and 14 deletions.
12 changes: 12 additions & 0 deletions app/core/forms/ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ def __init__(self, request, *args, **kwargs):
self.fields['ticket_type'].widget = self.fields['ticket_type'].hidden_widget()
self.fields['organization'].widget = self.fields['organization'].hidden_widget()

if self.instance.project is not None:

self.fields['milestone'].queryset = self.fields['milestone'].queryset.filter(
project=self.instance.project
)

else:

self.fields['milestone'].queryset = self.fields['milestone'].queryset.filter(
id=0
)


original_fields = self.fields.copy()
ticket_type = []
Expand Down
2 changes: 2 additions & 0 deletions app/core/forms/validate_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class TicketValidation(
'planned_finish_date',
'priority',
'project',
'milestone',
'real_start_date',
'real_finish_date',
'subscribed_users',
Expand All @@ -67,6 +68,7 @@ class TicketValidation(
'planned_finish_date',
'priority',
'project',
'milestone',
'real_start_date',
'real_finish_date',
'subscribed_users',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Generated by Django 5.0.8 on 2024-09-13 05:06
# Generated by Django 5.0.8 on 2024-09-14 06:29

import access.fields
import access.models
import core.lib.slash_commands
import core.models.ticket.ticket
import core.models.ticket.ticket_comment
import django.db.models.deletion
Expand Down Expand Up @@ -66,6 +67,7 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Comments',
'ordering': ['ticket', 'parent_id'],
},
bases=(core.lib.slash_commands.SlashCommands, models.Model),
),
migrations.CreateModel(
name='TicketCommentCategory',
Expand Down Expand Up @@ -121,14 +123,13 @@ class Migration(migrations.Migration):
('real_finish_date', models.DateTimeField(blank=True, help_text='Real finish date', null=True, verbose_name='Real Finish Date')),
('assigned_teams', models.ManyToManyField(blank=True, help_text='Assign the ticket to a Team(s)', related_name='assigned_teams', to='access.team', verbose_name='Assigned Team(s)')),
('assigned_users', models.ManyToManyField(blank=True, help_text='Assign the ticket to a User(s)', related_name='assigned_users', to=settings.AUTH_USER_MODEL, verbose_name='Assigned User(s)')),
('opened_by', models.ForeignKey(help_text='Who is the ticket for', on_delete=django.db.models.deletion.DO_NOTHING, related_name='opened_by', to=settings.AUTH_USER_MODEL, verbose_name='Opened By')),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
],
options={
'verbose_name': 'Ticket',
'verbose_name_plural': 'Tickets',
'ordering': ['id'],
'permissions': [('add_ticket_request', 'Can add a request ticket'), ('change_ticket_request', 'Can change any request ticket'), ('delete_ticket_request', 'Can delete a request ticket'), ('import_ticket_request', 'Can import a request ticket'), ('purge_ticket_request', 'Can purge a request ticket'), ('triage_ticket_request', 'Can triage all request ticket'), ('view_ticket_request', 'Can view all request ticket'), ('add_ticket_incident', 'Can add a incident ticket'), ('change_ticket_incident', 'Can change any incident ticket'), ('delete_ticket_incident', 'Can delete a incident ticket'), ('import_ticket_incident', 'Can import a incident ticket'), ('purge_ticket_incident', 'Can purge a incident ticket'), ('triage_ticket_incident', 'Can triage all incident ticket'), ('view_ticket_incident', 'Can view all incident ticket'), ('add_ticket_problem', 'Can add a problem ticket'), ('change_ticket_problem', 'Can change any problem ticket'), ('delete_ticket_problem', 'Can delete a problem ticket'), ('import_ticket_problem', 'Can import a problem ticket'), ('purge_ticket_problem', 'Can purge a problem ticket'), ('triage_ticket_problem', 'Can triage all problem ticket'), ('view_ticket_problem', 'Can view all problem ticket'), ('add_ticket_change', 'Can add a change ticket'), ('change_ticket_change', 'Can change any change ticket'), ('delete_ticket_change', 'Can delete a change ticket'), ('import_ticket_change', 'Can import a change ticket'), ('purge_ticket_change', 'Can purge a change ticket'), ('triage_ticket_change', 'Can triage all change ticket'), ('view_ticket_change', 'Can view all change ticket'), ('add_ticket_project_task', 'Can add a project task'), ('change_ticket_project_task', 'Can change any project task'), ('delete_ticket_project_task', 'Can delete a project task'), ('import_ticket_project_task', 'Can import a project task'), ('purge_ticket_project_task', 'Can purge a project task'), ('triage_ticket_project_task', 'Can triage all project task'), ('view_ticket_project_task', 'Can view all project task')],
},
bases=(core.lib.slash_commands.SlashCommands, models.Model),
),
]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.8 on 2024-09-13 05:06
# Generated by Django 5.0.8 on 2024-09-14 06:29

import access.models
import core.models.ticket.ticket_comment
Expand All @@ -18,6 +18,21 @@ class Migration(migrations.Migration):
]

operations = [
migrations.AddField(
model_name='ticket',
name='milestone',
field=models.ForeignKey(blank=True, help_text='Assign to a milestone', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='project_management.projectmilestone', verbose_name='Project Milestone'),
),
migrations.AddField(
model_name='ticket',
name='opened_by',
field=models.ForeignKey(help_text='Who is the ticket for', on_delete=django.db.models.deletion.DO_NOTHING, related_name='opened_by', to=settings.AUTH_USER_MODEL, verbose_name='Opened By'),
),
migrations.AddField(
model_name='ticket',
name='organization',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists]),
),
migrations.AddField(
model_name='ticket',
name='project',
Expand Down
14 changes: 13 additions & 1 deletion app/core/models/ticket/ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from core.middleware.get_request import get_request
from core.models.ticket.ticket_category import TicketCategory

from project_management.models.projects import Project
from project_management.models.project_milestone import Project, ProjectMilestone



Expand Down Expand Up @@ -528,6 +528,15 @@ def validation_title(field):
verbose_name = 'Project',
)

milestone = models.ForeignKey(
ProjectMilestone,
blank= True,
help_text = 'Assign to a milestone',
null = True,
on_delete = models.DO_NOTHING,
verbose_name = 'Project Milestone',
)


opened_by = models.ForeignKey(
User,
Expand Down Expand Up @@ -642,6 +651,7 @@ def __str__(self):
'category'
'urgency',
'project',
'milestone',
'priority',
'impact',
'subscribed_teams',
Expand Down Expand Up @@ -680,6 +690,7 @@ def __str__(self):

fields_project_task: list(str()) = common_fields + [
'category',
'milestone',
'status',
'urgency',
'priority',
Expand All @@ -695,6 +706,7 @@ def __str__(self):
tech_fields = [
'category',
'project',
'milestone',
'assigned_users',
'assigned_teams',
'subscribed_teams',
Expand Down
14 changes: 10 additions & 4 deletions app/core/templates/core/ticket.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,22 @@
</span>
</fieldset>
{% endif %}
{% if ticket.project %}
<fieldset>
<label>Project</label>
<span class="text">
{% if ticket.project %}
<a href="{% url 'Project Management:_project_view' pk=ticket.project_id %}">{{ ticket.project }}</a>
{% else %}
-
{% endif %}
</span>
</fieldset>
{% endif %}
{% if ticket.milestone %}
<fieldset>
<label>Milestone</label>
<span class="text">
<a href="{% url 'Project Management:_project_milestone_view' project_id=ticket.project_id pk=ticket.milestone.id %}">{{ ticket.milestone }}</a>
</span>
</fieldset>
{% endif %}
<fieldset>
<label>Priority</label>
<span class="text">U{{ ticket.get_urgency_display }} / I{{ ticket.get_impact_display }} / P{{ ticket.get_priority_display }}</span>
Expand Down
36 changes: 35 additions & 1 deletion app/core/tests/unit/ticket/test_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,38 @@ def test_ticket_create_add_opened_by_as_watcher_api(self):
as a subscribed user.
"""

pass
pass


@pytest.mark.skip(reason='test to be written')
def test_field_milestone_no_project(self):
"""Field Value Test
Ensure that a milestone can't be applied if no project
has been selected
"""

pass


@pytest.mark.skip(reason='test to be written')
def test_field_milestone_has_project(self):
"""Field Value Test
Ensure that a milestone can be applied if a project
has been selected
"""

pass


@pytest.mark.skip(reason='test to be written')
def test_field_milestone_different_project(self):
"""Field Value Test
Ensure that a milestone from a different project
can't be applied
"""

pass

5 changes: 5 additions & 0 deletions app/project_management/forms/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ class DetailForm(ProjectForm):
"slug": "tasks",
"sections": []
},
"milestones": {
"name": "Milestones",
"slug": "milestones",
"sections": []
},
"notes": {
"name": "Notes",
"slug": "notes",
Expand Down
109 changes: 109 additions & 0 deletions app/project_management/forms/project_milestone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from django import forms
from django.urls import reverse
from django.db.models import Q

from app import settings

from core.forms.common import CommonModelForm
from core.templatetags.markdown import to_duration

from project_management.models.project_milestone import ProjectMilestone



class ProjectMilestoneForm(CommonModelForm):

prefix = 'project'

class Meta:
fields = [
'id',
'organization',
'name',
'description',
'project',
'start_date',
'finish_date',
]


model = ProjectMilestone


def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields['start_date'].widget = forms.widgets.DateTimeInput(attrs={'type': 'datetime-local', 'format': "%Y-%m-%dT%H:%M"})
self.fields['start_date'].input_formats = settings.DATETIME_FORMAT
self.fields['start_date'].format="%Y-%m-%dT%H:%M"

self.fields['finish_date'].widget = forms.widgets.DateTimeInput(attrs={'type': 'datetime-local'})
self.fields['finish_date'].input_formats = settings.DATETIME_FORMAT
self.fields['finish_date'].format="%Y-%m-%dT%H:%M"

self.fields['description'].widget.attrs = {'style': "height: 800px; width: 1000px"}

self.fields['project'].widget = self.fields['project'].hidden_widget()


class DetailForm(ProjectMilestoneForm):


tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'percent_completed',
'organization'
'c_created',
'c_modified',
],
"right": [
'start_date',
'finish_date',
]
},
{
"layout": "single",
"name": "Description",
"fields": [
'description',
],
"markdown": [
'description',
],
},
]
},
"tasks": {
"name": "Tasks",
"slug": "tasks",
"sections": []
},
}


def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)

self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)

self.url_index_view = reverse('Project Management:_project_view', kwargs={'pk': self.instance.project.id}) + '?tab=milestones'
23 changes: 22 additions & 1 deletion app/project_management/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.8 on 2024-09-13 05:06
# Generated by Django 5.0.8 on 2024-09-14 06:29

import access.fields
import access.models
Expand Down Expand Up @@ -44,4 +44,25 @@ class Migration(migrations.Migration):
'ordering': ['code', 'name'],
},
),
migrations.CreateModel(
name='ProjectMilestone',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('name', models.CharField(max_length=50, unique=True)),
('slug', access.fields.AutoSlugField()),
('description', models.TextField(blank=True, default=None, null=True)),
('start_date', models.DateTimeField(blank=True, help_text='When work commenced on the project.', null=True, verbose_name='Real Start Date')),
('finish_date', models.DateTimeField(blank=True, help_text='When work was completed for the project', null=True, verbose_name='Real Finish Date')),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='project_management.project')),
],
options={
'verbose_name': 'Project Milestone',
'verbose_name_plural': 'Project Milestones',
'ordering': ['name'],
},
),
]
Loading

0 comments on commit 94c5b8e

Please sign in to comment.