@beauraines/sprint-tracker 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +16 -0
- package/.github/dependabot.yml +21 -0
- package/CHANGELOG.md +52 -0
- package/R/HeloSprintBurndown.R +138 -0
- package/R/ProjectHealth.R +109 -0
- package/R/backlogByTeam.R +52 -0
- package/R/backlogHealth.R +163 -0
- package/R/burndDownChart.R +144 -0
- package/R/carryOverAdjustedVelocity.R +154 -0
- package/R/featureTeamBacklog.R +73 -0
- package/R/projectHealth.Rmd +111 -0
- package/R/sprintOutcomes.R +142 -0
- package/R/sprintOutcomesCli.R +103 -0
- package/R/sprintOutcomesFeatureTeam.R +143 -0
- package/R/timeInCodeReview.R +157 -0
- package/README.md +181 -0
- package/cli.js +55 -0
- package/cmds/addOutcomes.js +98 -0
- package/cmds/addProject.js +53 -0
- package/cmds/addSprint.js +117 -0
- package/cmds/config.js +33 -0
- package/cmds/getSprintDetails.js +86 -0
- package/cmds/visualizations.js +116 -0
- package/docker-compose.yml +11 -0
- package/migrations/20230222011333-create-health-table.sql +10 -0
- package/migrations/20230222011334-outcomes.sql +42 -0
- package/migrations/20231206093700-seed-outcomes-table.sql +9 -0
- package/migrations.js +20 -0
- package/package.json +50 -0
- package/scripts/generateBacklogByTeam.sh +20 -0
- package/scripts/generateBacklogHealth.sh +12 -0
- package/scripts/generateChart.sh +7 -0
- package/scripts/generateFeatureTeamBacklog.sh +16 -0
- package/scripts/generateSprintOutcomePlot.sh +12 -0
- package/scripts/sprintOutcomes.sh +29 -0
- package/sql/bugsOpenedDuringSprint.sql +14 -0
- package/sql/bugsOpenedDuringSprintFeature.sql +14 -0
- package/sql/capacityAdjustedVelocity.sql +48 -0
- package/sql/carryOver.sql +33 -0
- package/sql/commitment.sql +30 -0
- package/sql/commitmentMet.sql +60 -0
- package/sql/completed.sql +40 -0
- package/sql/descoped.sql +21 -0
- package/sql/pullForward.sql +19 -0
- package/sql/pullForwardFeature.sql +10 -0
- package/sql/sprintOutcomes.sql +25 -0
- package/sql/sprint_outcomes.sql +23 -0
- package/src/addOutcomes.js +94 -0
- package/src/addSprint.js +108 -0
- package/src/getSprintDetails.js +88 -0
- package/utils/display.js +68 -0
- package/utils/input.js +24 -0
- package/utils/readConfig.js +74 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "npm" # See documentation for possible values
|
|
9
|
+
directory: "/" # Location of package manifests
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
12
|
+
day: "friday"
|
|
13
|
+
commit-message:
|
|
14
|
+
prefix: fix
|
|
15
|
+
prefix-development: chore
|
|
16
|
+
include: scope
|
|
17
|
+
groups:
|
|
18
|
+
dev-dependencies:
|
|
19
|
+
dependency-type: "development"
|
|
20
|
+
patterns:
|
|
21
|
+
- "*"
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
|
+
|
|
5
|
+
### [0.6.1](https://bitbucket.org/beauraines/sprint-tracker/compare/v0.6.0...v0.6.1) (2023-03-25)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* adds cli command to create tracker database ([d17d671](https://bitbucket.org/beauraines/sprint-tracker/commit/d17d6715ac2206476adf15c754069fd4c606c113))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* adds await to config reader ([4e7d3d1](https://bitbucket.org/beauraines/sprint-tracker/commit/4e7d3d1c6bd703257f958e3fdb17929ac579fcc7))
|
|
16
|
+
* **migrations:** allows to run on pre-existing database ([d0bef24](https://bitbucket.org/beauraines/sprint-tracker/commit/d0bef242ee3890d2a14cc933564c329ece231a2d))
|
|
17
|
+
* updates database migration commands ([f0a2307](https://bitbucket.org/beauraines/sprint-tracker/commit/f0a230735e836b54362f01123a914ce9047c2c98))
|
|
18
|
+
* updates script name ([a93c724](https://bitbucket.org/beauraines/sprint-tracker/commit/a93c7247d7483b8f87a378dd75aee198c1f18563))
|
|
19
|
+
|
|
20
|
+
## [0.6.0](https://bitbucket.org/beauraines/sprint-tracker/compare/v0.5.2...v0.6.0) (2023-03-25)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### ⚠ BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
* removes `recordSprintOutcomes` command
|
|
26
|
+
* now a configuration file is required `~/sprintTracker.json` with the full path to the database
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
{
|
|
30
|
+
"database":"/Users/beauraines/projects/sprint-tracker/tracker.db"
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Features
|
|
35
|
+
|
|
36
|
+
* adds command line support and sub commands ([0c897e1](https://bitbucket.org/beauraines/sprint-tracker/commit/0c897e1986a02281863dfbfd65cf0615d05874f3))
|
|
37
|
+
* adds sprintTracker command to global install ([54256cc](https://bitbucket.org/beauraines/sprint-tracker/commit/54256cc3be8ca1323a40c8a79b2483d09add8026))
|
|
38
|
+
* database is specified in config ([c33b916](https://bitbucket.org/beauraines/sprint-tracker/commit/c33b9169b87302b80aed3753b846b347db9f1f1c))
|
|
39
|
+
|
|
40
|
+
### [0.5.2](https://bitbucket.org/beauraines/sprint-tracker/compare/v0.5.1...v0.5.2) (2023-03-08)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Features
|
|
44
|
+
|
|
45
|
+
* adds script to generate outcome plot ([8d53548](https://bitbucket.org/beauraines/sprint-tracker/commit/8d535488eee52087b736bfbdf020d57aa43cbc37))
|
|
46
|
+
|
|
47
|
+
### 0.5.1 (2023-03-06)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
* update dependencies ([394cfa3](https://bitbucket.org/beauraines/sprint-tracker/commit/394cfa38c025265d3c925bca6785c754a3e56f2a))
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
library(ggplot2)
|
|
2
|
+
library(tidyverse)
|
|
3
|
+
library(lubridate)
|
|
4
|
+
library(DBI)
|
|
5
|
+
library(RSQLite)
|
|
6
|
+
con <- dbConnect(RSQLite::SQLite(),"~/BurnDownStatus.db")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
res <- dbSendQuery(con, "select
|
|
12
|
+
p.project_key,
|
|
13
|
+
p.name project,
|
|
14
|
+
ifnull(e.epic_name,b.epic) team,
|
|
15
|
+
b.date,
|
|
16
|
+
b.issue_status,
|
|
17
|
+
sum(b.issue_count) issue_count,
|
|
18
|
+
sum(b.estimate) estimate
|
|
19
|
+
from burndown b
|
|
20
|
+
LEFT join epics e on e.epic_key = b.epic
|
|
21
|
+
LEFT join projects p on b.project = p.project_key
|
|
22
|
+
WHERE b.project = 'HELO'
|
|
23
|
+
and b.date >= '2023-05-16'
|
|
24
|
+
group by
|
|
25
|
+
p.project_key,
|
|
26
|
+
p.name,
|
|
27
|
+
b.epic,
|
|
28
|
+
b.date,
|
|
29
|
+
b.issue_status
|
|
30
|
+
order by date desc;")
|
|
31
|
+
|
|
32
|
+
queryResponse = dbFetch(res)
|
|
33
|
+
|
|
34
|
+
burndownData = queryResponse %>%
|
|
35
|
+
tibble() %>%
|
|
36
|
+
mutate(date = as_datetime(date)) %>%
|
|
37
|
+
rename(issues = issue_count, Status = issue_status ) %>%
|
|
38
|
+
filter(!Status %in% c("Duplicate","Wont Do","As Designed","Cant Repro")) %>%
|
|
39
|
+
filter(!team %in% c('Data Science','Product')) %>%
|
|
40
|
+
# Transform other statuses
|
|
41
|
+
mutate( Status =
|
|
42
|
+
case_when(
|
|
43
|
+
Status == "Defining" ~ "Backlog",
|
|
44
|
+
Status == "Triaged" ~ "Backlog",
|
|
45
|
+
Status == "New" ~ "Backlog",
|
|
46
|
+
Status == "Team Review" ~ "Active",
|
|
47
|
+
Status == "In QA" ~ "QA Review",
|
|
48
|
+
Status == "In UAT" ~ "QA Review",
|
|
49
|
+
TRUE ~ as.character(Status))) %>%
|
|
50
|
+
relocate(date) %>%
|
|
51
|
+
arrange(date,team) %>%
|
|
52
|
+
group_by(date,project,project_key,team,Status) %>%
|
|
53
|
+
summarize(issues = sum(issues),estimate = sum(estimate)) %>%
|
|
54
|
+
ungroup()
|
|
55
|
+
|
|
56
|
+
burndownData = burndownData %>%
|
|
57
|
+
## all dates need to have values, missing values to 0
|
|
58
|
+
pivot_wider(names_from=Status, values_from = issues, values_fill = 0, id_cols = !estimate) %>%
|
|
59
|
+
pivot_longer(cols = !c(date,project,project_key,team), names_to = "Status", values_to = "issues")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Clear the result
|
|
63
|
+
dbClearResult(res)
|
|
64
|
+
|
|
65
|
+
# Disconnect from the database
|
|
66
|
+
dbDisconnect(con)
|
|
67
|
+
|
|
68
|
+
lapply(sort(unique(burndownData$team)), function(Team) {
|
|
69
|
+
|
|
70
|
+
# Team = 'Frontend'
|
|
71
|
+
|
|
72
|
+
burndown = burndownData %>%
|
|
73
|
+
# filter(Status != 'Closed') %>%
|
|
74
|
+
group_by(date,project,project_key,team) %>%
|
|
75
|
+
summarize(
|
|
76
|
+
closed_issues = sum(issues[Status != 'Closed']),
|
|
77
|
+
qa_issues = sum(issues[!Status %in% c('Closed',"QA Review")])
|
|
78
|
+
) %>%
|
|
79
|
+
filter(team == Team)
|
|
80
|
+
|
|
81
|
+
plotTitle = str_c(unique(burndown$team),"Sprint Burndown",sep=" ")
|
|
82
|
+
print(str_c("Plotting",plotTitle, sep=" "))
|
|
83
|
+
|
|
84
|
+
sprintStart = floor_date(min(burndown$date),"day")
|
|
85
|
+
startingIssueCount = burndown %>% filter(date == min(burndown$date)) %>% pull(closed_issues)
|
|
86
|
+
|
|
87
|
+
sprintEnd=sprintStart + days(12) # Days after first sprint snapshot. This should change in the long run
|
|
88
|
+
sprintDuration = as.integer(sprintEnd) - as.integer(sprintStart)
|
|
89
|
+
slope = -1 * startingIssueCount/sprintDuration
|
|
90
|
+
intercept = startingIssueCount - slope*as.integer(sprintStart)
|
|
91
|
+
|
|
92
|
+
burndown %>%
|
|
93
|
+
ggplot(aes(x=date)) +
|
|
94
|
+
geom_line(aes(y=closed_issues,color="red")) +
|
|
95
|
+
geom_line(aes(y=qa_issues,color="blue")) +
|
|
96
|
+
geom_abline(intercept = intercept, slope = slope, color = "black") +
|
|
97
|
+
scale_x_datetime(date_labels = "%d-%b",date_breaks = "1 day") +
|
|
98
|
+
labs(title = plotTitle,
|
|
99
|
+
subtitle = "red = issues closed, blue = issues to QA",
|
|
100
|
+
x = "Date",
|
|
101
|
+
y = "Issue Count",
|
|
102
|
+
alt = paste("Team burn down",
|
|
103
|
+
"displaying the idealized burndown",
|
|
104
|
+
sep = " ")) +
|
|
105
|
+
expand_limits(x=c(sprintStart,sprintEnd),y=0) +
|
|
106
|
+
theme(
|
|
107
|
+
plot.title = element_text(family = "roboto"),
|
|
108
|
+
axis.title.x = element_text(family = "roboto"),
|
|
109
|
+
axis.title.y = element_text(family = "roboto"),
|
|
110
|
+
panel.grid.major.y = element_line(colour = "#D9D9D9"), panel.grid.minor.y = element_line(colour = "#D9D9D9"),
|
|
111
|
+
panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(),
|
|
112
|
+
# background color
|
|
113
|
+
panel.background = element_rect(fill = "#D9D9D920", colour = NA),
|
|
114
|
+
legend.position = "bottom",
|
|
115
|
+
plot.background = element_rect(colour = "#D9D9D9", fill=NA)
|
|
116
|
+
) +
|
|
117
|
+
scale_y_continuous(breaks = scales::pretty_breaks())
|
|
118
|
+
|
|
119
|
+
print(str_c("Saving",plotTitle,sep = " "))
|
|
120
|
+
ggsave(str_c("~/projects/sprint-tracker/","Helo-Sprint-Burndown-",Team,".png"),bg="white", width = 2882, height = 1700, units = "px")
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## This tries to facet_wrap the plots but it's not working
|
|
125
|
+
# burndownData %>%
|
|
126
|
+
# filter(Status != 'Closed') %>%
|
|
127
|
+
# group_by(date,project,project_key,team) %>%
|
|
128
|
+
# summarize(issues = sum(issues),estimate = sum(estimate)) %>%
|
|
129
|
+
# ggplot(aes(x=date)) +
|
|
130
|
+
# geom_line(aes(y=issues,color=team)) +
|
|
131
|
+
# geom_abline(intercept = burndownData %>% filter(date == sprintStart) %>% pull(issues) - slope*as.integer(min(burndownData$date)),
|
|
132
|
+
# slope = slope,
|
|
133
|
+
# color = "black") +
|
|
134
|
+
# scale_x_date(date_labels = "%d-%b",date_breaks = "1 day") +
|
|
135
|
+
# expand_limits(x=c(min(burndownData$date),min(burndownData$date) + 12),y=0) +
|
|
136
|
+
# facet_wrap(~team,ncol = 2)
|
|
137
|
+
|
|
138
|
+
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
library(ggplot2)
|
|
2
|
+
library(tidyverse)
|
|
3
|
+
|
|
4
|
+
library(DBI)
|
|
5
|
+
library(RSQLite)
|
|
6
|
+
con <- dbConnect(RSQLite::SQLite(), "~/projects/sprint-tracker/tracker.db")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
res <- dbSendQuery(con, "select *
|
|
12
|
+
from health
|
|
13
|
+
where
|
|
14
|
+
project = 'ALPR Data Enhancements'
|
|
15
|
+
-- project = 'ACDC'
|
|
16
|
+
Order by date desc;")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
project_health = dbFetch(res) %>%
|
|
20
|
+
tibble() %>%
|
|
21
|
+
mutate(date = as.Date(date,"%Y-%m-%d"),
|
|
22
|
+
closed_count = total_count - open_count,
|
|
23
|
+
closed_percentage = (total_count - open_count) / total_count,
|
|
24
|
+
unestimated_percentage = unestimated_count /open_count,
|
|
25
|
+
# aim_unestimated_percentage = (unestimated_count -26) /(open_count -26)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
project_health = project_health%>%
|
|
29
|
+
pivot_longer(cols=contains(c("count","percentage")),names_to = "measure",values_to = "count")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Clear the result
|
|
33
|
+
dbClearResult(res)
|
|
34
|
+
|
|
35
|
+
# Disconnect from the database
|
|
36
|
+
dbDisconnect(con)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## Issue Counts
|
|
40
|
+
|
|
41
|
+
plotTitle = paste(project_health%>%pull(project)%>%unique(),"Issue Counts")
|
|
42
|
+
issuePlot = project_health %>%
|
|
43
|
+
filter(!measure %in% c("unestimated_percentage","closed_percentage","blocked_count","bug_count")) %>%
|
|
44
|
+
ggplot() +
|
|
45
|
+
geom_line(aes(x=date,y=count,group=measure,color=measure)) +
|
|
46
|
+
# geom_line(data=project_health,aes(x=date,y=count)) +
|
|
47
|
+
geom_hline(yintercept = 26) + # Issues to roll over to CDCE project = ACDC and labels in (move-to-cdce) ORDER BY cf[12111] ASC
|
|
48
|
+
labs(title = plotTitle,
|
|
49
|
+
x = "Date",
|
|
50
|
+
y = "Issue Count",
|
|
51
|
+
caption = str_wrap("The horizontal line indicates the number of issues that will be rolled over to the CDCE team on 10 March at the end of the AIM SOW",width=100),
|
|
52
|
+
alt = "") +
|
|
53
|
+
# scale_colour_manual(values = statusColors,guide_legend(title=legendTitle)) +
|
|
54
|
+
# scale_fill_manual(values = statusFills, guide_legend(title=legendTitle)) +
|
|
55
|
+
scale_x_date(date_labels = "%d-%b",date_breaks = "1 week") +
|
|
56
|
+
expand_limits(y=0) +
|
|
57
|
+
theme(
|
|
58
|
+
plot.title = element_text(family = "roboto"),
|
|
59
|
+
axis.title.x = element_text(family = "roboto"),
|
|
60
|
+
axis.title.y = element_text(family = "roboto"),
|
|
61
|
+
panel.grid.major.y = element_line(colour = "#D9D9D9"), panel.grid.minor.y = element_line(colour = "#D9D9D9"),
|
|
62
|
+
panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(),
|
|
63
|
+
# background color
|
|
64
|
+
panel.background = element_rect(fill = "#D9D9D920", colour = NA),
|
|
65
|
+
legend.position = "bottom",
|
|
66
|
+
plot.background = element_rect(colour = "#D9D9D9", fill=NA)
|
|
67
|
+
) +
|
|
68
|
+
scale_y_continuous(breaks = scales::pretty_breaks())
|
|
69
|
+
filename = str_c("~/projects/sprint-tracker/",str_remove_all(plotTitle," "),".png")
|
|
70
|
+
print(str_c("Saving",plotTitle,"to",filename,sep = " "))
|
|
71
|
+
ggsave(filename,plot=issuePlot,bg="white")
|
|
72
|
+
|
|
73
|
+
## Issue Counts
|
|
74
|
+
|
|
75
|
+
plotTitle = paste(project_health%>%pull(project)%>%unique(),"Project Health")
|
|
76
|
+
countPlot = project_health %>%
|
|
77
|
+
filter(measure %in% c("unestimated_percentage","closed_percentage")) %>%
|
|
78
|
+
ggplot() +
|
|
79
|
+
geom_line(aes(x=date,y=count,group=measure,color=measure)) +
|
|
80
|
+
# geom_line(data=project_health,aes(x=date,y=count)) +
|
|
81
|
+
labs(title = plotTitle,
|
|
82
|
+
x = "Date",
|
|
83
|
+
y = "Percentage",
|
|
84
|
+
caption = str_wrap("This plot includes issues from non-AIM teams, including 26 issues that will be rolled over to the CDCE team at the end of our contract",
|
|
85
|
+
width=100),
|
|
86
|
+
alt = "") +
|
|
87
|
+
# scale_colour_manual(values = statusColors,guide_legend(title=legendTitle)) +
|
|
88
|
+
# scale_fill_manual(values = statusFills, guide_legend(title=legendTitle)) +
|
|
89
|
+
scale_x_date(date_labels = "%d-%b",date_breaks = "1 week") +
|
|
90
|
+
# expand_limits(y=0) +
|
|
91
|
+
theme(
|
|
92
|
+
plot.title = element_text(family = "roboto"),
|
|
93
|
+
axis.title.x = element_text(family = "roboto"),
|
|
94
|
+
axis.title.y = element_text(family = "roboto"),
|
|
95
|
+
panel.grid.major.y = element_line(colour = "#D9D9D9"), panel.grid.minor.y = element_line(colour = "#D9D9D9"),
|
|
96
|
+
panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(),
|
|
97
|
+
# background color
|
|
98
|
+
panel.background = element_rect(fill = "#D9D9D920", colour = NA),
|
|
99
|
+
legend.position = "bottom",
|
|
100
|
+
plot.background = element_rect(colour = "#D9D9D9", fill=NA)
|
|
101
|
+
) +
|
|
102
|
+
# scale_y_continuous(breaks = scales::pretty_breaks()) +
|
|
103
|
+
ylim(0,1) +
|
|
104
|
+
facet_wrap(~measure,
|
|
105
|
+
nrow = 2)
|
|
106
|
+
filename = str_c("~/projects/sprint-tracker/",str_remove_all(plotTitle," "),".png")
|
|
107
|
+
print(str_c("Saving",plotTitle,"to",filename,sep = " "))
|
|
108
|
+
ggsave(filename,plot=countPlot,bg="white")
|
|
109
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
library(ggplot2)
|
|
2
|
+
library(tidyverse)
|
|
3
|
+
library(lubridate)
|
|
4
|
+
|
|
5
|
+
#az boards query --organization https://dev.azure.com/teamsi --id b18a81c9-3e3a-4dc6-87ef-140caf5bcb6a | jq -r '[.[]| {team:.fields."System.AreaPath",type:.fields."System.WorkItemType",id,estimate: .fields."Microsoft.VSTS.Scheduling.StoryPoints" }]' | json2csv -o ~/projects/backlog.csv
|
|
6
|
+
|
|
7
|
+
backlog = read_csv('~/projects/backlog.csv')
|
|
8
|
+
|
|
9
|
+
data = backlog %>%
|
|
10
|
+
mutate(
|
|
11
|
+
estimate = replace_na(estimate,0),
|
|
12
|
+
team = str_remove(team,"The Helo Project\\\\Solution Team(\\\\)"),
|
|
13
|
+
team = str_remove(team,"The Helo Project\\\\")
|
|
14
|
+
) %>%
|
|
15
|
+
group_by(team,type) %>%
|
|
16
|
+
summarise(count = n()) %>%
|
|
17
|
+
mutate(total = sum(count))
|
|
18
|
+
|
|
19
|
+
p = ggplot(data,aes(x=reorder(team,total),y=count, fill=type, color = type)) +
|
|
20
|
+
geom_col() +
|
|
21
|
+
coord_flip() +
|
|
22
|
+
scale_fill_manual(values=c("#31313180",
|
|
23
|
+
"#d61c1c80",
|
|
24
|
+
"#2e509680")) +
|
|
25
|
+
scale_color_manual(values=c("#313131",
|
|
26
|
+
"#d61c1c",
|
|
27
|
+
"#2e5096")) +
|
|
28
|
+
labs(title = "Backlog Work Items by Team",
|
|
29
|
+
subtitle = "Work-items in a ready to work state, including Stories, Analyses, and Bugs.\nThis includes work-items both assigned and not assigned to a sprint, including the current sprint.",
|
|
30
|
+
x = "",
|
|
31
|
+
y = "Issue Count",
|
|
32
|
+
alt = paste("QA issue burn up chart",
|
|
33
|
+
sep = " "),
|
|
34
|
+
caption = Sys.Date(),
|
|
35
|
+
fill = 'Work Item Type',
|
|
36
|
+
color = 'Work Item Type') +
|
|
37
|
+
theme(
|
|
38
|
+
plot.title = element_text(family = "roboto"),
|
|
39
|
+
axis.title.x = element_text(family = "roboto"),
|
|
40
|
+
axis.title.y = element_text(family = "roboto"),
|
|
41
|
+
panel.grid.major.x = element_line(colour = "#D9D9D9"), panel.grid.minor.x = element_line(colour = "#D9D9D9"),
|
|
42
|
+
panel.grid.major.y = element_blank(), panel.grid.minor.y = element_blank(),
|
|
43
|
+
# background color
|
|
44
|
+
panel.background = element_rect(fill = "#D9D9D920", colour = NA),
|
|
45
|
+
legend.position = "bottom",
|
|
46
|
+
plot.background = element_rect(colour = "#D9D9D9", fill=NA)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
FILENAME = str_c("~/projects/sprint-tracker/","backlog_by_team",".png")
|
|
50
|
+
ggsave(FILENAME,plot=p,bg="white", width = 2882, height = 1700, units = "px")
|
|
51
|
+
print(str_c("Saving to ",FILENAME),sep = " ")
|
|
52
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
library(ggplot2)
|
|
2
|
+
library(tidyverse)
|
|
3
|
+
|
|
4
|
+
library(DBI)
|
|
5
|
+
library(RSQLite)
|
|
6
|
+
con <- dbConnect(RSQLite::SQLite(),"~/projects/sprint-tracker/tracker.db")
|
|
7
|
+
|
|
8
|
+
res <- dbSendQuery(con, "
|
|
9
|
+
SELECT
|
|
10
|
+
bh.id,
|
|
11
|
+
p.name,
|
|
12
|
+
bh.date,
|
|
13
|
+
bh.health_type,
|
|
14
|
+
bh.backlog_count,
|
|
15
|
+
bh.unhealthy_count
|
|
16
|
+
FROM backlog_health bh
|
|
17
|
+
JOIN
|
|
18
|
+
projects p ON p.id = bh.project_id;
|
|
19
|
+
")
|
|
20
|
+
|
|
21
|
+
backlogData = dbFetch(res) %>%
|
|
22
|
+
tibble() %>%
|
|
23
|
+
select(-id) %>%
|
|
24
|
+
mutate(
|
|
25
|
+
date = lubridate::ymd(date),
|
|
26
|
+
healthy_count = backlog_count - unhealthy_count)
|
|
27
|
+
|
|
28
|
+
# pivot_longer(cols=c(qa_review_count,qa_validated_count),values_to="count",names_to="status") %>%
|
|
29
|
+
# Coalesce NA to zero
|
|
30
|
+
# mutate(count = replace_na(count,0),date = lubridate::ymd(date))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Clear the result
|
|
34
|
+
dbClearResult(res)
|
|
35
|
+
|
|
36
|
+
# Disconnect from the database
|
|
37
|
+
dbDisconnect(con)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
statusColors = c("backlog_count" = "#000000", "healthy_count" = "#03AC13")
|
|
41
|
+
statusFills = c("backlog_count" = "#00000020", "healthy_count" = "#03AC1380")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
p<-backlogData %>%
|
|
46
|
+
pivot_longer(cols=c(backlog_count,unhealthy_count,healthy_count),values_to = "count", names_to = "health_state") %>%
|
|
47
|
+
filter(health_type == 'workable backlog') %>%
|
|
48
|
+
filter(!health_state == 'unhealthy_count') %>%
|
|
49
|
+
ggplot(aes(x=date,
|
|
50
|
+
y=count
|
|
51
|
+
,
|
|
52
|
+
group=health_state,
|
|
53
|
+
color=health_state,
|
|
54
|
+
fill=health_state
|
|
55
|
+
)) +
|
|
56
|
+
# geom_point() + geom_line() +
|
|
57
|
+
geom_area(linewidth=2,position="identity") +
|
|
58
|
+
geom_label(data = backlogData %>%
|
|
59
|
+
pivot_longer(cols=c(backlog_count,unhealthy_count,healthy_count),values_to = "count", names_to = "health_state") %>%
|
|
60
|
+
filter(health_type == 'workable backlog') %>%
|
|
61
|
+
filter(health_state == "healthy_count")%>%
|
|
62
|
+
tail(n=1),
|
|
63
|
+
mapping = aes(y=count,label=count),
|
|
64
|
+
nudge_y = 1.1,
|
|
65
|
+
nudge_x = 2.2) +
|
|
66
|
+
geom_label(data = backlogData %>%
|
|
67
|
+
pivot_longer(cols=c(backlog_count,unhealthy_count,healthy_count),values_to = "count", names_to = "health_state") %>%
|
|
68
|
+
filter(health_type == 'workable backlog') %>%
|
|
69
|
+
filter(health_state == "backlog_count")%>%
|
|
70
|
+
tail(n=1),
|
|
71
|
+
mapping = aes(y=count,label=count),
|
|
72
|
+
nudge_y = 1.1,
|
|
73
|
+
nudge_x = 2.2) +
|
|
74
|
+
scale_color_manual(values = statusColors,guide_legend(title="Status")) +
|
|
75
|
+
scale_fill_manual(values = statusFills,guide_legend(title="Status")) +
|
|
76
|
+
labs(title = "Backlog Health",
|
|
77
|
+
subtitle = "Workable items with acceptance criteria text",
|
|
78
|
+
x = "Date",
|
|
79
|
+
y = "Issue Count",
|
|
80
|
+
alt = paste("Line chart of workable backlog items",
|
|
81
|
+
sep = " "),
|
|
82
|
+
caption = Sys.Date()) +
|
|
83
|
+
scale_x_date(date_labels = "%d-%b",date_breaks = "1 week") +
|
|
84
|
+
theme(
|
|
85
|
+
plot.title = element_text(family = "roboto"),
|
|
86
|
+
axis.title.x = element_text(family = "roboto"),
|
|
87
|
+
axis.title.y = element_text(family = "roboto"),
|
|
88
|
+
axis.text.x = element_text(angle = 45, vjust = 1, hjust=1),
|
|
89
|
+
# Grid
|
|
90
|
+
panel.grid.major.y = element_line(colour = "#D9D9D9"),
|
|
91
|
+
panel.grid.minor.y = element_line(colour = "#D9D9D9"),
|
|
92
|
+
panel.grid.major.x = element_line(colour = "#D9D9D9"),
|
|
93
|
+
panel.grid.minor.x = element_blank(),
|
|
94
|
+
# background color
|
|
95
|
+
panel.background = element_rect(fill = "#D9D9D920", colour = NA),
|
|
96
|
+
plot.background = element_rect(colour = "#D9D9D9", fill=NA),
|
|
97
|
+
# Move legend
|
|
98
|
+
legend.position = "bottom"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
ggsave(str_c("~/projects/ado-utilities/","backlog_health_ac",".png"),plot=p,bg="white", width = 2882, height = 1700, units = "px")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
p<-backlogData %>%
|
|
105
|
+
pivot_longer(cols=c(backlog_count,unhealthy_count,healthy_count),values_to = "count", names_to = "health_state") %>%
|
|
106
|
+
filter(health_type == 'backlog health') %>%
|
|
107
|
+
filter(!health_state == 'unhealthy_count') %>%
|
|
108
|
+
ggplot(aes(x=date,
|
|
109
|
+
y=count
|
|
110
|
+
,
|
|
111
|
+
group=health_state,
|
|
112
|
+
color=health_state,
|
|
113
|
+
fill=health_state
|
|
114
|
+
)) +
|
|
115
|
+
# geom_point() + geom_line() +
|
|
116
|
+
geom_area(linewidth=2,position="identity") +
|
|
117
|
+
geom_label(data = backlogData %>%
|
|
118
|
+
pivot_longer(cols=c(backlog_count,unhealthy_count,healthy_count),values_to = "count", names_to = "health_state") %>%
|
|
119
|
+
filter(health_type == 'backlog health') %>%
|
|
120
|
+
filter(health_state == "healthy_count")%>%
|
|
121
|
+
tail(n=1),
|
|
122
|
+
mapping = aes(y=count,label=count),
|
|
123
|
+
nudge_y = 1.1,
|
|
124
|
+
nudge_x = 2.2) +
|
|
125
|
+
geom_label(data = backlogData %>%
|
|
126
|
+
pivot_longer(cols=c(backlog_count,unhealthy_count,healthy_count),values_to = "count", names_to = "health_state") %>%
|
|
127
|
+
filter(health_type == 'backlog health') %>%
|
|
128
|
+
filter(health_state == "backlog_count")%>%
|
|
129
|
+
tail(n=1),
|
|
130
|
+
mapping = aes(y=count,label=count),
|
|
131
|
+
nudge_y = 1.1,
|
|
132
|
+
nudge_x = 2.2) +
|
|
133
|
+
scale_color_manual(values = statusColors,guide_legend(title="Status")) +
|
|
134
|
+
scale_fill_manual(values = statusFills,guide_legend(title="Status")) +
|
|
135
|
+
labs(title = "Backlog Health",
|
|
136
|
+
subtitle = "Backlog items (Story,Analysis) in a ready to work state. Prior to 14 June 2023, bugs were included in the data.",
|
|
137
|
+
x = "Date",
|
|
138
|
+
y = "Issue Count",
|
|
139
|
+
alt = paste("QA issue burn up chart",
|
|
140
|
+
sep = " "),
|
|
141
|
+
caption = Sys.Date()) +
|
|
142
|
+
scale_x_date(date_labels = "%d-%b",date_breaks = "1 week") +
|
|
143
|
+
theme(
|
|
144
|
+
plot.title = element_text(family = "roboto"),
|
|
145
|
+
axis.title.x = element_text(family = "roboto"),
|
|
146
|
+
axis.title.y = element_text(family = "roboto"),
|
|
147
|
+
axis.text.x = element_text(angle = 45, vjust = 1, hjust=1),
|
|
148
|
+
# Grid
|
|
149
|
+
panel.grid.major.y = element_line(colour = "#D9D9D9"),
|
|
150
|
+
panel.grid.minor.y = element_line(colour = "#D9D9D9"),
|
|
151
|
+
panel.grid.major.x = element_line(colour = "#D9D9D9"),
|
|
152
|
+
panel.grid.minor.x = element_blank(),
|
|
153
|
+
# background color
|
|
154
|
+
panel.background = element_rect(fill = "#D9D9D920", colour = NA),
|
|
155
|
+
plot.background = element_rect(colour = "#D9D9D9", fill=NA),
|
|
156
|
+
# Move legend
|
|
157
|
+
legend.position = "bottom"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
FILENAME = str_c("~/projects/ado-utilities/","backlog_health",".png")
|
|
161
|
+
ggsave(FILENAME,plot=p,bg="white", width = 2882, height = 1700, units = "px")
|
|
162
|
+
print(str_c("Saving to ",FILENAME),sep = " ")
|
|
163
|
+
|