@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,88 @@
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
+ let sprints = await db.query(database,`select id, sprint_name AS name from sprints where project_id = ${project_id};`)
38
+
39
+ // FIXME - sprint_name isn't being displayed because the function is looking for name
40
+ console.log('If your sprint is not displayed, you will have to manually add it.')
41
+ let sprintId = promptForInput(sprints,'Enter the sprint id: ')
42
+
43
+ statement = `select
44
+ p.name,
45
+ s.sprint_name,
46
+ s.start_date,
47
+ s.end_date,
48
+ o.name AS outcome_name,
49
+ so.issue_count,
50
+ so.story_points,
51
+ so.notes,
52
+ o.description
53
+
54
+ from
55
+ sprint_outcomes so
56
+ JOIN sprints s on s.id = so.sprint_id
57
+ JOIN outcomes o on o.id = so.outcome_id
58
+ JOIN projects p on p.id = s.project_id
59
+ where
60
+ -- p.id = 4
61
+ -- and sprint_name = 'ACDC 15'
62
+ p.id = ${project_id}
63
+ and s.id = ${sprintId}`
64
+
65
+ let sprintDetails = await db.query(database,statement)
66
+ console.log(sprintDetails)
67
+
68
+
69
+ }
70
+
71
+ main()
72
+
73
+ function promptForInput(items,promptText) {
74
+ let id
75
+ for (const item of items) {
76
+ console.log(item.id, item.name)
77
+ }
78
+ while (!id) {
79
+ id = prompt(promptText)
80
+
81
+ // validate the response
82
+ if (!items.find(x => (x.id == id))) {
83
+ console.error('Invalid id')
84
+ id = null
85
+ }
86
+ }
87
+ return id
88
+ }
@@ -0,0 +1,68 @@
1
+ const Table = require('cli-table3');
2
+
3
+ /**
4
+ * Displays the sprint outcomes. A sprint outcome is a
5
+ *
6
+ * { sprint_id, outcome_id, issue_count, story_points, notes }
7
+ *
8
+ *
9
+ * @param {Array} outcomes the array of sprint outcome objects
10
+ */
11
+ function sprintOutcomes(outcomes) {
12
+ let table = new Table({
13
+ head: ['Sprint ID', 'Sprint Name','Outcome Name','Issue Count','Story Points','Notes']
14
+ });
15
+ for (const outcome of outcomes) {
16
+ table.push([
17
+ outcome.sprint_id,
18
+ outcome.sprint_name,
19
+ outcome.outcome_name,
20
+ outcome.issue_count,
21
+ outcome.story_points,
22
+ outcome.notes
23
+ ])
24
+ }
25
+
26
+ console.log(table.toString());
27
+
28
+
29
+ }
30
+
31
+ function sprintDetails(details) {
32
+ let table = new Table({
33
+ head: ['Name', 'Sprint Name','Start Date','End Date', 'Outcome', 'Issue Count','Story Points','Notes']
34
+ })
35
+ for (const detail of details) {
36
+ table.push([
37
+ detail.name,
38
+ detail.sprint_name,
39
+ detail.start_date,
40
+ detail.end_date,
41
+ detail.outcome_name,
42
+ detail.issue_count,
43
+ detail.story_points,
44
+ detail.notes
45
+ ])
46
+ }
47
+ console.log(table.toString());
48
+
49
+ }
50
+
51
+ function project(project) {
52
+ let table = new Table({})
53
+ for (const key in project) {
54
+ if (Object.hasOwnProperty.call(project, key)) {
55
+ const element = project[key];
56
+ table.push({
57
+ [key]: element
58
+ })
59
+ }
60
+ }
61
+ console.log(table.toString());
62
+ }
63
+
64
+ module.exports = {
65
+ project,
66
+ sprintDetails,
67
+ sprintOutcomes
68
+ }
package/utils/input.js ADDED
@@ -0,0 +1,24 @@
1
+ const prompt = require('prompt-sync')({sigint:true});
2
+
3
+ function promptForInput(items,promptText) {
4
+ let id
5
+ for (const item of items) {
6
+ console.log(item.id, item.name)
7
+ }
8
+ while (!id) {
9
+ id = prompt(promptText)
10
+
11
+ // validate the response
12
+ if (!items.find(x => (x.id == id))) {
13
+ console.error('Invalid id')
14
+ id = null
15
+ }
16
+ }
17
+ return id
18
+ }
19
+
20
+
21
+ module.exports = {
22
+ prompt,
23
+ promptForInput
24
+ }
@@ -0,0 +1,74 @@
1
+ const fs = require('fs');
2
+ const { helpers, database } = require('helpers')
3
+
4
+ /**
5
+ * Reads the specified config file
6
+ *
7
+ * @param {string} configFile the fully qualified path and file name for the config file
8
+ * @returns {object} the configuration object
9
+ */
10
+ readConfig = async (configFile) => {
11
+
12
+ let config
13
+ if ( await helpers.fileExists(configFile) ) {
14
+ config = fs.readFileSync(configFile,
15
+ { encoding: 'utf8', flag: 'r' });
16
+ config = JSON.parse(config);
17
+ } else {
18
+ console.error(`Config file not found. You must create one using the config command`)
19
+ process.exit(1);
20
+ }
21
+
22
+ return config
23
+ }
24
+
25
+ /**
26
+ * Validates the config file to ensure that it has all of the required properties specified. This
27
+ * is only schema validation, it does not check if the properties are valid values or data types.
28
+ *
29
+ * @param {string} configFile the fully qualified path and file name for the config file
30
+ * @param {Array} configProps an array of properties that defines a valid config file
31
+ *
32
+ * @throws {Configuration file not found}
33
+ * @throws {Invalid configuration file}
34
+ * @returns {boolean}
35
+ */
36
+ validateConfig = async (configFile, configProps) => {
37
+ if (! fs.existsSync(configFile) ) {
38
+ throw new Error('Configuration file not found')
39
+ }
40
+ let config
41
+ config = fs.readFileSync(configFile,
42
+ { encoding: 'utf8', flag: 'r' });
43
+ config = JSON.parse(config);
44
+ // Check for properties
45
+ let validConfig = true
46
+ for (key of configProps) {
47
+ validConfig = config[key] ? true : false
48
+ }
49
+
50
+ if (!validConfig) {
51
+ throw Error('Invalid configuration file')
52
+ }
53
+ return validConfig
54
+ }
55
+
56
+ /**
57
+ * Creates a boilerplate config file
58
+ *
59
+ * @param {string} configFile the fully qualified path and file name for the config file
60
+ * @param {Array} configProps an array of properties that defines a valid config file
61
+ *
62
+ */
63
+ createConfig = async (configFile,configProps) => {
64
+ let config = {}
65
+ for (key of configProps) {
66
+ config[key] = ''
67
+ }
68
+ fs.writeFileSync(configFile,JSON.stringify(config))
69
+ }
70
+
71
+ module.exports = {
72
+ readConfig,
73
+ createConfig
74
+ }