diff --git a/backend/plugins/taiga/tasks/user_story_convertor.go b/backend/plugins/taiga/tasks/user_story_convertor.go
index dc4e15a9771..963a25ae18e 100644
--- a/backend/plugins/taiga/tasks/user_story_convertor.go
+++ b/backend/plugins/taiga/tasks/user_story_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
package tasks
import (
+ "fmt"
+
"github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -71,22 +73,41 @@ func ConvertUserStories(subtaskCtx plugin.SubTaskContext) errors.Error {
Convert: func(userStory *models.TaigaUserStory) ([]interface{}, errors.Error) {
var result []interface{}
+ // Map Taiga is_closed to DevLake standard status
+ status := "TODO"
+ if userStory.IsClosed {
+ status = "DONE"
+ }
+
issue := &ticket.Issue{
DomainEntity: domainlayer.DomainEntity{
Id: issueIdGen.Generate(userStory.ConnectionId, userStory.UserStoryId),
},
- IssueKey: userStory.Subject,
+ IssueKey: fmt.Sprintf("#%d", userStory.Ref),
Title: userStory.Subject,
Type: "USER_STORY",
OriginalType: "User Story",
- Status: userStory.Status,
+ Status: status,
OriginalStatus: userStory.Status,
+ CreatedDate: userStory.CreatedDate,
+ UpdatedDate: userStory.ModifiedDate,
+ ResolutionDate: userStory.FinishedDate,
+ AssigneeId: fmt.Sprintf("%d", userStory.AssignedTo),
+ AssigneeName: userStory.AssignedToName,
}
if userStory.TotalPoints > 0 {
issue.StoryPoint = &userStory.TotalPoints
}
+ // Calculate lead time: creation → resolution for closed stories
+ if userStory.IsClosed && issue.CreatedDate != nil && issue.ResolutionDate != nil {
+ leadTimeMinutes := uint(issue.ResolutionDate.Sub(*issue.CreatedDate).Minutes())
+ if leadTimeMinutes > 0 {
+ issue.LeadTimeMinutes = &leadTimeMinutes
+ }
+ }
+
result = append(result, issue)
boardIssue := &ticket.BoardIssue{
diff --git a/backend/plugins/taiga/tasks/user_story_extractor.go b/backend/plugins/taiga/tasks/user_story_extractor.go
index 355330486de..642981e2076 100644
--- a/backend/plugins/taiga/tasks/user_story_extractor.go
+++ b/backend/plugins/taiga/tasks/user_story_extractor.go
@@ -19,6 +19,7 @@ package tasks
import (
"encoding/json"
+ "time"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/plugin"
@@ -56,14 +57,20 @@ func ExtractUserStories(taskCtx plugin.SubTaskContext) errors.Error {
StatusExtraInfo struct {
Name string `json:"name"`
} `json:"status_extra_info"`
- CreatedDate string `json:"created_date"`
- ModifiedDate string `json:"modified_date"`
- FinishDate *string `json:"finish_date"`
- AssignedTo *uint64 `json:"assigned_to"`
- TotalPoints *float64 `json:"total_points"`
- MilestoneId *uint64 `json:"milestone"`
- Priority *int `json:"priority"`
- IsBlocked bool `json:"is_blocked"`
+ IsClosed bool `json:"is_closed"`
+ CreatedDate *time.Time `json:"created_date"`
+ ModifiedDate *time.Time `json:"modified_date"`
+ FinishDate *time.Time `json:"finish_date"`
+ AssignedTo *uint64 `json:"assigned_to"`
+ AssignedToExtraInfo *struct {
+ FullNameDisplay string `json:"full_name_display"`
+ } `json:"assigned_to_extra_info"`
+ TotalPoints *float64 `json:"total_points"`
+ MilestoneId *uint64 `json:"milestone"`
+ MilestoneName *string `json:"milestone_name"`
+ Priority *int `json:"priority"`
+ IsBlocked bool `json:"is_blocked"`
+ BlockedNote string `json:"blocked_note"`
}
err := json.Unmarshal(row.Data, &apiUserStory)
if err != nil {
@@ -71,9 +78,13 @@ func ExtractUserStories(taskCtx plugin.SubTaskContext) errors.Error {
}
var assignedTo uint64
+ var assignedToName string
if apiUserStory.AssignedTo != nil {
assignedTo = *apiUserStory.AssignedTo
}
+ if apiUserStory.AssignedToExtraInfo != nil {
+ assignedToName = apiUserStory.AssignedToExtraInfo.FullNameDisplay
+ }
var totalPoints float64
if apiUserStory.TotalPoints != nil {
totalPoints = *apiUserStory.TotalPoints
@@ -82,23 +93,34 @@ func ExtractUserStories(taskCtx plugin.SubTaskContext) errors.Error {
if apiUserStory.MilestoneId != nil {
milestoneId = *apiUserStory.MilestoneId
}
+ var milestoneName string
+ if apiUserStory.MilestoneName != nil {
+ milestoneName = *apiUserStory.MilestoneName
+ }
var priority int
if apiUserStory.Priority != nil {
priority = *apiUserStory.Priority
}
userStory := &models.TaigaUserStory{
- ConnectionId: data.Options.ConnectionId,
- ProjectId: data.Options.ProjectId,
- UserStoryId: apiUserStory.Id,
- Ref: apiUserStory.Ref,
- Subject: apiUserStory.Subject,
- Status: apiUserStory.StatusExtraInfo.Name,
- AssignedTo: assignedTo,
- TotalPoints: totalPoints,
- MilestoneId: milestoneId,
- Priority: priority,
- IsBlocked: apiUserStory.IsBlocked,
+ ConnectionId: data.Options.ConnectionId,
+ ProjectId: data.Options.ProjectId,
+ UserStoryId: apiUserStory.Id,
+ Ref: apiUserStory.Ref,
+ Subject: apiUserStory.Subject,
+ Status: apiUserStory.StatusExtraInfo.Name,
+ IsClosed: apiUserStory.IsClosed,
+ CreatedDate: apiUserStory.CreatedDate,
+ ModifiedDate: apiUserStory.ModifiedDate,
+ FinishedDate: apiUserStory.FinishDate,
+ AssignedTo: assignedTo,
+ AssignedToName: assignedToName,
+ TotalPoints: totalPoints,
+ MilestoneId: milestoneId,
+ MilestoneName: milestoneName,
+ Priority: priority,
+ IsBlocked: apiUserStory.IsBlocked,
+ BlockedNote: apiUserStory.BlockedNote,
}
return []interface{}{userStory}, nil
diff --git a/grafana/dashboards/DeveloperTelemetry.json b/grafana/dashboards/DeveloperTelemetry.json
new file mode 100644
index 00000000000..ddb9bcb0cc2
--- /dev/null
+++ b/grafana/dashboards/DeveloperTelemetry.json
@@ -0,0 +1,1116 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": null,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "time_series",
+ "rawQuery": true,
+ "rawSql": "SELECT\n date as time,\n developer_id as metric,\n active_hours as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nORDER BY date",
+ "refId": "A",
+ "sql": {
+ "columns": [
+ {
+ "parameters": [],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ }
+ }
+ ],
+ "title": "Active Hours by Developer",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "time_series",
+ "rawQuery": true,
+ "rawSql": "SELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\n AND JSON_EXTRACT(git_activity, '$.total_commits') IS NOT NULL\nORDER BY time_sec",
+ "refId": "A"
+ }
+ ],
+ "title": "Total Commits by Developer",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "yellow",
+ "value": 500
+ },
+ {
+ "color": "red",
+ "value": 1000
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 8
+ },
+ "id": 3,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.97,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": false
+ },
+ "orientation": "horizontal",
+ "showValue": "always",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n developer_id,\n CAST(SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) AS UNSIGNED) as lines_added\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY developer_id\nORDER BY lines_added DESC",
+ "refId": "A"
+ }
+ ],
+ "title": "Total Lines Added by Developer",
+ "type": "barchart"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "yellow",
+ "value": 100
+ },
+ {
+ "color": "red",
+ "value": 200
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 8
+ },
+ "id": 4,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.97,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": false
+ },
+ "orientation": "horizontal",
+ "showValue": "always",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 0
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n developer_id,\n CAST(SUM(active_hours) AS UNSIGNED) as hours\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY developer_id\nORDER BY hours DESC\nLIMIT 15",
+ "refId": "A"
+ }
+ ],
+ "title": "Total Active Hours by Developer",
+ "type": "barchart"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 16
+ },
+ "id": 5,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.97,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 0
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n DAYNAME(date) as day_name,\n AVG(active_hours) as avg_hours\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY DAYOFWEEK(date), DAYNAME(date)\nORDER BY DAYOFWEEK(date)",
+ "refId": "A"
+ }
+ ],
+ "title": "Average Active Hours by Day of Week",
+ "type": "barchart"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-GrYlRd"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 16
+ },
+ "id": 6,
+ "options": {
+ "legend": {
+ "calcs": ["mean", "max"],
+ "displayMode": "table",
+ "placement": "right",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "time_series",
+ "rawQuery": true,
+ "rawSql": "WITH top_devs AS (\n SELECT developer_id\n FROM _tool_developer_metrics\n WHERE connection_id = 2 AND $__timeFilter(date)\n GROUP BY developer_id\n ORDER BY SUM(CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED)) DESC\n LIMIT 10\n)\nSELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\n AND developer_id IN (SELECT developer_id FROM top_devs)\nORDER BY time_sec",
+ "refId": "A"
+ }
+ ],
+ "title": "Daily Commits - Top 10 Developers",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "bars",
+ "fillOpacity": 100,
+ "gradientMode": "none",
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "lines_added"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "green",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "lines_deleted"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "red",
+ "mode": "fixed"
+ }
+ },
+ {
+ "id": "custom.transform",
+ "value": "negative-Y"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 24
+ },
+ "id": 7,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "time_series",
+ "rawQuery": true,
+ "rawSql": "SELECT\n date as time,\n 'lines_added' as metric,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY date\nUNION ALL\nSELECT\n date as time,\n 'lines_deleted' as metric,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_deleted')) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY date\nORDER BY time",
+ "refId": "A"
+ }
+ ],
+ "title": "Code Churn (Lines Added vs Deleted)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 24
+ },
+ "id": 8,
+ "options": {
+ "legend": {
+ "calcs": ["mean"],
+ "displayMode": "table",
+ "placement": "right",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "time_series",
+ "rawQuery": true,
+ "rawSql": "SELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(development_activity, '$.test_runs_detected') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\n AND JSON_EXTRACT(development_activity, '$.test_runs_detected') > 0\nORDER BY time_sec",
+ "refId": "A"
+ }
+ ],
+ "title": "Test Runs Detected",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "align": "auto",
+ "cellOptions": {
+ "type": "auto"
+ },
+ "inspect": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "total_commits"
+ },
+ "properties": [
+ {
+ "id": "custom.cellOptions",
+ "value": {
+ "type": "color-background"
+ }
+ },
+ {
+ "id": "color",
+ "value": {
+ "mode": "continuous-GrYlRd"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "avg_active_hours"
+ },
+ "properties": [
+ {
+ "id": "custom.cellOptions",
+ "value": {
+ "type": "gauge"
+ }
+ },
+ {
+ "id": "max",
+ "value": 10
+ },
+ {
+ "id": "min",
+ "value": 0
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 32
+ },
+ "id": 9,
+ "options": {
+ "cellHeight": "sm",
+ "footer": {
+ "countRows": false,
+ "fields": "",
+ "reducer": ["sum"],
+ "show": false
+ },
+ "showHeader": true,
+ "sortBy": [
+ {
+ "desc": true,
+ "displayName": "total_commits"
+ }
+ ]
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n developer_id,\n name,\n email,\n SUM(JSON_EXTRACT(git_activity, '$.total_commits')) as total_commits,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as total_lines_added,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_deleted')) as total_lines_deleted,\n AVG(active_hours) as avg_active_hours,\n SUM(active_hours) as total_active_hours\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY developer_id, name, email\nORDER BY total_commits DESC",
+ "refId": "A"
+ }
+ ],
+ "title": "Developer Productivity Summary",
+ "type": "table"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 0,
+ "y": 40
+ },
+ "id": 10,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "values": false,
+ "calcs": ["sum"],
+ "fields": ""
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n SUM(JSON_EXTRACT(git_activity, '$.total_commits')) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
+ "refId": "A"
+ }
+ ],
+ "title": "Total Commits",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 6,
+ "y": 40
+ },
+ "id": 11,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "values": false,
+ "calcs": ["sum"],
+ "fields": ""
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n SUM(active_hours) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
+ "refId": "A"
+ }
+ ],
+ "title": "Total Active Hours",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 12,
+ "y": 40
+ },
+ "id": 12,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "values": false,
+ "calcs": ["sum"],
+ "fields": ""
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
+ "refId": "A"
+ }
+ ],
+ "title": "Total Lines Added",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 18,
+ "y": 40
+ },
+ "id": 13,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "values": false,
+ "calcs": ["lastNotNull"],
+ "fields": ""
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "mysql",
+ "uid": "mysql"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT\n COUNT(DISTINCT developer_id) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
+ "refId": "A"
+ }
+ ],
+ "title": "Active Developers",
+ "type": "stat"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": ["developer", "telemetry", "productivity"],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6w",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Developer Telemetry",
+ "uid": "developer-telemetry",
+ "version": 0,
+ "weekStart": ""
+}
diff --git a/grafana/dashboards/Taiga.json b/grafana/dashboards/Taiga.json
new file mode 100644
index 00000000000..1e43d38e540
--- /dev/null
+++ b/grafana/dashboards/Taiga.json
@@ -0,0 +1,2105 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": null,
+ "links": [
+ {
+ "asDropdown": false,
+ "icon": "bolt",
+ "includeVars": false,
+ "keepTime": true,
+ "tags": [],
+ "targetBlank": false,
+ "title": "Homepage",
+ "tooltip": "",
+ "type": "link",
+ "url": "/grafana/d/Lv1XbLHnk/data-specific-dashboards-homepage"
+ },
+ {
+ "asDropdown": false,
+ "icon": "external link",
+ "includeVars": false,
+ "keepTime": true,
+ "tags": [
+ "Data Source Specific Dashboard"
+ ],
+ "targetBlank": false,
+ "title": "Metric dashboards",
+ "tooltip": "",
+ "type": "dashboards",
+ "url": ""
+ }
+ ],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 13,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Taiga Plugin",
+ "url": "https://github.com/apache/incubator-devlake"
+ }
+ ],
+ "options": {
+ "code": {
+ "language": "plaintext",
+ "showLineNumbers": false,
+ "showMiniMap": false
+ },
+ "content": "- Use Cases: This dashboard shows the basic project management metrics from Taiga.\n- Data Source Required: Taiga\n- Metrics: User Story throughput, lead time, story points, milestone progress and assignee breakdown.",
+ "mode": "markdown"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "queryType": "randomWalk",
+ "refId": "A"
+ }
+ ],
+ "title": "Dashboard Introduction",
+ "type": "text"
+ },
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 3
+ },
+ "id": 100,
+ "targets": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "1. User Story Throughput",
+ "type": "row"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Total number of user stories created in the selected time range and board.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 0,
+ "y": 4
+ },
+ "id": 2,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Count",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementCount"
+ }
+ ],
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "editorMode": "code",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "select\r\n count(distinct i.id) as value\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "sql": {
+ "columns": [
+ {
+ "parameters": [],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ },
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Number of User Stories [Created in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Total number of user stories that have been marked as Done/Closed in the selected time range.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 4,
+ "y": 4
+ },
+ "id": 3,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Count",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementCount"
+ }
+ ],
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "editorMode": "code",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "select\r\n count(distinct i.id) as value\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "sql": {
+ "columns": [
+ {
+ "parameters": [],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ },
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Number of Closed Stories [Created in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Monthly distribution of open vs. closed user stories.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 1,
+ "drawStyle": "bars",
+ "fillOpacity": 12,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 4
+ },
+ "id": 4,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Count",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementCount"
+ }
+ ],
+ "options": {
+ "legend": {
+ "calcs": [
+ "sum"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "SELECT\r\n DATE_ADD(date(i.created_date), INTERVAL -DAYOFMONTH(date(i.created_date))+1 DAY) as time,\r\n count(distinct case when status != 'DONE' then i.id else null end) as \"Number of Open Stories\",\r\n count(distinct case when status = 'DONE' then i.id else null end) as \"Number of Closed Stories\"\r\nFROM issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})\r\ngroup by 1",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Story Status Distribution over Month [Created in Selected Time Range]",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Story Delivery Rate = count(Closed Stories) / count(All Stories)",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "percentage",
+ "steps": [
+ {
+ "color": "red",
+ "value": null
+ },
+ {
+ "color": "green",
+ "value": 50
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 10
+ },
+ "id": 5,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Delivery Rate",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementDeliveryRate"
+ }
+ ],
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "with _requirements as(\r\n select\r\n count(distinct i.id) as total_count,\r\n count(distinct case when i.status = 'DONE' then i.id else null end) as delivered_count\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})\r\n)\r\n\r\nselect\r\n now() as time,\r\n 1.0 * delivered_count/total_count as requirement_delivery_rate\r\nfrom _requirements",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Story Delivery Rate [Created in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Monthly trend of story delivery rate.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "Delivery Rate(%)",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 12,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 10
+ },
+ "id": 6,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Delivery Rate",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementDeliveryRate"
+ }
+ ],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "with _requirements as(\r\n select\r\n DATE_ADD(date(i.created_date), INTERVAL -DAYOFMONTH(date(i.created_date))+1 DAY) as time,\r\n 1.0 * count(distinct case when i.status = 'DONE' then i.id else null end)/count(distinct i.id) as delivered_rate\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})\r\n group by 1\r\n)\r\n\r\nselect\r\n time,\r\n delivered_rate\r\nfrom _requirements\r\norder by time",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Story Delivery Rate over Time [Created in Selected Time Range]",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 16
+ },
+ "id": 101,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "2. Issue Lead Time",
+ "type": "row"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Average number of days from story creation to resolution.",
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 1,
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 14
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 0,
+ "y": 17
+ },
+ "id": 7,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Lead Time",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementLeadTime"
+ }
+ ],
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "mean"
+ ],
+ "fields": "/^value$/",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n avg(lead_time_minutes/1440) as value\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in (${board_id})",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Mean Lead Time in Days [Resolved in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "80% of stories are resolved within this many days.",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 21
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 4,
+ "y": 17
+ },
+ "id": 8,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Lead Time",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementLeadTime"
+ }
+ ],
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "with _ranks as(\r\n select\r\n i.lead_time_minutes,\r\n percent_rank() over (order by lead_time_minutes asc) as ranks\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where\r\n i.type in (${type})\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in (${board_id})\r\n)\r\n\r\nselect\r\n max(lead_time_minutes/1440) as value\r\nfrom _ranks\r\nwhere\r\n ranks <= 0.8",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "80% Stories' Lead Time ≤ # Days [Resolved in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Mean lead time per month for resolved stories.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "Lead Time (days)",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 17
+ },
+ "id": 9,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Lead Time",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementLeadTime"
+ }
+ ],
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.5,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "with _requirements as(\r\n select\r\n DATE_ADD(date(i.resolution_date), INTERVAL -DAYOFMONTH(date(i.resolution_date))+1 DAY) as time,\r\n avg(lead_time_minutes/1440) as mean_lead_time\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where\r\n i.type in (${type})\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in (${board_id})\r\n group by 1\r\n)\r\n\r\nselect\r\n date_format(time,'%M %Y') as month,\r\n mean_lead_time\r\nfrom _requirements\r\norder by time asc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Mean Lead Time by Month [Resolved in Selected Time Range]",
+ "type": "barchart"
+ },
+ {
+ "datasource": "mysql",
+ "description": "The cumulative distribution of story lead time. Each point shows the percent rank of a given lead time value.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 24,
+ "x": 0,
+ "y": 23
+ },
+ "id": 10,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "Requirement Lead Time",
+ "url": "https://devlake.apache.org/docs/Metrics/RequirementLeadTime"
+ }
+ ],
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.51,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 100
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "editorMode": "code",
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "with _ranks as(\r\n select\r\n round(i.lead_time_minutes/1440) as lead_time_day\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where\r\n i.type in (${type})\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in (${board_id})\r\n order by lead_time_day asc\r\n)\r\n\r\nselect\r\n now() as time,\r\n lpad(concat(lead_time_day,'d'), 4, ' ') as metric,\r\n percent_rank() over (order by lead_time_day asc) as value\r\nfrom _ranks\r\norder by lead_time_day asc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "sql": {
+ "columns": [
+ {
+ "parameters": [],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ },
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Cumulative Distribution of Lead Time [Resolved in Selected Time Range]",
+ "transformations": [
+ {
+ "id": "reduce",
+ "options": {
+ "reducers": [
+ "current"
+ ]
+ }
+ }
+ ],
+ "type": "barchart"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 29
+ },
+ "id": 102,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "3. Story Points",
+ "type": "row"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Total story points for all user stories in the selected time range.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "blue",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 0,
+ "y": 30
+ },
+ "id": 11,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "editorMode": "code",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n sum(ts.total_points) as value\r\nfrom _tool_taiga_user_stories ts\r\n join board_issues bi on bi.issue_id = concat('taiga:TaigaUserStory:', ts.connection_id, ':', ts.user_story_id)\r\nwhere\r\n bi.board_id in (${board_id})",
+ "refId": "A",
+ "sql": {
+ "columns": [
+ {
+ "parameters": [],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ },
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Total Story Points",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Total story points for closed/completed user stories.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 4,
+ "y": 30
+ },
+ "id": 12,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "editorMode": "code",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n sum(ts.total_points) as value\r\nfrom _tool_taiga_user_stories ts\r\n join board_issues bi on bi.issue_id = concat('taiga:TaigaUserStory:', ts.connection_id, ':', ts.user_story_id)\r\n join issues i on i.id = bi.issue_id\r\nwhere\r\n bi.board_id in (${board_id})\r\n and i.status = 'DONE'",
+ "refId": "A",
+ "sql": {
+ "columns": [
+ {
+ "parameters": [],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ },
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Completed Story Points",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Story points allocated to each milestone/sprint. Requires milestone data from Taiga.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "Story Points",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 30
+ },
+ "id": 13,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.6,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": -15,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n COALESCE(NULLIF(ts.milestone_name,''), 'No Milestone') as Milestone,\r\n sum(ts.total_points) as `Story Points`\r\nfrom _tool_taiga_user_stories ts\r\n join board_issues bi on bi.issue_id = concat('taiga:TaigaUserStory:', ts.connection_id, ':', ts.user_story_id)\r\nwhere\r\n bi.board_id in (${board_id})\r\ngroup by 1\r\norder by `Story Points` desc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Story Points by Milestone",
+ "type": "barchart"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Story points distributed across assignees.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "Story Points",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 12,
+ "x": 0,
+ "y": 36
+ },
+ "id": 14,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.6,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": -15,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n COALESCE(NULLIF(i.assignee_name,''), 'Unassigned') as Assignee,\r\n sum(i.story_point) as `Story Points`\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})\r\ngroup by 1\r\norder by `Story Points` desc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Story Points by Assignee [Created in Selected Time Range]",
+ "type": "barchart"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Distribution of story points across open vs. closed stories.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 12,
+ "x": 12,
+ "y": 36
+ },
+ "id": 15,
+ "options": {
+ "displayLabels": [
+ "name",
+ "value"
+ ],
+ "legend": {
+ "displayMode": "list",
+ "placement": "right",
+ "showLegend": true
+ },
+ "pieType": "pie",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n sum(ts.total_points) as `Story Points`,\r\n CASE WHEN i.status = 'DONE' THEN 'Completed' ELSE 'In Progress' END as Status\r\nfrom _tool_taiga_user_stories ts\r\n join board_issues bi on bi.issue_id = concat('taiga:TaigaUserStory:', ts.connection_id, ':', ts.user_story_id)\r\n join issues i on i.id = bi.issue_id\r\nwhere\r\n bi.board_id in (${board_id})\r\ngroup by Status",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Story Points: Completed vs In Progress",
+ "type": "piechart"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 42
+ },
+ "id": 103,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "4. User Stories Overview",
+ "type": "row"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Breakdown of user stories by their current status in Taiga.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 0,
+ "y": 43
+ },
+ "id": 16,
+ "options": {
+ "displayLabels": [
+ "name",
+ "value"
+ ],
+ "legend": {
+ "displayMode": "list",
+ "placement": "right",
+ "showLegend": true
+ },
+ "pieType": "donut",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n COALESCE(NULLIF(i.original_status,''), i.status) as Status,\r\n count(distinct i.id) as `User Stories`\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})\r\ngroup by 1\r\norder by 2 desc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "User Stories by Status [Created in Selected Time Range]",
+ "type": "piechart"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Number of user stories assigned to each team member.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "User Stories",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 8,
+ "y": 43
+ },
+ "id": 17,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.6,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "horizontal",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n COALESCE(NULLIF(i.assignee_name,''), 'Unassigned') as Assignee,\r\n count(distinct i.id) as `User Stories`\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere\r\n i.type in (${type})\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in (${board_id})\r\ngroup by 1\r\norder by `User Stories` desc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "User Stories by Assignee [Created in Selected Time Range]",
+ "type": "barchart"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Number of user stories in each milestone/sprint.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "User Stories",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 16,
+ "y": 43
+ },
+ "id": 18,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.6,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "horizontal",
+ "showValue": "auto",
+ "stacking": "none",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ },
+ "xTickLabelRotation": 0,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n COALESCE(NULLIF(ts.milestone_name,''), 'No Milestone') as Milestone,\r\n count(distinct ts.user_story_id) as `User Stories`\r\nfrom _tool_taiga_user_stories ts\r\n join board_issues bi on bi.issue_id = concat('taiga:TaigaUserStory:', ts.connection_id, ':', ts.user_story_id)\r\nwhere\r\n bi.board_id in (${board_id})\r\ngroup by 1\r\norder by `User Stories` desc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "User Stories per Milestone",
+ "type": "barchart"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Milestone progress: open vs. closed user stories per sprint.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 51
+ },
+ "id": 19,
+ "options": {
+ "barRadius": 0,
+ "barWidth": 0.6,
+ "fullHighlight": false,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "stacking": "normal",
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ },
+ "xTickLabelRotation": -15,
+ "xTickLabelSpacing": 0
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": "mysql",
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select\r\n COALESCE(NULLIF(ts.milestone_name,''), 'No Milestone') as Milestone,\r\n count(distinct case when i.status != 'DONE' then ts.user_story_id else null end) as `Open Stories`,\r\n count(distinct case when i.status = 'DONE' then ts.user_story_id else null end) as `Closed Stories`\r\nfrom _tool_taiga_user_stories ts\r\n join board_issues bi on bi.issue_id = concat('taiga:TaigaUserStory:', ts.connection_id, ':', ts.user_story_id)\r\n join issues i on i.id = bi.issue_id\r\nwhere\r\n bi.board_id in (${board_id})\r\ngroup by 1\r\norder by `Closed Stories` desc",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Milestone Progress: Open vs. Closed Stories",
+ "type": "barchart"
+ },
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 2,
+ "w": 24,
+ "x": 0,
+ "y": 58
+ },
+ "id": 130,
+ "options": {
+ "code": {
+ "language": "plaintext",
+ "showLineNumbers": false,
+ "showMiniMap": false
+ },
+ "content": "
\n\nThis dashboard is created based on this [data schema](https://devlake.apache.org/docs/DataModels/DevLakeDomainLayerSchema). Want to add more metrics? Please follow the [guide](https://devlake.apache.org/docs/Configuration/Dashboards/GrafanaUserGuide).",
+ "mode": "markdown"
+ },
+ "pluginVersion": "9.5.15",
+ "targets": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "queryType": "randomWalk",
+ "refId": "A"
+ }
+ ],
+ "type": "text"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [
+ "Data Source Dashboard"
+ ],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": true,
+ "text": [
+ "All"
+ ],
+ "value": [
+ "$__all"
+ ]
+ },
+ "datasource": "mysql",
+ "definition": "select concat(name, '--', id) from boards where id like 'taiga%'",
+ "hide": 0,
+ "includeAll": true,
+ "label": "Choose Project",
+ "multi": true,
+ "name": "board_id",
+ "options": [],
+ "query": "select concat(name, '--', id) from boards where id like 'taiga%'",
+ "refresh": 1,
+ "regex": "/^(?.*)--(?.*)$/",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ },
+ {
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": "mysql",
+ "definition": "select distinct i.type from issues i join board_issues bi on i.id = bi.issue_id where bi.board_id like 'taiga%'",
+ "hide": 0,
+ "includeAll": true,
+ "label": "Issue Type",
+ "multi": false,
+ "name": "type",
+ "options": [],
+ "query": "select distinct i.type from issues i join board_issues bi on i.id = bi.issue_id where bi.board_id like 'taiga%'",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-6M",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "utc",
+ "title": "Taiga",
+ "uid": "taiga-dashboard",
+ "version": 1,
+ "weekStart": ""
+}