Skip to content

Commit

Permalink
feat(sprints): add gantt chart
Browse files Browse the repository at this point in the history
  • Loading branch information
pablolmedorado committed Oct 13, 2020
1 parent 6587727 commit 53ebd21
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 5 deletions.
15 changes: 13 additions & 2 deletions backend/scrum/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from django.db.models import Count, F, Q, QuerySet, Sum
from django.utils.translation import ugettext_lazy as _

from .models import Effort, Progress, UserStory
from .models import Effort, Progress, Sprint, UserStory


def burn_chart_data(instance: UserStory) -> Dict:
def burn_chart_data(instance: Sprint) -> Dict:
sprint_dates = [
instance.start_date + timedelta(days=num_of_days)
for num_of_days in range((instance.end_date - instance.start_date).days + 1)
Expand Down Expand Up @@ -81,6 +81,17 @@ def burn_chart_data(instance: UserStory) -> Dict:
return {"total_effort": total_effort, "data": chart_data}


def gantt_chart_data(instance: Sprint) -> Dict:
return {
"name": instance.name,
"start_date": instance.start_date,
"end_date": instance.end_date,
"user_stories": instance.user_stories.values(
"name", "start_date", "end_date", "current_progress", "validated", "risk_level"
).order_by("start_date"),
}


def user_story_user_chart_data(queryset: QuerySet) -> Dict:
user_list = (
get_user_model()
Expand Down
7 changes: 7 additions & 0 deletions backend/scrum/views_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
burn_chart_data,
effort_role_timeline_chart_data,
effort_user_timeline_chart_data,
gantt_chart_data,
user_story_user_chart_data,
)
from common.mixins import AuthorshipMixin, OrderedMixin
Expand Down Expand Up @@ -69,6 +70,12 @@ def burn_chart(self, request, *args, **kwargs):
chart_data = burn_chart_data(instance)
return Response(chart_data)

@action(detail=True, methods=["GET"])
def gantt_chart(self, request, *args, **kwargs):
instance = self.get_object()
chart_data = gantt_chart_data(instance)
return Response(chart_data)


class EpicApi(AuthorshipMixin, FlexFieldsModelViewSet):
permission_classes = (permissions.IsAuthenticated, IsAdminUserOrReadOnly)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/BaseChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
:update-args="[true, true, true]"
:options="chartOptions"
/>
<v-card v-else :height="height" flat>
<v-card v-else :height="height || 400" flat>
<v-overlay absolute>
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
Expand Down
78 changes: 78 additions & 0 deletions frontend/src/components/scrum/charts/SprintGanttChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script>
import { mapGetters } from "vuex";
import { get } from "lodash";
import colors from "vuetify/lib/util/colors";
import SprintService from "@/services/scrum/sprint-service";
import BaseChart from "@/components/common/BaseChart";
import { truncate } from "@/filters";
export default {
name: "SprintGanttChart",
extends: BaseChart,
props: {
sprintId: {
type: String,
required: true
},
height: {
type: Number,
default: null
}
},
data() {
return {
constructorType: "ganttChart",
service: SprintService,
fetchFunctionName: "ganttChartData"
};
},
computed: {
...mapGetters("scrum", ["riskLevelsMap"]),
localChartOptions() {
const userStories = get(this.chartData, "user_stories", []);
return {
title: {
text: this.chartData.name
},
legend: { enabled: false },
xAxis: {
min: new Date(this.chartData.start_date).getTime(),
max: new Date(this.chartData.end_date).getTime()
},
series: [
{
name: this.chartData.name,
data: userStories.map(userStory => ({
name: truncate(userStory.name),
start: new Date(userStory.start_date).getTime(),
end: new Date(userStory.end_date).getTime(),
completed: userStory.current_progress / 100,
color: this.getUserStoryColour(userStory)
}))
}
]
};
}
},
methods: {
buildFetchFunctionArgs() {
return [this.sprintId];
},
getUserStoryColour(userStory) {
if (userStory.risk_level !== 0) {
return this.riskLevelsMap[userStory.risk_level].colour;
}
if (userStory.validated) {
return colors.green.base;
}
if (!userStory.current_progress) {
return colors.grey.base;
}
return colors.blue.base;
}
}
};
</script>
2 changes: 2 additions & 0 deletions frontend/src/plugins/highcharts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import Vue from "vue";
import Highcharts from "highcharts";
import moreInit from "highcharts/highcharts-more";
import stockInit from "highcharts/modules/stock";
import ganttInit from "highcharts/modules/gantt";
import exportingInit from "highcharts/modules/exporting";
import HighchartsVue from "highcharts-vue";

moreInit(Highcharts);
stockInit(Highcharts);
ganttInit(Highcharts);
exportingInit(Highcharts);

Vue.use(HighchartsVue);
2 changes: 1 addition & 1 deletion frontend/src/plugins/reverse.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions frontend/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import EventDetail from "@/views/calendar/EventDetail";
import Sprints from "@/views/scrum/sprints/Sprints";
import SprintKanban from "@/views/scrum/sprints/SprintKanban";
import SprintChart from "@/views/scrum/sprints/SprintChart";
import SprintGantt from "@/views/scrum/sprints/SprintGantt";
import UserStories from "@/views/scrum/user-stories/UserStories";
import UserStoryDetail from "@/views/scrum/user-stories/UserStoryDetail";
import Epics from "@/views/scrum/Epics";
Expand Down Expand Up @@ -107,6 +108,16 @@ const router = new Router({
},
props: true
},
{
path: "/scrum/sprints/:sprintId/gantt",
name: "sprint-gantt",
component: SprintGantt,
meta: {
title: "Sprint - Gantt",
keepAlive: true
},
props: true
},
{
path: "/scrum/sprints/:sprintId/user-stories",
name: "sprint-user-stories",
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/services/scrum/sprint-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ export default class SprintService extends BaseService {
const url = Urls[`${this.baseUrlName}-burn-chart`]({ pk });
return Api.get(url);
}

static ganttChartData(pk) {
const url = Urls[`${this.baseUrlName}-gantt-chart`]({ pk });
return Api.get(url);
}
}
12 changes: 11 additions & 1 deletion frontend/src/views/scrum/sprints/SprintChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
Kanban
</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
<v-btn icon :to="{ name: 'sprint-gantt', params: { sprintId } }" v-bind="attrs" v-on="on">
<v-icon>mdi-chart-timeline</v-icon>
</v-btn>
</template>
<span>
Diagrama de Gantt
</span>
</v-tooltip>
<v-divider vertical inset></v-divider>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
Expand All @@ -32,7 +42,7 @@
</v-btn>
</v-toolbar>
<v-card-text>
<SprintBurnChart ref="chart" :sprint-id="sprintId" :burn-up="burnUp" />
<SprintBurnChart ref="chart" :sprint-id="sprintId" :burn-up="burnUp" :reactive-filters="false" />
</v-card-text>
</v-card>
</v-container>
Expand Down
75 changes: 75 additions & 0 deletions frontend/src/views/scrum/sprints/SprintGantt.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<template>
<v-container fluid>
<ContextBreadcrumbs :items="breadcrumbs" />
<v-card class="mt-2">
<v-toolbar flat>
<v-toolbar-title class="text-h6"> Diagrama de Gantt </v-toolbar-title>
<v-spacer></v-spacer>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
<v-btn icon :to="{ name: 'sprint-kanban', params: { sprintId } }" v-bind="attrs" v-on="on">
<v-icon>mdi-teach</v-icon>
</v-btn>
</template>
<span>
Kanban
</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
<v-btn icon :to="{ name: 'sprint-chart', params: { sprintId } }" v-bind="attrs" v-on="on">
<v-icon>mdi-fire</v-icon>
</v-btn>
</template>
<span>
Diagrama de quemado (Burn-down/up)
</span>
</v-tooltip>
<v-divider vertical inset></v-divider>
<v-btn icon :disabled="loading" @click="getChartData">
<v-icon>mdi-refresh</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<SprintGanttChart ref="chart" :sprint-id="sprintId" :reactive-filters="false" />
</v-card-text>
</v-card>
</v-container>
</template>

<script>
import { mapGetters } from "vuex";
import BreadcrumbsContextMixin from "@/mixins/scrum/breadcrumbs-context-mixin";
import SprintGanttChart from "@/components/scrum/charts/SprintGanttChart";
export default {
name: "SprintGantt",
components: { SprintGanttChart },
mixins: [BreadcrumbsContextMixin],
computed: {
...mapGetters(["loading"]),
breadcrumbs() {
if (this.contextItem) {
return [
{
text: "Sprints",
to: { name: "sprints" },
exact: true
},
{ text: this.contextItem.name, disabled: false, link: false },
{ text: "Diagrama de Gantt", disabled: true }
];
} else {
return [];
}
}
},
methods: {
getChartData() {
this.$refs.chart.getChartData();
}
}
};
</script>
10 changes: 10 additions & 0 deletions frontend/src/views/scrum/sprints/SprintKanban.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
Diagrama de quemado (Burn-down/up)
</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
<v-btn icon :to="{ name: 'sprint-gantt', params: { sprintId } }" v-bind="attrs" v-on="on">
<v-icon>mdi-chart-timeline</v-icon>
</v-btn>
</template>
<span>
Diagrama de Gantt
</span>
</v-tooltip>
<v-divider vertical inset></v-divider>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/views/scrum/sprints/Sprints.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@
Diagrama de quemado (Burn-down/up)
</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on, attrs }">
<v-btn icon :to="{ name: 'sprint-gantt', params: { sprintId: item.id } }" v-bind="attrs" v-on="on">
<v-icon>mdi-chart-timeline</v-icon>
</v-btn>
</template>
<span>
Diagrama de Gantt
</span>
</v-tooltip>
</template>
</ItemIndex>
</v-col>
Expand Down

0 comments on commit 53ebd21

Please sign in to comment.