@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
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
if ! docker info > /dev/null 2>&1; then
|
|
4
|
+
echo "This script uses docker, and it isn't running - please start docker and try again!"
|
|
5
|
+
exit 1
|
|
6
|
+
fi
|
|
7
|
+
|
|
8
|
+
# PROJECT_DIR must have trailing slash
|
|
9
|
+
PROJECT_DIR=/Users/beauraines/projects/
|
|
10
|
+
IMAGE=rocker/tidyverse
|
|
11
|
+
|
|
12
|
+
cd ~/Documents/Expeditors/feature-team-work-items
|
|
13
|
+
|
|
14
|
+
jq -r '.[]| [.id,.fields."System.Title",.fields."System.WorkItemType",.fields."System.IterationPath",.fields."System.State", input_filename] | @csv' feature-team-*.json > ~/projects/featureTeamBacklog.csv
|
|
15
|
+
|
|
16
|
+
docker run -it --rm -v ${PROJECT_DIR}:/home/rstudio/projects/ ${IMAGE} su -c 'Rscript /home/rstudio/projects/sprint-tracker/R/featureTeamBacklog.R 2>/dev/null' rstudio
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
if ! docker info > /dev/null 2>&1; then
|
|
4
|
+
echo "This script uses docker, and it isn't running - please start docker and try again!"
|
|
5
|
+
exit 1
|
|
6
|
+
fi
|
|
7
|
+
|
|
8
|
+
# PROJECT_DIR must have trailing slash
|
|
9
|
+
PROJECT_DIR=/Users/beauraines/projects/
|
|
10
|
+
IMAGE=rocker/tidyverse
|
|
11
|
+
|
|
12
|
+
docker run -it --rm -v ${PROJECT_DIR}:/home/rstudio/projects/ ${IMAGE} su -c 'Rscript /home/rstudio/projects/sprint-tracker/R/sprintOutcomes.R 2>/dev/null' rstudio
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#set -x
|
|
3
|
+
|
|
4
|
+
# Get Sprint Outcome data
|
|
5
|
+
|
|
6
|
+
# How to make this dynamic - like the lines that start with cat
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
cat 2023-07-25T18:00-07:00.csv | csvsql --query /Users/beauraines/projects/sprint-tracker/sql/commitment.sql
|
|
10
|
+
cat 2023-07-25T18:00-07:00.csv | csvsql --query /Users/beauraines/projects/sprint-tracker/sql/commitment.sql | csvlook
|
|
11
|
+
csvsql --query ~/projects/sprint-tracker/sql/commitmentMet.sql 2023-08-07T18:00-07:00.csv 2023-07-25T18:00-07:00.csv | csvlook --no-inference
|
|
12
|
+
cat 2023-08-07T18:00-07:00.csv | csvsql --no-inference --query ~/projects/sprint-tracker/sql/foo2.sql | csvlook
|
|
13
|
+
cat 2023-08-07T18:00-07:00.csv | csvsql --no-inference --query ~/projects/sprint-tracker/sql/completed.sql | csvlook
|
|
14
|
+
|
|
15
|
+
csvsql --query ~/projects/sprint-tracker/sql/descoped.sql 2023-08-07T18:00-07:00.csv 2023-07-25T18:00-07:00.csv | csvlook
|
|
16
|
+
cat 2023-07-25T18:00-07:00.csv | csvsql --query /Users/beauraines/projects/sprint-tracker/sql/commitment.sql
|
|
17
|
+
cat 2023-08-07T18:00-07:00.csv | csvsql --no-inference --query ~/projects/sprint-tracker/sql/completed.sql | csvlook
|
|
18
|
+
csvsql --query ~/projects/sprint-tracker/sql/pullForward.sql 2023-07-25T18:00-07:00.csv 2023-08-07T18:00-07:00.csv | csvlook
|
|
19
|
+
csvsql --query ~/projects/sprint-tracker/sql/descoped.sql 2023-08-07T18:00-07:00.csv 2023-07-25T18:00-07:00.csv | csvlook
|
|
20
|
+
csvsql --query ~/projects/sprint-tracker/sql/carryOver.sql 2023-08-07T18:00-07:00.csv | csvlook
|
|
21
|
+
|
|
22
|
+
csvsql --query ~/projects/sprint-tracker/sql/commitmentMet.sql 2023-08-07T18:00-07:00.csv 2023-07-25T18:00-07:00.csv | csvlook --no-inference
|
|
23
|
+
cat 2023-08-07T18:00-07:00.csv | csvsql --no-inference --query ~/projects/sprint-tracker/sql/completed.sql | csvlook
|
|
24
|
+
csvsql --query ~/projects/sprint-tracker/sql/pullForward.sql 2023-07-25T18:00-07:00.csv 2023-08-07T18:00-07:00.csv | csvlook
|
|
25
|
+
csvsql --query ~/projects/sprint-tracker/sql/descoped.sql 2023-08-07T18:00-07:00.csv 2023-07-25T18:00-07:00.csv | csvlook
|
|
26
|
+
csvsql --query ~/projects/sprint-tracker/sql/carryOver.sql 2023-08-07T18:00-07:00.csv | csvlook
|
|
27
|
+
cat 2023-07-25T18:00-07:00.csv | csvsql --query /Users/beauraines/projects/sprint-tracker/sql/commitment.sql
|
|
28
|
+
cat 2023-07-25T18:00-07:00.csv | csvsql --query /Users/beauraines/projects/sprint-tracker/sql/commitment.sql | csvlook
|
|
29
|
+
csvsql --query ~/projects/sprint-tracker/sql/commitmentMet.sql 2023-08-07T18:00-07:00.csv 2023-07-25T18:00-07:00.csv | csvlook --no-inference
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/bugsOpenedDuringSprint.sql 2023-08-22T16:30-07:00.csv 2023-09-04T18:00-07:00.csv | csvlook
|
|
2
|
+
SELECT
|
|
3
|
+
end.ID,
|
|
4
|
+
end.Title,
|
|
5
|
+
end.State,
|
|
6
|
+
end."Area Path"
|
|
7
|
+
FROM
|
|
8
|
+
'2023-09-04T18:00-07:00' end
|
|
9
|
+
left join '2023-08-22T16:30-07:00' start on start.id = end.id
|
|
10
|
+
WHERE
|
|
11
|
+
start.id is null and
|
|
12
|
+
end."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
13
|
+
-- end."Area Path" = 'The Helo Project\Solution Team\Feature Team 1'
|
|
14
|
+
and end.'Work Item Type' = 'Bug'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/bugsOpenedDuringSprintFeature.sql 2023-08-22T16:30-07:00.csv 2023-09-04T18:00-07:00.csv | csvlook
|
|
2
|
+
SELECT
|
|
3
|
+
end.ID,
|
|
4
|
+
end.Title,
|
|
5
|
+
end.State,
|
|
6
|
+
end."Area Path"
|
|
7
|
+
FROM
|
|
8
|
+
'2023-09-04T18:00-07:00' end
|
|
9
|
+
left join '2023-08-22T16:30-07:00' start on start.id = end.id
|
|
10
|
+
WHERE
|
|
11
|
+
start.id is null and
|
|
12
|
+
-- end."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
13
|
+
end."Area Path" = 'The Helo Project\Solution Team\Feature Team 1'
|
|
14
|
+
and end.'Work Item Type' = 'Bug'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
select
|
|
2
|
+
s.id,
|
|
3
|
+
s.sprint_name,
|
|
4
|
+
s.start_date,
|
|
5
|
+
s.end_date,
|
|
6
|
+
case
|
|
7
|
+
when developers is null and s.start_date < '2022-05-25' then 2
|
|
8
|
+
when developers is null and s.start_date >= '2022-05-25' then 3
|
|
9
|
+
else developers
|
|
10
|
+
end
|
|
11
|
+
developers, -- if null, before 2022-05-25 only 2, 2022-05-25 and after, 3
|
|
12
|
+
ifnull(non_working_days,0) AS non_working_days,
|
|
13
|
+
ifnull(developer_pto_days,0) as developer_pto_days,
|
|
14
|
+
so.story_points AS story_points_delivered,
|
|
15
|
+
case
|
|
16
|
+
when developers is null and s.start_date < '2022-05-25' then 2
|
|
17
|
+
when developers is null and s.start_date >= '2022-05-25' then 3
|
|
18
|
+
else developers
|
|
19
|
+
end * (10 - ifnull(non_working_days,0)) - ifnull(developer_pto_days,0) AS developer_days,
|
|
20
|
+
|
|
21
|
+
/* case
|
|
22
|
+
when developers is null and s.start_date < '2022-05-25' then 2
|
|
23
|
+
when developers is null and s.start_date >= '2022-05-25' then 3
|
|
24
|
+
else developers
|
|
25
|
+
end * (10 - ifnull(non_working_days,0)) - ifnull(developer_pto_days,0) / 30 AS something,
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
cast(so.story_points as REAL) / (case
|
|
29
|
+
when developers is null and s.start_date < '2022-05-25' then 2
|
|
30
|
+
when developers is null and s.start_date >= '2022-05-25' then 3
|
|
31
|
+
else developers
|
|
32
|
+
end * (10 - ifnull(non_working_days,0)) - ifnull(developer_pto_days,0)) AS points_per_developer_day,
|
|
33
|
+
|
|
34
|
+
cast(so.story_points as REAL) / (case
|
|
35
|
+
when developers is null and s.start_date < '2022-05-25' then 2
|
|
36
|
+
when developers is null and s.start_date >= '2022-05-25' then 3
|
|
37
|
+
else developers
|
|
38
|
+
end * (10 - ifnull(non_working_days,0)) - ifnull(developer_pto_days,0)) * case
|
|
39
|
+
when developers is null and s.start_date < '2022-05-25' then 2
|
|
40
|
+
when developers is null and s.start_date >= '2022-05-25' then 3
|
|
41
|
+
else developers
|
|
42
|
+
end * 10 AS capacity_adjusted_velocity,*/
|
|
43
|
+
|
|
44
|
+
so.notes
|
|
45
|
+
from sprints s
|
|
46
|
+
JOIN sprint_outcomes so on so.sprint_id = s.id and so.outcome_id = 1 -- delivered stories only
|
|
47
|
+
WHERE
|
|
48
|
+
s.project_id = 4;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/carryOver.sql 2023-09-04T18:00-07:00.csv | csvlook
|
|
2
|
+
|
|
3
|
+
SELECT
|
|
4
|
+
"Area Path",
|
|
5
|
+
count(*),
|
|
6
|
+
sum("Story Points"),
|
|
7
|
+
sum(CASE
|
|
8
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
9
|
+
THEN 1
|
|
10
|
+
ELSE "Story Points"
|
|
11
|
+
END) Story_Points_Adjusted
|
|
12
|
+
FROM
|
|
13
|
+
'2023-09-04T18:00-07:00'
|
|
14
|
+
WHERE
|
|
15
|
+
"Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
16
|
+
and State not in ('Closed','As Designed','Wont Fix')
|
|
17
|
+
GROUP BY "Area Path"
|
|
18
|
+
|
|
19
|
+
UNION
|
|
20
|
+
SELECT
|
|
21
|
+
'Total',
|
|
22
|
+
count(*),
|
|
23
|
+
sum("Story Points"),
|
|
24
|
+
sum(CASE
|
|
25
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
26
|
+
THEN 1
|
|
27
|
+
ELSE "Story Points"
|
|
28
|
+
END) Story_Points_Adjusted
|
|
29
|
+
FROM
|
|
30
|
+
'2023-09-04T18:00-07:00'
|
|
31
|
+
WHERE
|
|
32
|
+
"Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
33
|
+
and State not in ('Closed','As Designed','Wont Fix')
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- cat 2023-08-22T16:30-07:00.csv | csvsql --query /Users/beauraines/projects/sprint-tracker/sql/commitment.sql | csvlook
|
|
2
|
+
SELECT
|
|
3
|
+
start."Area Path",
|
|
4
|
+
count(*) AS Committed_Stories,
|
|
5
|
+
sum(CASE
|
|
6
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
7
|
+
THEN 1
|
|
8
|
+
ELSE "Story Points"
|
|
9
|
+
END) Committed_Story_Points
|
|
10
|
+
FROM
|
|
11
|
+
stdin AS start
|
|
12
|
+
WHERE
|
|
13
|
+
start."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
14
|
+
GROUP BY
|
|
15
|
+
start."Area Path"
|
|
16
|
+
|
|
17
|
+
UNION
|
|
18
|
+
|
|
19
|
+
SELECT
|
|
20
|
+
'Total',
|
|
21
|
+
count(*) AS Committed_Stories,
|
|
22
|
+
sum(CASE
|
|
23
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
24
|
+
THEN 1
|
|
25
|
+
ELSE "Story Points"
|
|
26
|
+
END) Committed_Story_Points
|
|
27
|
+
FROM
|
|
28
|
+
stdin AS start
|
|
29
|
+
WHERE
|
|
30
|
+
start."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/commitmentMet.sql 2023-09-04T18:00-07:00.csv 2023-08-22T16:30-07:00.csv | csvlook --no-inference
|
|
2
|
+
|
|
3
|
+
WITH start AS (
|
|
4
|
+
SELECT
|
|
5
|
+
*,
|
|
6
|
+
CASE
|
|
7
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
8
|
+
THEN 1
|
|
9
|
+
ELSE "Story Points"
|
|
10
|
+
END Story_Points_Adjusted
|
|
11
|
+
FROM
|
|
12
|
+
'2023-08-22T16:30-07:00'
|
|
13
|
+
),
|
|
14
|
+
|
|
15
|
+
end as (
|
|
16
|
+
SELECT
|
|
17
|
+
*,
|
|
18
|
+
CASE
|
|
19
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
20
|
+
THEN 1
|
|
21
|
+
ELSE "Story Points"
|
|
22
|
+
END Story_Points_Adjusted
|
|
23
|
+
FROM
|
|
24
|
+
'2023-09-04T18:00-07:00'
|
|
25
|
+
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
SELECT
|
|
31
|
+
start."Area Path",
|
|
32
|
+
count(*) AS Commitment,
|
|
33
|
+
sum(start."Story_Points_Adjusted") AS Committed_Story_Points,
|
|
34
|
+
sum(CASE WHEN end.State in ('Closed','As Designed') THEN 1 ELSE 0 END) AS Closed_Stories,
|
|
35
|
+
sum(CASE WHEN end.State in ('Closed','As Designed') THEN end."Story_Points_Adjusted" ELSE 0 END) AS Closed_Story_Points,
|
|
36
|
+
sum(CASE WHEN end.State = 'Closed' THEN 1 WHEN end.State = 'QA Review' THEN 1 WHEN end.State = 'In QA' THEN 1 WHEN end.State = 'In UAT' THEN 1 ELSE 0 END) AS QA_Stories,
|
|
37
|
+
cast(sum(CASE WHEN end.State = 'Closed' THEN 1 ELSE 0 END) as float) / count(*) AS Commited_Story_Percent
|
|
38
|
+
FROM
|
|
39
|
+
start
|
|
40
|
+
LEFT join end on end.id = start.id and start."Area Path" = end."Area Path" -- committed work-items move between teams
|
|
41
|
+
WHERE
|
|
42
|
+
start."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
43
|
+
GROUP BY
|
|
44
|
+
start."Area Path"
|
|
45
|
+
|
|
46
|
+
UNION
|
|
47
|
+
|
|
48
|
+
SELECT
|
|
49
|
+
'Total',
|
|
50
|
+
count(*) AS Commitment,
|
|
51
|
+
sum(start."Story_Points_Adjusted") AS Committed_Story_Points,
|
|
52
|
+
sum(CASE WHEN end.State = 'Closed' THEN 1 ELSE 0 END) AS Closed_Stories,
|
|
53
|
+
sum(CASE WHEN end.State in ('Closed','As Designed') THEN end."Story_Points_Adjusted" ELSE 0 END) AS Closed_Story_Points,
|
|
54
|
+
sum(CASE WHEN end.State = 'Closed' THEN 1 WHEN end.State = 'QA Review' THEN 1 WHEN end.State = 'In QA' THEN 1 WHEN end.State = 'In UAT' THEN 1 ELSE 0 END) AS QA_Stories,
|
|
55
|
+
cast(sum(CASE WHEN end.State = 'Closed' THEN 1 ELSE 0 END) as float) / count(*) AS Commited_Story_Percent
|
|
56
|
+
FROM
|
|
57
|
+
start
|
|
58
|
+
LEFT join end on end.id = start.id and start."Area Path" = end."Area Path"
|
|
59
|
+
WHERE
|
|
60
|
+
start."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- cat 2023-09-04T18:00-07:00.csv | csvsql --no-inference --query ~/projects/sprint-tracker/sql/completed.sql | csvlook
|
|
2
|
+
WITH work_items AS
|
|
3
|
+
(SELECT
|
|
4
|
+
*,
|
|
5
|
+
CASE
|
|
6
|
+
WHEN "Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
7
|
+
THEN 1
|
|
8
|
+
ELSE "Story Points"
|
|
9
|
+
END Story_Points_Adjusted
|
|
10
|
+
FROM
|
|
11
|
+
stdin
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
SELECT
|
|
15
|
+
"Area Path",
|
|
16
|
+
sum(CASE WHEN State in ('Closed','As Designed','Wont Fix') THEN 1 ELSE 0 END) AS Closed_Stories,
|
|
17
|
+
sum(CASE WHEN State in ('Closed','As Designed','Wont Fix') THEN Story_Points_Adjusted ELSE 0 END) AS Closed_Story_Points,
|
|
18
|
+
sum(CASE WHEN State in ('Closed','As Designed','QA Review','In QA','In UAT') THEN 1 ELSE 0 END) AS Closed_Including_QA_Stories,
|
|
19
|
+
sum(CASE WHEN State in ('QA Review','In QA','In UAT') THEN 1 ELSE 0 END) AS QA_Stories
|
|
20
|
+
FROM
|
|
21
|
+
work_items
|
|
22
|
+
WHERE
|
|
23
|
+
State in ('Closed','QA Review','In QA','In UAT','As Designed','Wont Fix')
|
|
24
|
+
and "Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
25
|
+
GROUP BY
|
|
26
|
+
"Area Path"
|
|
27
|
+
|
|
28
|
+
UNION
|
|
29
|
+
|
|
30
|
+
SELECT
|
|
31
|
+
"Total",
|
|
32
|
+
sum(CASE WHEN State in ('Closed','As Designed','Wont Fix') THEN 1 ELSE 0 END) AS Closed_Stories,
|
|
33
|
+
sum(CASE WHEN State in ('Closed','As Designed','Wont Fix') THEN Story_Points_Adjusted ELSE 0 END) AS Closed_Story_Points,
|
|
34
|
+
sum(CASE WHEN State in ('Closed','As Designed','QA Review','In QA','In UAT') THEN 1 ELSE 0 END) AS Closed_Including_QA_Stories,
|
|
35
|
+
sum(CASE WHEN State in ('QA Review','In QA','In UAT') THEN 1 ELSE 0 END) AS QA_Stories
|
|
36
|
+
FROM
|
|
37
|
+
work_items
|
|
38
|
+
WHERE
|
|
39
|
+
State in ('Closed','QA Review','In QA','In UAT','As Designed','Wont Fix')
|
|
40
|
+
and "Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
package/sql/descoped.sql
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/descoped.sql 2023-09-04T18:00-07:00.csv 2023-08-22T16:30-07:00.csv | csvlook --no-inference
|
|
2
|
+
SELECT
|
|
3
|
+
start.'Work Item Type',
|
|
4
|
+
start."Area Path",
|
|
5
|
+
count(*),
|
|
6
|
+
-- sum(start."Story Points"),
|
|
7
|
+
sum(CASE
|
|
8
|
+
WHEN start."Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
9
|
+
THEN 1
|
|
10
|
+
ELSE start."Story Points"
|
|
11
|
+
END) AS Story_Points
|
|
12
|
+
FROM
|
|
13
|
+
'2023-09-04T18:00-07:00' end
|
|
14
|
+
right join '2023-08-22T16:30-07:00' start on start.id = end.id
|
|
15
|
+
WHERE
|
|
16
|
+
end.id is null and
|
|
17
|
+
start."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
18
|
+
-- start."Area Path" in ('The Helo Project\Solution Team\Feature Team 1')
|
|
19
|
+
GROUP BY
|
|
20
|
+
start.'Work Item Type',
|
|
21
|
+
start."Area Path"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/pullForward.sql 2023-08-22T16:30-07:00.csv 2023-09-04T18:00-07:00.csv | csvlook
|
|
2
|
+
SELECT
|
|
3
|
+
end.'Work Item Type',
|
|
4
|
+
count(*),
|
|
5
|
+
-- sum(end."Story Points"),
|
|
6
|
+
sum(CASE
|
|
7
|
+
WHEN end."Story Points" = 0 -- Coerce any unestimated work-item to 1 point
|
|
8
|
+
THEN 1
|
|
9
|
+
ELSE end."Story Points"
|
|
10
|
+
END) AS Story_Points
|
|
11
|
+
FROM
|
|
12
|
+
'2023-09-04T18:00-07:00' end
|
|
13
|
+
left join '2023-08-22T16:30-07:00' start on start.id = end.id
|
|
14
|
+
WHERE
|
|
15
|
+
start.id is null and
|
|
16
|
+
-- end."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
17
|
+
end."Area Path" = 'The Helo Project\Solution Team\Feature Team 1'
|
|
18
|
+
GROUP BY
|
|
19
|
+
end.'Work Item Type'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- csvsql --query ~/projects/sprint-tracker/sql/pullForwardFeature.sql 2023-09-04T18:00-07:00.csv 2023-08-22T16:30-07:00.csv | csvlook
|
|
2
|
+
SELECT
|
|
3
|
+
end.*
|
|
4
|
+
FROM
|
|
5
|
+
'2023-09-04T18:00-07:00' end
|
|
6
|
+
left join '2023-08-22T16:30-07:00' start on start.id = end.id
|
|
7
|
+
WHERE
|
|
8
|
+
start.id is null and
|
|
9
|
+
-- end."Area Path" not in ('The Helo Project\Solution Team\Data Science','The Helo Project\Solution Team\Product Ownership','The Helo Project\Solution Team\QA Team')
|
|
10
|
+
end."Area Path" = 'The Helo Project\Solution Team\Feature Team 1'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- SQLite
|
|
2
|
+
SELECT
|
|
3
|
+
p.name project_name,
|
|
4
|
+
s.id sprint_id,
|
|
5
|
+
s.sprint_name,
|
|
6
|
+
s.end_date,
|
|
7
|
+
case
|
|
8
|
+
when o.name = 'Commitment' then 'A-Commitment'
|
|
9
|
+
when o.name = 'Delivered' then 'B-Delivered'
|
|
10
|
+
else o.name
|
|
11
|
+
end outcome,
|
|
12
|
+
-- s.commited_points,
|
|
13
|
+
-- so.issue_count,
|
|
14
|
+
-- so.story_points,
|
|
15
|
+
ifnull(so.story_points, so.issue_count) AS story_points -- accounts for unpointed bug stories
|
|
16
|
+
|
|
17
|
+
from projects p
|
|
18
|
+
|
|
19
|
+
JOIN sprints s on s.project_id = p.id
|
|
20
|
+
JOIN sprint_outcomes so on so.sprint_id = s.id
|
|
21
|
+
JOIN outcomes o on o.id = so.outcome_id
|
|
22
|
+
|
|
23
|
+
WHERE
|
|
24
|
+
-- p.id = 10 -- Helo Project Team
|
|
25
|
+
p.id = 11 -- Feature Team One
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- SQLite
|
|
2
|
+
SELECT
|
|
3
|
+
p.name project_name,
|
|
4
|
+
s.id sprint_id,
|
|
5
|
+
s.sprint_name,
|
|
6
|
+
case
|
|
7
|
+
when o.name = 'Commitment' then 'A-Commitment'
|
|
8
|
+
when o.name = 'Delivered' then 'B-Delivered'
|
|
9
|
+
else o.name
|
|
10
|
+
end outcome,
|
|
11
|
+
-- s.commited_points,
|
|
12
|
+
-- so.issue_count,
|
|
13
|
+
so.story_points
|
|
14
|
+
|
|
15
|
+
from projects p
|
|
16
|
+
|
|
17
|
+
JOIN sprints s on s.project_id = p.id
|
|
18
|
+
JOIN sprint_outcomes so on so.sprint_id = s.id
|
|
19
|
+
JOIN outcomes o on o.id = so.outcome_id
|
|
20
|
+
|
|
21
|
+
WHERE
|
|
22
|
+
p.pm_name like 'Beau%'
|
|
23
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const prompt = require('prompt-sync')({sigint:true});
|
|
4
|
+
const {homedir} = require('os');
|
|
5
|
+
const { database: db } = require('helpers')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
// TODO read from a config or CLI
|
|
10
|
+
// TODO add migrations check or index function
|
|
11
|
+
let database = `${homedir}/projects/sprint-tracker/tracker.db`
|
|
12
|
+
|
|
13
|
+
// list projects
|
|
14
|
+
|
|
15
|
+
let statement = 'select * from projects;';
|
|
16
|
+
|
|
17
|
+
let projects = await db.query(database,statement)
|
|
18
|
+
|
|
19
|
+
for (const project of projects) {
|
|
20
|
+
console.log(project.id, project.name)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Select the project
|
|
24
|
+
let project_id
|
|
25
|
+
|
|
26
|
+
let text = 'Enter the project id: '
|
|
27
|
+
while (!project_id) {
|
|
28
|
+
project_id = prompt(text)
|
|
29
|
+
|
|
30
|
+
// validate the response
|
|
31
|
+
if (!projects.find(x => (x.id == project_id))) {
|
|
32
|
+
console.error('Invalid project id')
|
|
33
|
+
project_id = null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
console.log(project_id)
|
|
39
|
+
|
|
40
|
+
// Add new sprint
|
|
41
|
+
|
|
42
|
+
let sprints = await db.query(database,`select id, sprint_name AS name from sprints where project_id = ${project_id};`)
|
|
43
|
+
|
|
44
|
+
// FIXME - sprint_name isn't being displayed because the function is looking for name
|
|
45
|
+
console.log('If your sprint is not displayed, you will have to manually add it.')
|
|
46
|
+
let sprintId = promptForInput(sprints,'Enter the sprint id: ')
|
|
47
|
+
|
|
48
|
+
// enter outcomes
|
|
49
|
+
let sprintOutcomes = []
|
|
50
|
+
let outcomes = await db.query(database,'select * from outcomes;')
|
|
51
|
+
for (const outcome of outcomes) {
|
|
52
|
+
// TODO Fix output formatting
|
|
53
|
+
console.log(outcome.id, outcome.name, outcome.description)
|
|
54
|
+
let issue_count = prompt('Enter the number of stories or a blank line to skip: ')
|
|
55
|
+
if (issue_count == '' ) {
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
let story_points = prompt('Enter the number of story points: ')
|
|
59
|
+
let notes = prompt('Enter any notes: ')
|
|
60
|
+
|
|
61
|
+
sprintOutcomes.push({
|
|
62
|
+
sprint_id: sprintId,
|
|
63
|
+
outcome_id: outcome.id,
|
|
64
|
+
issue_count,
|
|
65
|
+
story_points,
|
|
66
|
+
notes
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
// validate sprint_outcomes
|
|
70
|
+
|
|
71
|
+
console.log(sprintOutcomes)
|
|
72
|
+
|
|
73
|
+
// Save outcomes to DB
|
|
74
|
+
let result = await db.insert(database,'sprint_outcomes',sprintOutcomes,['sprint_id','outcome_id'])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main()
|
|
78
|
+
|
|
79
|
+
function promptForInput(items,promptText) {
|
|
80
|
+
let id
|
|
81
|
+
for (const item of items) {
|
|
82
|
+
console.log(item.id, item.name)
|
|
83
|
+
}
|
|
84
|
+
while (!id) {
|
|
85
|
+
id = prompt(promptText)
|
|
86
|
+
|
|
87
|
+
// validate the response
|
|
88
|
+
if (!items.find(x => (x.id == id))) {
|
|
89
|
+
console.error('Invalid id')
|
|
90
|
+
id = null
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return id
|
|
94
|
+
}
|
package/src/addSprint.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const prompt = require('prompt-sync')({sigint:true});
|
|
4
|
+
const {homedir} = require('os');
|
|
5
|
+
const { database: db } = require('helpers')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
// TODO read from a config or CLI
|
|
10
|
+
let database = `${homedir}/projects/sprint-tracker/tracker.db`
|
|
11
|
+
|
|
12
|
+
// list projects
|
|
13
|
+
|
|
14
|
+
let statement = 'select * from projects;';
|
|
15
|
+
|
|
16
|
+
let projects = await db.query(database,statement)
|
|
17
|
+
|
|
18
|
+
for (const project of projects) {
|
|
19
|
+
console.log(project.id, project.name)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Select the project
|
|
23
|
+
let project_id
|
|
24
|
+
|
|
25
|
+
let text = 'Enter the project id: '
|
|
26
|
+
while (!project_id) {
|
|
27
|
+
project_id = prompt(text)
|
|
28
|
+
|
|
29
|
+
// validate the response
|
|
30
|
+
if (!projects.find(x => (x.id == project_id))) {
|
|
31
|
+
console.error('Invalid project id')
|
|
32
|
+
project_id = null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
console.log(project_id)
|
|
38
|
+
|
|
39
|
+
// Get new sprint information from user
|
|
40
|
+
|
|
41
|
+
let sprint_name = prompt('Enter the sprint name: ')
|
|
42
|
+
let start_date = prompt('Enter the sprint start date: ')
|
|
43
|
+
let end_date = prompt('Enter the sprint end date: ')
|
|
44
|
+
let non_working_days = prompt('Enter the number of non working days: ')
|
|
45
|
+
let developers = prompt('Enter the number of developers assigned: ')
|
|
46
|
+
let developer_pto_days = prompt('Enter the total number of developer PTO days: ')
|
|
47
|
+
|
|
48
|
+
let sprint = {
|
|
49
|
+
sprint_name,
|
|
50
|
+
project_id,
|
|
51
|
+
start_date,
|
|
52
|
+
end_date,
|
|
53
|
+
non_working_days,
|
|
54
|
+
developers,
|
|
55
|
+
developer_pto_days
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Save outcomes to DB
|
|
59
|
+
let result = await db.insert(database,'sprints',[sprint])
|
|
60
|
+
let sprintId = result[0].lastID
|
|
61
|
+
|
|
62
|
+
// Prompt for commitment
|
|
63
|
+
let sprintOutcomes = []
|
|
64
|
+
let outcomes = await db.query(database,`select * from outcomes where name = 'Commitment';`)
|
|
65
|
+
for (const outcome of outcomes) {
|
|
66
|
+
// TODO Fix output formatting
|
|
67
|
+
console.log(outcome.id, outcome.name, outcome.description)
|
|
68
|
+
let issue_count = prompt('Enter the number of stories or a blank line to skip: ')
|
|
69
|
+
if (issue_count == '' ) {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
let story_points = prompt('Enter the number of story points: ')
|
|
73
|
+
let notes = prompt('Enter any notes: ')
|
|
74
|
+
|
|
75
|
+
sprintOutcomes.push({
|
|
76
|
+
sprint_id: sprintId,
|
|
77
|
+
outcome_id: outcome.id,
|
|
78
|
+
issue_count,
|
|
79
|
+
story_points,
|
|
80
|
+
notes
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
// validate sprint_outcomes
|
|
84
|
+
|
|
85
|
+
console.log(sprintOutcomes)
|
|
86
|
+
|
|
87
|
+
// Save outcomes to DB
|
|
88
|
+
result = await db.insert(database,'sprint_outcomes',sprintOutcomes,['sprint_id','outcome_id'])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
main()
|
|
92
|
+
|
|
93
|
+
function promptForInput(items,promptText) {
|
|
94
|
+
let id
|
|
95
|
+
for (const item of items) {
|
|
96
|
+
console.log(item.id, item.name)
|
|
97
|
+
}
|
|
98
|
+
while (!id) {
|
|
99
|
+
id = prompt(promptText)
|
|
100
|
+
|
|
101
|
+
// validate the response
|
|
102
|
+
if (!items.find(x => (x.id == id))) {
|
|
103
|
+
console.error('Invalid id')
|
|
104
|
+
id = null
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return id
|
|
108
|
+
}
|