@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.
Files changed (53) hide show
  1. package/.eslintrc.json +16 -0
  2. package/.github/dependabot.yml +21 -0
  3. package/CHANGELOG.md +52 -0
  4. package/R/HeloSprintBurndown.R +138 -0
  5. package/R/ProjectHealth.R +109 -0
  6. package/R/backlogByTeam.R +52 -0
  7. package/R/backlogHealth.R +163 -0
  8. package/R/burndDownChart.R +144 -0
  9. package/R/carryOverAdjustedVelocity.R +154 -0
  10. package/R/featureTeamBacklog.R +73 -0
  11. package/R/projectHealth.Rmd +111 -0
  12. package/R/sprintOutcomes.R +142 -0
  13. package/R/sprintOutcomesCli.R +103 -0
  14. package/R/sprintOutcomesFeatureTeam.R +143 -0
  15. package/R/timeInCodeReview.R +157 -0
  16. package/README.md +181 -0
  17. package/cli.js +55 -0
  18. package/cmds/addOutcomes.js +98 -0
  19. package/cmds/addProject.js +53 -0
  20. package/cmds/addSprint.js +117 -0
  21. package/cmds/config.js +33 -0
  22. package/cmds/getSprintDetails.js +86 -0
  23. package/cmds/visualizations.js +116 -0
  24. package/docker-compose.yml +11 -0
  25. package/migrations/20230222011333-create-health-table.sql +10 -0
  26. package/migrations/20230222011334-outcomes.sql +42 -0
  27. package/migrations/20231206093700-seed-outcomes-table.sql +9 -0
  28. package/migrations.js +20 -0
  29. package/package.json +50 -0
  30. package/scripts/generateBacklogByTeam.sh +20 -0
  31. package/scripts/generateBacklogHealth.sh +12 -0
  32. package/scripts/generateChart.sh +7 -0
  33. package/scripts/generateFeatureTeamBacklog.sh +16 -0
  34. package/scripts/generateSprintOutcomePlot.sh +12 -0
  35. package/scripts/sprintOutcomes.sh +29 -0
  36. package/sql/bugsOpenedDuringSprint.sql +14 -0
  37. package/sql/bugsOpenedDuringSprintFeature.sql +14 -0
  38. package/sql/capacityAdjustedVelocity.sql +48 -0
  39. package/sql/carryOver.sql +33 -0
  40. package/sql/commitment.sql +30 -0
  41. package/sql/commitmentMet.sql +60 -0
  42. package/sql/completed.sql +40 -0
  43. package/sql/descoped.sql +21 -0
  44. package/sql/pullForward.sql +19 -0
  45. package/sql/pullForwardFeature.sql +10 -0
  46. package/sql/sprintOutcomes.sql +25 -0
  47. package/sql/sprint_outcomes.sql +23 -0
  48. package/src/addOutcomes.js +94 -0
  49. package/src/addSprint.js +108 -0
  50. package/src/getSprintDetails.js +88 -0
  51. package/utils/display.js +68 -0
  52. package/utils/input.js +24 -0
  53. package/utils/readConfig.js +74 -0
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {homedir} = require('os');
4
+ const { database: db } = require('helpers')
5
+ const { prompt,promptForInput } = require('../utils/input.js')
6
+ const {readConfig} = require('../utils/readConfig.js')
7
+ const display = require('../utils/display.js')
8
+ const path = require('path');
9
+
10
+ const command = 'addOutcome'
11
+ const desc = 'Menu driven process to add outcomes to a sprint'
12
+ const builder = {}
13
+
14
+ const handler = async function () {
15
+ // TODO add migrations check or index function
16
+ const filename = 'sprintTracker.json'
17
+ const configFile = path.join(homedir(),filename)
18
+ const config = await readConfig(configFile)
19
+ const database = config.database
20
+
21
+ // list projects
22
+
23
+ let statement = 'select * from projects;';
24
+
25
+ let projects = await db.query(database,statement)
26
+
27
+ for (const project of projects) {
28
+ console.log(project.id, project.name)
29
+ }
30
+
31
+ // Select the project
32
+ let project_id
33
+
34
+ let text = 'Enter the project id: '
35
+ while (!project_id) {
36
+ project_id = prompt(text)
37
+
38
+ // validate the response
39
+ if (!projects.find(x => (x.id == project_id))) {
40
+ console.error('Invalid project id')
41
+ project_id = null
42
+ }
43
+ }
44
+
45
+
46
+ console.log(project_id)
47
+
48
+ // Add new sprint
49
+
50
+ let sprints = await db.query(database,`select id, sprint_name AS name from sprints where project_id = ${project_id};`)
51
+
52
+ // FIXME - sprint_name isn't being displayed because the function is looking for name
53
+ console.log('If your sprint is not displayed, you will have to manually add it.')
54
+ let sprintId = promptForInput(sprints,'Enter the sprint id: ')
55
+
56
+ // enter outcomes
57
+ let sprintOutcomes = []
58
+ let outcomes = await db.query(database,'select * from outcomes;')
59
+ for (const outcome of outcomes) {
60
+ // TODO Fix output formatting
61
+ console.log(outcome.id, outcome.name, outcome.description)
62
+ let issue_count = prompt('Enter the number of stories or a blank line to skip: ')
63
+ if (issue_count == '' ) {
64
+ continue
65
+ }
66
+ let story_points = prompt('Enter the number of story points: ')
67
+ let notes = prompt('Enter any notes: ')
68
+
69
+ sprintOutcomes.push({
70
+ sprint_id: sprintId,
71
+ sprint_name: sprints.filter(x => x.id == sprintId)[0].name,
72
+ outcome_id: outcome.id,
73
+ outcome_name: outcome.name,
74
+ outcome_description: outcome.description,
75
+ issue_count,
76
+ story_points,
77
+ notes
78
+ })
79
+ }
80
+ // validate sprint_outcomes
81
+ display.sprintOutcomes(sprintOutcomes)
82
+
83
+ // Save outcomes to DB
84
+ // Remove properties not in the table
85
+ for (const outcome of sprintOutcomes) {
86
+ delete outcome.sprint_name
87
+ delete outcome.outcome_name
88
+ delete outcome.outcome_description
89
+ }
90
+ let result = await db.insert(database,'sprint_outcomes',sprintOutcomes,['sprint_id','outcome_id'])
91
+ }
92
+
93
+ module.exports = {
94
+ command,
95
+ desc,
96
+ builder,
97
+ handler
98
+ }
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {homedir} = require('os');
4
+ const { database: db } = require('helpers')
5
+ const { prompt } = require('../utils/input.js')
6
+ const {readConfig} = require('../utils/readConfig.js')
7
+ const display = require('../utils/display.js')
8
+ const path = require('path');
9
+
10
+ const command = 'addProject'
11
+ const desc = 'Prompts to add a new project to the database'
12
+ const builder = {}
13
+
14
+ const handler = async function () {
15
+
16
+ const filename = 'sprintTracker.json'
17
+ const configFile = path.join(homedir(),filename)
18
+ const config = await readConfig(configFile)
19
+ const database = config.database
20
+
21
+ // Get new project information from user
22
+ let name = prompt('Enter the Project Name: ')
23
+ let pm_name = prompt('Enter the PM Name: ')
24
+ let pm_email = prompt('Enter the PM Email: ')
25
+ let client = prompt('Enter the Client: ')
26
+ let project_number = prompt('Enter the Project Number: ')
27
+ let start_date = prompt('Enter the start date (YYYY-MM-DD): ')
28
+ let end_date = prompt('Enter the end date (YYYY-MM-DD): ')
29
+
30
+
31
+ let project = {
32
+ name,
33
+ pm_name,
34
+ pm_email,
35
+ client,
36
+ project_number,
37
+ start_date,
38
+ end_date
39
+ }
40
+
41
+ // Save project to DB
42
+ let result = await db.insert(database,'projects',[project])
43
+
44
+ project.id = result[0].lastID
45
+ display.project(project)
46
+ }
47
+
48
+ module.exports = {
49
+ command,
50
+ desc,
51
+ builder,
52
+ handler
53
+ }
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {homedir} = require('os');
4
+ const { database: db } = require('helpers')
5
+ const { prompt } = require('../utils/input.js')
6
+ const {readConfig} = require('../utils/readConfig.js')
7
+ const display = require('../utils/display.js')
8
+ const path = require('path');
9
+
10
+ const command = 'addSprint'
11
+ const desc = 'Prompts to add a new sprint to an existing project'
12
+ const builder = {}
13
+
14
+ const handler = async function () {
15
+
16
+ const filename = 'sprintTracker.json'
17
+ const configFile = path.join(homedir(),filename)
18
+ const config = await readConfig(configFile)
19
+ const database = config.database
20
+
21
+ // list projects
22
+
23
+ let statement = 'select * from projects;';
24
+
25
+ let projects = await db.query(database,statement)
26
+
27
+ for (const project of projects) {
28
+ console.log(project.id, project.name)
29
+ }
30
+
31
+ // Select the project
32
+ let project_id
33
+
34
+ let text = 'Enter the project id: '
35
+ while (!project_id) {
36
+ project_id = prompt(text)
37
+
38
+ // validate the response
39
+ if (!projects.find(x => (x.id == project_id))) {
40
+ console.error('Invalid project id')
41
+ project_id = null
42
+ }
43
+ }
44
+
45
+
46
+ console.log(project_id)
47
+
48
+ // Get new sprint information from user
49
+
50
+ let sprint_name = prompt('Enter the sprint name: ')
51
+ let start_date = prompt('Enter the sprint start date: ')
52
+ let end_date = prompt('Enter the sprint end date: ')
53
+ let non_working_days = prompt('Enter the number of non working days: ')
54
+ let developers = prompt('Enter the number of developers assigned: ')
55
+ let developer_pto_days = prompt('Enter the total number of developer PTO days: ')
56
+
57
+ let sprint = {
58
+ sprint_name,
59
+ project_id,
60
+ start_date,
61
+ end_date,
62
+ non_working_days,
63
+ developers,
64
+ developer_pto_days
65
+ }
66
+
67
+ // Save outcomes to DB
68
+ let result = await db.insert(database,'sprints',[sprint])
69
+ let sprintId = result[0].lastID
70
+
71
+ // Prompt for commitment
72
+ let sprintOutcomes = []
73
+ let outcomes = await db.query(database,`select * from outcomes where name = 'Commitment';`)
74
+ for (const outcome of outcomes) {
75
+ // TODO Fix output formatting
76
+ console.log(outcome.id, outcome.name, outcome.description)
77
+ let issue_count = prompt('Enter the number of stories or a blank line to skip: ')
78
+ if (issue_count == '' ) {
79
+ continue
80
+ }
81
+ let story_points = prompt('Enter the number of story points: ')
82
+ let notes = prompt('Enter any notes: ')
83
+
84
+ sprintOutcomes.push({
85
+ sprint_id: sprintId,
86
+ sprint_name,
87
+ outcome_id: outcome.id,
88
+ outcome_name: outcome.name,
89
+ outcome_description: outcome.description,
90
+ issue_count,
91
+ story_points,
92
+ notes
93
+ })
94
+ }
95
+ // validate sprint_outcomes
96
+
97
+ display.sprintOutcomes(sprintOutcomes)
98
+
99
+ if (sprintOutcomes.length != 0) {
100
+ // Save outcomes to DB
101
+ // Remove properties not in the table
102
+ for (const outcome of sprintOutcomes) {
103
+ delete outcome.sprint_name
104
+ delete outcome.outcome_name
105
+ delete outcome.outcome_description
106
+ }
107
+ result = await db.insert(database,'sprint_outcomes',sprintOutcomes,['sprint_id','outcome_id'])
108
+ }
109
+
110
+ }
111
+
112
+ module.exports = {
113
+ command,
114
+ desc,
115
+ builder,
116
+ handler
117
+ }
package/cmds/config.js ADDED
@@ -0,0 +1,33 @@
1
+ const { homedir } = require('os');
2
+ const { createConfig } = require('../utils/readConfig.js')
3
+ const { helpers } = require('helpers')
4
+ const path = require('path');
5
+
6
+
7
+ const command = 'config'
8
+ const desc = 'Creates a config file, prompting for required inputs'
9
+ const builder = {}
10
+
11
+ const handler = async function () {
12
+
13
+ const filename = 'sprintTracker.json'
14
+ const configFile = path.join(homedir(), filename)
15
+
16
+ if (helpers.fileExists(configFile)) {
17
+ console.error(`Aborting. Existing configuration file ${configFile} found to not overwrite your values.`)
18
+ process.exit(1)
19
+ }
20
+
21
+ const configProps = ['database']
22
+ await createConfig(configFile, configProps)
23
+ console.log(`Created ${configFile}`)
24
+ console.log(`Please edit the file and update the ${configProps} value(s)`)
25
+
26
+ }
27
+
28
+ module.exports = {
29
+ command,
30
+ desc,
31
+ builder,
32
+ handler
33
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {homedir} = require('os');
4
+ const { database: db } = require('helpers')
5
+ const { prompt,promptForInput } = require('../utils/input.js')
6
+ const {readConfig} = require('../utils/readConfig.js')
7
+ const display = require('../utils/display.js')
8
+ const path = require('path');
9
+
10
+ const command = 'getSprint'
11
+ const desc = 'Gets sprint details'
12
+ const builder = {}
13
+ const handler = async function () {
14
+
15
+ const filename = 'sprintTracker.json'
16
+ const configFile = path.join(homedir(),filename)
17
+ const config = await readConfig(configFile)
18
+ const database = config.database
19
+
20
+ // list projects
21
+
22
+ let statement = 'select * from projects;';
23
+
24
+ let projects = await db.query(database,statement)
25
+
26
+ for (const project of projects) {
27
+ console.log(project.id, project.name)
28
+ }
29
+
30
+ // Select the project
31
+ let project_id
32
+
33
+ let text = 'Enter the project id: '
34
+ while (!project_id) {
35
+ project_id = prompt(text)
36
+
37
+ // validate the response
38
+ if (!projects.find(x => (x.id == project_id))) {
39
+ console.error('Invalid project id')
40
+ project_id = null
41
+ }
42
+ }
43
+
44
+
45
+ let sprints = await db.query(database,`select id, sprint_name AS name from sprints where project_id = ${project_id};`)
46
+
47
+ // FIXME - sprint_name isn't being displayed because the function is looking for name
48
+ console.log('If your sprint is not displayed, you will have to manually add it.')
49
+ let sprintId = promptForInput(sprints,'Enter the sprint id: ')
50
+
51
+ statement = `select
52
+ p.name,
53
+ s.sprint_name,
54
+ s.start_date,
55
+ s.end_date,
56
+ o.name AS outcome_name,
57
+ so.issue_count,
58
+ so.story_points,
59
+ so.notes,
60
+ o.description
61
+
62
+ from
63
+ sprint_outcomes so
64
+ JOIN sprints s on s.id = so.sprint_id
65
+ JOIN outcomes o on o.id = so.outcome_id
66
+ JOIN projects p on p.id = s.project_id
67
+ where
68
+ -- p.id = 4
69
+ -- and sprint_name = 'ACDC 15'
70
+ p.id = ${project_id}
71
+ and s.id = ${sprintId}`
72
+
73
+ let sprintDetails = await db.query(database,statement)
74
+ // console.log(sprintDetails)
75
+ display.sprintDetails(sprintDetails)
76
+
77
+
78
+ }
79
+
80
+
81
+ module.exports = {
82
+ command,
83
+ desc,
84
+ builder,
85
+ handler
86
+ }
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { AsyncParser } = require('@json2csv/node')
4
+ const {homedir, tmpdir} = require('os');
5
+ const { database: db } = require('helpers')
6
+ const { prompt } = require('../utils/input.js')
7
+ const {readConfig} = require('helpers').config
8
+ const exec = require('child_process').exec;
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const process = require('process');
12
+
13
+ const command = 'visualizations'
14
+ const desc = 'Creates Feature Team visualizations with R. Must have docker, rstudio-tidyverse container and more installed'
15
+ const builder = {}
16
+
17
+ const handler = async function () {
18
+ // Get Data from Database and write to file
19
+ console.log('Getting data')
20
+
21
+ const filename = 'sprintTracker.json'
22
+ const config = await readConfig(filename)
23
+ const database = config.database
24
+
25
+ // list projects
26
+
27
+ let statement = 'select * from projects;';
28
+
29
+ let projects = await db.query(database,statement)
30
+
31
+ for (const project of projects) {
32
+ console.log(project.id, project.name)
33
+ }
34
+
35
+ // Select the project
36
+ let project_id
37
+
38
+ let text = 'Enter the project id: '
39
+ while (!project_id) {
40
+ project_id = prompt(text)
41
+
42
+ // validate the response
43
+ if (!projects.find(x => (x.id == project_id))) {
44
+ console.error('Invalid project id')
45
+ project_id = null
46
+ }
47
+ }
48
+
49
+ let query = `
50
+ SELECT
51
+ p.name project_name,
52
+ s.id sprint_id,
53
+ s.sprint_name,
54
+ s.end_date,
55
+ case
56
+ when o.name = 'Commitment' then 'A-Commitment'
57
+ when o.name = 'Delivered' then 'B-Delivered'
58
+ else o.name
59
+ end outcome,
60
+ ifnull(so.story_points, so.issue_count) AS story_points -- accounts for unpointed bug stories
61
+
62
+ from projects p
63
+
64
+ JOIN sprints s on s.project_id = p.id
65
+ JOIN sprint_outcomes so on so.sprint_id = s.id
66
+ JOIN outcomes o on o.id = so.outcome_id
67
+
68
+ WHERE
69
+ p.id = ${project_id};
70
+ `
71
+
72
+ let outcomes = await db.query(database,query)
73
+
74
+ // Convert JSON to CSV
75
+ const opts = {};
76
+ const transformOpts = {};
77
+ const asyncOpts = {};
78
+ const parser = new AsyncParser(opts, transformOpts, asyncOpts);
79
+
80
+ const csv = await parser.parse(outcomes).promise();
81
+
82
+ // Write to file
83
+
84
+
85
+ const outputFile = path.join(tmpdir(),'outcomes.csv');
86
+ try {
87
+ fs.writeFileSync(outputFile, csv);
88
+ // file written successfully
89
+ console.log(`Data written to ${outputFile}`)
90
+ } catch (err) {
91
+ console.error(err);
92
+ process.exit(1)
93
+ }
94
+
95
+
96
+ console.log('Making visualizations')
97
+ // Run docker command
98
+ let PROJECT_DIR=path.join(homedir(),'projects')
99
+ let IMAGE='rocker/tidyverse'
100
+ const cmd = `docker run -t --rm -v ${PROJECT_DIR}:/home/rstudio/projects/ -v ${tmpdir()}:/tmp ${IMAGE} su -c 'Rscript /home/rstudio/projects/sprint-tracker/R/sprintOutcomesCli.R' rstudio`
101
+ exec(cmd, function(err, stdout, stderr) {
102
+ if (err) {
103
+ console.log(stderr);
104
+ console.log(err.message)
105
+ process.exit(err.code)
106
+ }
107
+ console.log(stdout);
108
+ });
109
+ }
110
+
111
+ module.exports = {
112
+ command,
113
+ desc,
114
+ builder,
115
+ handler
116
+ }
@@ -0,0 +1,11 @@
1
+ services:
2
+ rstudio:
3
+ image: rocker/tidyverse
4
+ ports:
5
+ - 8888:8787
6
+ volumes:
7
+ - ~/projects:/home/rstudio/projects
8
+ - ~/BurnDownStatus.db:/home/rstudio/BurnDownStatus.db
9
+ container_name: rstudio-server-sprint-tracker
10
+ environment:
11
+ PASSWORD: password
@@ -0,0 +1,10 @@
1
+ CREATE TABLE IF NOT EXISTS health (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ project STRING NOT NULL,
4
+ date DATE NOT NULL,
5
+ bug_count INTEGER DEFAULT (0),
6
+ unestimated_count INTEGER DEFAULT (0),
7
+ blocked_count INTEGER DEFAULT (0),
8
+ open_count INTEGER DEFAULT (0),
9
+ total_count INTEGER DEFAULT (0)
10
+ );
@@ -0,0 +1,42 @@
1
+ CREATE TABLE IF NOT EXISTS outcomes (
2
+ id INTEGER PRIMARY KEY,
3
+ name STRING NOT NULL,
4
+ description TEXT NOT NULL
5
+ );
6
+
7
+ CREATE TABLE IF NOT EXISTS projects (
8
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9
+ name STRING,
10
+ pm_name STRING,
11
+ pm_email STRING,
12
+ client STRING,
13
+ project_number STRING,
14
+ start_date DATE,
15
+ end_date DATE
16
+ );
17
+
18
+
19
+ CREATE TABLE IF NOT EXISTS sprint_outcomes (
20
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
21
+ sprint_id INTEGER REFERENCES sprints (id) ON DELETE CASCADE
22
+ ON UPDATE CASCADE,
23
+ outcome_id INTEGER REFERENCES outcomes (id) ON DELETE SET DEFAULT
24
+ ON UPDATE RESTRICT,
25
+ issue_count INTEGER DEFAULT (0),
26
+ story_points INTEGER DEFAULT (0),
27
+ notes TEXT
28
+ );
29
+
30
+ CREATE TABLE IF NOT EXISTS sprints (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ sprint_name STRING,
33
+ project_id INTEGER REFERENCES projects (id) ON DELETE CASCADE
34
+ ON UPDATE CASCADE,
35
+ start_date DATE,
36
+ end_date DATE,
37
+ commited_points INTEGER DEFAULT (0),
38
+ committed_stories INTEGER DEFAULT (0),
39
+ non_working_days INTEGER DEFAULT (0),
40
+ developers INTEGER DEFAULT (1),
41
+ developer_pto_days INTEGER DEFAULT (0)
42
+ );
@@ -0,0 +1,9 @@
1
+ INSERT into OUTCOMES (name,description)
2
+ VALUES
3
+ ("Commitment","Initial sprint commitment"),
4
+ ("Delivered","Work completed this sprint"),
5
+ ("Unplanned - Story","All unplanned work brought into the sprint, whether completed or not"),
6
+ ("Unplanned - Bug","Unplanned work without a pre-existing story that was deemed critical enough to add to the sprint"),
7
+ ("Descoped","Committed work, removed from the sprint"),
8
+ ("Carryover","Incomplete, started or unstarted, work from this sprint, assumed that it will carryover to the next sprint"),
9
+ ("Carryover - External","Work that was carried over due to blockers external to the scrum team. Use of this is optional, as an agile team should be fully self-sufficient.");
package/migrations.js ADDED
@@ -0,0 +1,20 @@
1
+ const sqlite3 = require('sqlite3').verbose();
2
+ let {CommandsRunner, SQLite3Driver} = require('node-db-migration');
3
+ const {homedir} = require('os');
4
+ const {readConfig} = require('./utils/readConfig.js')
5
+ const path = require('path');
6
+
7
+ const main = async () => {
8
+ const filename = 'sprintTracker.json'
9
+ const configFile = path.join(homedir(),filename)
10
+ const config = await readConfig(configFile)
11
+ const database = config.database
12
+ let db = new sqlite3.Database(database);
13
+ let migrations = new CommandsRunner({
14
+ driver: new SQLite3Driver(db),
15
+ directoryWithScripts: __dirname + '/migrations',
16
+ });
17
+ migrations.run(process.argv[2])
18
+ }
19
+
20
+ main()
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@beauraines/sprint-tracker",
3
+ "version": "0.6.1",
4
+ "description": "Database and R visualizations to track sprint performance",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "sprint-tracker": "cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "jest",
11
+ "addOutcomes": "node src/addOutcomes.js",
12
+ "addSprint": "node src/addSprint.js",
13
+ "getSprintDetails": "node src/getSprintDetails.js",
14
+ "generateSprintOutcomePlot": "sh scripts/generateSprintOutcomePlot.sh",
15
+ "database:init": "node migrations.js init",
16
+ "database:migrate": "node migrations.js migrate",
17
+ "database:upgrade": "node migrations.js migrate",
18
+ "should-release": "should-release",
19
+ "release": "standard-version",
20
+ "lint": "eslint '*/**.js'",
21
+ "lint:fix": "eslint '*/**.js' --fix"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+ssh://git@bitbucket.org/beauraines/sprint-tracker.git"
26
+ },
27
+ "author": "Beau Raines",
28
+ "license": "ISC",
29
+ "bugs": {
30
+ "url": "https://bitbucket.org/beauraines/sprint-tracker/issues"
31
+ },
32
+ "homepage": "https://bitbucket.org/beauraines/sprint-tracker#readme",
33
+ "devDependencies": {
34
+ "eslint": "^8.42.0",
35
+ "eslint-plugin-jest": "^27.2.1",
36
+ "jest": "^29.3.1"
37
+ },
38
+ "dependencies": {
39
+ "@json2csv/node": "^7.0.1",
40
+ "cli-table3": "^0.6.3",
41
+ "helpers": "npm:@beauraines/node-helpers@^2.4.0",
42
+ "node-db-migration": "^1.4.0",
43
+ "prompt-sync": "^4.2.0",
44
+ "should-release": "^1.2.0",
45
+ "sqlite": "^4.1.2",
46
+ "sqlite3": "^5.1.2",
47
+ "standard-version": "^9.5.0",
48
+ "yargs": "^17.7.1"
49
+ }
50
+ }
@@ -0,0 +1,20 @@
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
+
9
+ QUERY_ID=b18a81c9-3e3a-4dc6-87ef-140caf5bcb6a
10
+
11
+ az boards query --organization https://dev.azure.com/teamsi --id ${QUERY_ID} |
12
+ jq -r '[.[]| {team:.fields."System.AreaPath",type:.fields."System.WorkItemType",id,estimate: .fields."Microsoft.VSTS.Scheduling.StoryPoints" }]' |
13
+ json2csv -o ~/projects/backlog.csv
14
+
15
+
16
+ # PROJECT_DIR must have trailing slash
17
+ PROJECT_DIR=/Users/beauraines/projects/
18
+ IMAGE=rocker/tidyverse
19
+
20
+ docker run -it --rm -v ${PROJECT_DIR}:/home/rstudio/projects/ ${IMAGE} su -c 'Rscript /home/rstudio/projects/sprint-tracker/R/backlogByTeam.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/backlogHealth.R 2>/dev/null' rstudio