Skip to content

Commit e7cda9b

Browse files
committed
Code quality and comments
1 parent 30ffc30 commit e7cda9b

File tree

1 file changed

+62
-23
lines changed

1 file changed

+62
-23
lines changed

.github/scripts/update-project-item-estimation.js

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,44 @@
1+
//
2+
// Work item cost estimation logic.
3+
// See https://github.com/macrogreg/ProjectTrackingTemplate for info.
4+
//
5+
6+
7+
// Module imports:
18
const axios = require('axios');
29

10+
11+
// Configure the lookup table for the work item cost estimates.
12+
// This is a 2D table `ESTIMATED_COST_IN_DAYS[size][risk]` with cells containing the estimated effort in
13+
// working days, given the item's Size and Risk.
14+
// This doc explains how the numbers are determined:
15+
// https://github.com/macrogreg/ProjectTrackingTemplate#estimating-work-items-and-computing-time-effort
16+
function createEstimatedCostLookupTable() {
17+
18+
function estimatedCostTableRow(low, mid, high, severe) {
19+
return {
20+
["LOW"]: low,
21+
["MID"]: mid,
22+
["HIGH"]: high,
23+
["SEVERE"]: severe
24+
};
25+
}
26+
27+
const EstimatedCostInDays = {
28+
["XS"]: estimatedCostTableRow( 0.5, 1, 1.5, 4),
29+
["S"]: estimatedCostTableRow( 2, 3, 4.5, 12),
30+
["M"]: estimatedCostTableRow( 4, 5, 7.5, 20),
31+
["L"]: estimatedCostTableRow( 7.5, 10, 15, 40),
32+
["XL"]: estimatedCostTableRow( 15, 20, 30, 80),
33+
};
34+
35+
return EstimatedCostInDays;
36+
}
37+
38+
const ESTIMATED_COST_IN_DAYS = createEstimatedCostLookupTable();
39+
40+
41+
// Read Env parameters. GH workflow calling this script should set them up.
342
function loadEnvParameters() {
443

544
const tokenTargetProjectRW = process.env.TOKEN_TARGET_PROJECT_RW;
@@ -44,36 +83,23 @@ function loadEnvParameters() {
4483
}
4584

4685

47-
function EstimatedCostTableRow(low, mid, high, severe) {
48-
return {
49-
["LOW"]: low,
50-
["MID"]: mid,
51-
["HIGH"]: high,
52-
["SEVERE"]: severe
53-
};
54-
}
55-
56-
57-
function computeEstimatedCost(size, risk) {
58-
59-
const EstimatedCostInDays = {
60-
["XS"]: EstimatedCostTableRow( 0.5, 1, 1.5, 4),
61-
["S"]: EstimatedCostTableRow( 2, 3, 4.5, 12),
62-
["M"]: EstimatedCostTableRow( 4, 5, 7.5, 20),
63-
["L"]: EstimatedCostTableRow( 7.5, 10, 15, 40),
64-
["XL"]: EstimatedCostTableRow( 15, 20, 30, 80),
65-
};
86+
// Lookup the estimated work item cost in days from the `ESTIMATED_COST_IN_DAYS` table
87+
// using the specified `size` and `risk`.
88+
// `size` comes in form: "Code (explanation)", e.g. "XS (1 ≤ day)"
89+
// `risk` comes in form: "Code: explanation", e.g. "Low: well-understood".
90+
// Codes are parsed out and used for look-ups.
91+
function lookupEstimatedCost(size, risk) {
6692

6793
const sizeKey = size?.split('(')[0].trim().toUpperCase();
6894
const riskKey = risk?.split(':')[0].trim().toUpperCase();
6995

70-
if (!(sizeKey in EstimatedCostInDays)) {
96+
if (!(sizeKey in ESTIMATED_COST_IN_DAYS)) {
7197
const errMsg = `Invalid Size specifier: original value: "${size}"; key: "${sizeKey}".`;
7298
console.log(errMsg);
7399
throw new Error(errMsg);
74100
}
75101

76-
const costTableRow = EstimatedCostInDays[sizeKey];
102+
const costTableRow = ESTIMATED_COST_IN_DAYS[sizeKey];
77103

78104
if (!(riskKey in costTableRow)) {
79105
const errMsg = `Invalid Risk specifier: original value: "${risk}"; key: "${riskKey}".`;
@@ -82,11 +108,12 @@ function computeEstimatedCost(size, risk) {
82108
}
83109

84110
const estimate = costTableRow[riskKey];
85-
// console.log(`EstimatedCostInDays[${sizeKey}][${riskKey}]: '${estimate}'`);
111+
// console.log(`ESTIMATED_COST_IN_DAYS[${sizeKey}][${riskKey}]: '${estimate}'`);
86112
return estimate;
87113
}
88114

89115

116+
// Execute a GraphGL call against the GitHub API.
90117
async function graphql(query, variables, bearerToken) {
91118

92119
const EndpointUrl = "https://api.github.com/graphql";
@@ -115,6 +142,8 @@ async function graphql(query, variables, bearerToken) {
115142
};
116143

117144

145+
// GitHub GraphGL uses entity IDs to refer to items. This function looks up the respective ID needed to run later
146+
// queries. E.g. the ID of the target project, and the ID of the estimate field.
118147
async function getFieldIds(envParams) {
119148

120149
const ownerType = envParams.varTargetProjectOwnerType.toLowerCase();
@@ -191,6 +220,9 @@ async function getFieldIds(envParams) {
191220
}
192221

193222

223+
// Gets all project work items. Uses paging (100 batches).
224+
// This should work for projects with several 1000s of items, but we have not perf-tested it for projects with
225+
// several tens of thousands, let alone several hundreds of thousands of items.
194226
async function getProjectItems(projectId, tokenTargetProjectRW) {
195227
const query = `
196228
query($projectId: ID!, $cursor: String) {
@@ -263,6 +295,7 @@ async function getProjectItems(projectId, tokenTargetProjectRW) {
263295
}
264296

265297

298+
// Sets the estimate field of the specified item to the specified value.
266299
async function updateDaysEstimate(projectId, itemId, fieldId, newValue, tokenTargetProjectRW) {
267300

268301
const mutation = `
@@ -292,6 +325,11 @@ async function updateDaysEstimate(projectId, itemId, fieldId, newValue, tokenTar
292325
}
293326

294327

328+
// Read Env params, get IDs to use for GraphQL lookups, get all work items from the target project.
329+
// For each work item:
330+
// Get `Size` and `Risk`, compute the corresponding `Days Estimate` that the item should have;
331+
// Get the actual `Days Estimate` set on the item. If it differs from what it should be, update accordingly;
332+
// Handle counts, totals, errors.
295333
async function main() {
296334

297335
const envParams = loadEnvParameters();
@@ -331,7 +369,7 @@ async function main() {
331369
continue;
332370
}
333371

334-
const estimate = computeEstimatedCost(size, risk);
372+
const estimate = lookupEstimatedCost(size, risk);
335373
console.log(`Computed Estimate = '${estimate ?? "<not available>"}'.`);
336374

337375
const existingEstimate = fields.find(f => f.field?.name.toLowerCase() === "days estimate")?.number;
@@ -366,6 +404,7 @@ async function main() {
366404
}
367405

368406

407+
// Top-scope main function invocation:
369408
main().catch(err => {
370409
console.error("Script failed:", err);
371410
process.exit(1);

0 commit comments

Comments
 (0)