@muzzamil-khan/redmine-agent-mcp 1.0.0 → 1.2.0
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/dist/config/env.d.ts +2 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +8 -0
- package/dist/config/env.js.map +1 -1
- package/dist/mcp/server.js +719 -147
- package/dist/mcp/server.js.map +1 -1
- package/dist/services/assigneeDirectory.d.ts +34 -0
- package/dist/services/assigneeDirectory.d.ts.map +1 -0
- package/dist/services/assigneeDirectory.js +138 -0
- package/dist/services/assigneeDirectory.js.map +1 -0
- package/dist/services/googleSheets.d.ts +32 -0
- package/dist/services/googleSheets.d.ts.map +1 -0
- package/dist/services/googleSheets.js +109 -0
- package/dist/services/googleSheets.js.map +1 -0
- package/dist/services/redmine.d.ts +72 -0
- package/dist/services/redmine.d.ts.map +1 -1
- package/dist/services/redmine.js +141 -0
- package/dist/services/redmine.js.map +1 -1
- package/dist/services/redmineConfig.d.ts +90 -0
- package/dist/services/redmineConfig.d.ts.map +1 -0
- package/dist/services/redmineConfig.js +300 -0
- package/dist/services/redmineConfig.js.map +1 -0
- package/dist/spreadsheet/batchColumnMapping.d.ts +93 -0
- package/dist/spreadsheet/batchColumnMapping.d.ts.map +1 -0
- package/dist/spreadsheet/batchColumnMapping.js +666 -0
- package/dist/spreadsheet/batchColumnMapping.js.map +1 -0
- package/dist/spreadsheet/batchCreationTypes.d.ts +61 -0
- package/dist/spreadsheet/batchCreationTypes.d.ts.map +1 -0
- package/dist/spreadsheet/batchCreationTypes.js +18 -0
- package/dist/spreadsheet/batchCreationTypes.js.map +1 -0
- package/dist/spreadsheet/batchIssueConfig.d.ts +26 -0
- package/dist/spreadsheet/batchIssueConfig.d.ts.map +1 -0
- package/dist/spreadsheet/batchIssueConfig.js +106 -0
- package/dist/spreadsheet/batchIssueConfig.js.map +1 -0
- package/dist/spreadsheet/batchPreviewFormat.d.ts +41 -0
- package/dist/spreadsheet/batchPreviewFormat.d.ts.map +1 -0
- package/dist/spreadsheet/batchPreviewFormat.js +194 -0
- package/dist/spreadsheet/batchPreviewFormat.js.map +1 -0
- package/dist/spreadsheet/batchSheetHeuristics.d.ts +13 -0
- package/dist/spreadsheet/batchSheetHeuristics.d.ts.map +1 -0
- package/dist/spreadsheet/batchSheetHeuristics.js +30 -0
- package/dist/spreadsheet/batchSheetHeuristics.js.map +1 -0
- package/dist/spreadsheet/bulkUpdateExtract.d.ts +14 -0
- package/dist/spreadsheet/bulkUpdateExtract.d.ts.map +1 -0
- package/dist/spreadsheet/bulkUpdateExtract.js +83 -0
- package/dist/spreadsheet/bulkUpdateExtract.js.map +1 -0
- package/dist/spreadsheet/csvUtils.d.ts +14 -0
- package/dist/spreadsheet/csvUtils.d.ts.map +1 -0
- package/dist/spreadsheet/csvUtils.js +113 -0
- package/dist/spreadsheet/csvUtils.js.map +1 -0
- package/dist/spreadsheet/extractRawBatchSheet.d.ts +14 -0
- package/dist/spreadsheet/extractRawBatchSheet.d.ts.map +1 -0
- package/dist/spreadsheet/extractRawBatchSheet.js +33 -0
- package/dist/spreadsheet/extractRawBatchSheet.js.map +1 -0
- package/dist/spreadsheet/featureRow.d.ts +15 -0
- package/dist/spreadsheet/featureRow.d.ts.map +1 -0
- package/dist/spreadsheet/featureRow.js +119 -0
- package/dist/spreadsheet/featureRow.js.map +1 -0
- package/dist/spreadsheet/parseBatchCreationCsv.d.ts +8 -0
- package/dist/spreadsheet/parseBatchCreationCsv.d.ts.map +1 -0
- package/dist/spreadsheet/parseBatchCreationCsv.js +22 -0
- package/dist/spreadsheet/parseBatchCreationCsv.js.map +1 -0
- package/dist/spreadsheet/parseBatchCreationCsvV1.d.ts +3 -0
- package/dist/spreadsheet/parseBatchCreationCsvV1.d.ts.map +1 -0
- package/dist/spreadsheet/parseBatchCreationCsvV1.js +226 -0
- package/dist/spreadsheet/parseBatchCreationCsvV1.js.map +1 -0
- package/dist/spreadsheet/parseBatchCreationCsvV2.d.ts +4 -0
- package/dist/spreadsheet/parseBatchCreationCsvV2.d.ts.map +1 -0
- package/dist/spreadsheet/parseBatchCreationCsvV2.js +180 -0
- package/dist/spreadsheet/parseBatchCreationCsvV2.js.map +1 -0
- package/dist/spreadsheet/redmineIssueCell.d.ts +38 -0
- package/dist/spreadsheet/redmineIssueCell.d.ts.map +1 -0
- package/dist/spreadsheet/redmineIssueCell.js +176 -0
- package/dist/spreadsheet/redmineIssueCell.js.map +1 -0
- package/dist/spreadsheet/sheetColumnMap.d.ts +38 -0
- package/dist/spreadsheet/sheetColumnMap.d.ts.map +1 -0
- package/dist/spreadsheet/sheetColumnMap.js +75 -0
- package/dist/spreadsheet/sheetColumnMap.js.map +1 -0
- package/dist/spreadsheet/sheetGridToCsv.d.ts +28 -0
- package/dist/spreadsheet/sheetGridToCsv.d.ts.map +1 -0
- package/dist/spreadsheet/sheetGridToCsv.js +88 -0
- package/dist/spreadsheet/sheetGridToCsv.js.map +1 -0
- package/dist/spreadsheet/sheetRedmineFields.d.ts +7 -0
- package/dist/spreadsheet/sheetRedmineFields.d.ts.map +1 -0
- package/dist/spreadsheet/sheetRedmineFields.js +22 -0
- package/dist/spreadsheet/sheetRedmineFields.js.map +1 -0
- package/dist/spreadsheet/sheetWriteBack.d.ts +27 -0
- package/dist/spreadsheet/sheetWriteBack.d.ts.map +1 -0
- package/dist/spreadsheet/sheetWriteBack.js +90 -0
- package/dist/spreadsheet/sheetWriteBack.js.map +1 -0
- package/dist/workflows/batchCreateIssuesWorkflow.d.ts +69 -0
- package/dist/workflows/batchCreateIssuesWorkflow.d.ts.map +1 -0
- package/dist/workflows/batchCreateIssuesWorkflow.js +1102 -0
- package/dist/workflows/batchCreateIssuesWorkflow.js.map +1 -0
- package/dist/workflows/bulkUpdateIssuesWorkflow.d.ts +21 -0
- package/dist/workflows/bulkUpdateIssuesWorkflow.d.ts.map +1 -0
- package/dist/workflows/bulkUpdateIssuesWorkflow.js +384 -0
- package/dist/workflows/bulkUpdateIssuesWorkflow.js.map +1 -0
- package/dist/workflows/createIssueWorkflow.d.ts +41 -0
- package/dist/workflows/createIssueWorkflow.d.ts.map +1 -0
- package/dist/workflows/createIssueWorkflow.js +697 -0
- package/dist/workflows/createIssueWorkflow.js.map +1 -0
- package/dist/workflows/getAllUsersWorkflow.d.ts +27 -0
- package/dist/workflows/getAllUsersWorkflow.d.ts.map +1 -0
- package/dist/workflows/getAllUsersWorkflow.js +48 -0
- package/dist/workflows/getAllUsersWorkflow.js.map +1 -0
- package/dist/workflows/issueCreationHelpers.d.ts +54 -0
- package/dist/workflows/issueCreationHelpers.d.ts.map +1 -0
- package/dist/workflows/issueCreationHelpers.js +272 -0
- package/dist/workflows/issueCreationHelpers.js.map +1 -0
- package/dist/workflows/logTimeWorkflow.d.ts +6 -1
- package/dist/workflows/logTimeWorkflow.d.ts.map +1 -1
- package/dist/workflows/logTimeWorkflow.js +139 -20
- package/dist/workflows/logTimeWorkflow.js.map +1 -1
- package/dist/workflows/sheetToCsvWorkflow.d.ts +15 -0
- package/dist/workflows/sheetToCsvWorkflow.d.ts.map +1 -0
- package/dist/workflows/sheetToCsvWorkflow.js +65 -0
- package/dist/workflows/sheetToCsvWorkflow.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { BATCH_TRACKER_BY_LEVEL, FEATURE_ROOT_NODE_ID, } from "./batchCreationTypes.js";
|
|
2
|
+
import { findFeatureSheetRow, readFeatureRowExistingIdFromSheet, } from "./featureRow.js";
|
|
3
|
+
import { buildSheetWriteBackTarget, extractFeatureTitle, findV1HeaderRowIndex, mapV1Columns, readExistingRedmineIdFromRow, } from "./sheetColumnMap.js";
|
|
4
|
+
import { hoursForSubtaskOnRow, hoursForUserStoryOnRow, isAggregatingSheetRow, } from "./batchSheetHeuristics.js";
|
|
5
|
+
import { normalizeHeaderCell, parseCsvRows, parseHours, splitSubjectAndDescription, } from "./csvUtils.js";
|
|
6
|
+
import { normalizeSheetPriority, trackerForSheetRow, } from "./sheetRedmineFields.js";
|
|
7
|
+
function sheetMetaNotes(row, cols) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
if (cols.status >= 0) {
|
|
10
|
+
const v = cell(row, cols.status);
|
|
11
|
+
if (v)
|
|
12
|
+
parts.push(`Status: ${v}`);
|
|
13
|
+
}
|
|
14
|
+
return parts.join("\n");
|
|
15
|
+
}
|
|
16
|
+
function cell(row, index) {
|
|
17
|
+
return (row[index] ?? "").trim();
|
|
18
|
+
}
|
|
19
|
+
function descriptionFromParts(parts) {
|
|
20
|
+
const sections = [];
|
|
21
|
+
const { subject, description: bodyRest } = splitSubjectAndDescription(parts.body);
|
|
22
|
+
if (bodyRest)
|
|
23
|
+
sections.push(bodyRest);
|
|
24
|
+
else if (parts.body.trim() && parts.body.trim() !== subject) {
|
|
25
|
+
sections.push(parts.body.trim());
|
|
26
|
+
}
|
|
27
|
+
if (parts.comments.trim())
|
|
28
|
+
sections.push(parts.comments.trim());
|
|
29
|
+
if (parts.system.trim())
|
|
30
|
+
sections.push(`System: ${parts.system.trim()}`);
|
|
31
|
+
if (parts.meta?.trim())
|
|
32
|
+
sections.push(parts.meta.trim());
|
|
33
|
+
return sections.join("\n\n").trim();
|
|
34
|
+
}
|
|
35
|
+
function makeNode(params) {
|
|
36
|
+
const { subject } = splitSubjectAndDescription(params.subjectSource);
|
|
37
|
+
const etaHours = parseHours(params.eta);
|
|
38
|
+
const issue = {
|
|
39
|
+
nodeId: `row-${params.rowIndex}-${params.level}`,
|
|
40
|
+
rowIndex: params.rowIndex,
|
|
41
|
+
level: params.level,
|
|
42
|
+
tracker: trackerForSheetRow(params.sheetType ?? "", params.level),
|
|
43
|
+
subject,
|
|
44
|
+
description: descriptionFromParts({
|
|
45
|
+
body: params.subjectSource,
|
|
46
|
+
comments: params.comments,
|
|
47
|
+
system: params.system,
|
|
48
|
+
...(params.meta ? { meta: params.meta } : {}),
|
|
49
|
+
}),
|
|
50
|
+
targetVersion: "",
|
|
51
|
+
assignedTo: params.assignedTo,
|
|
52
|
+
system: params.system,
|
|
53
|
+
parentNodeId: params.parentNodeId,
|
|
54
|
+
...(params.sheetType?.trim()
|
|
55
|
+
? { sheetType: params.sheetType.trim() }
|
|
56
|
+
: {}),
|
|
57
|
+
...(params.existingRedmineId !== undefined
|
|
58
|
+
? { existingRedmineId: params.existingRedmineId }
|
|
59
|
+
: {}),
|
|
60
|
+
};
|
|
61
|
+
if (etaHours !== undefined) {
|
|
62
|
+
issue.estimatedHours = etaHours;
|
|
63
|
+
}
|
|
64
|
+
const priority = normalizeSheetPriority(params.sheetPriority ?? "");
|
|
65
|
+
if (priority)
|
|
66
|
+
issue.priority = priority;
|
|
67
|
+
return issue;
|
|
68
|
+
}
|
|
69
|
+
export function parseBatchCreationCsvV1(content) {
|
|
70
|
+
const rows = parseCsvRows(content);
|
|
71
|
+
const parseWarnings = [];
|
|
72
|
+
const headerRowIndex = findV1HeaderRowIndex(rows);
|
|
73
|
+
if (headerRowIndex < 0) {
|
|
74
|
+
return {
|
|
75
|
+
format: "v1",
|
|
76
|
+
scopeNotes: "",
|
|
77
|
+
headerRowIndex: -1,
|
|
78
|
+
issues: [],
|
|
79
|
+
parseWarnings: [
|
|
80
|
+
'Could not find v1 header row with columns "Ticket", "Task", "Sub Tasks".',
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const featureTitle = extractFeatureTitle(rows, headerRowIndex);
|
|
85
|
+
const scopeRows = rows.slice(0, headerRowIndex);
|
|
86
|
+
const scopeNotes = featureTitle
|
|
87
|
+
? `Feature: ${featureTitle}`
|
|
88
|
+
: scopeRows
|
|
89
|
+
.map((r) => r.map((c) => c.trim()).filter(Boolean).join(" "))
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join("\n");
|
|
92
|
+
const headerRow = rows[headerRowIndex] ?? [];
|
|
93
|
+
const featureSheetRow = findFeatureSheetRow(rows, headerRowIndex);
|
|
94
|
+
const existingFeatureId = featureTitle
|
|
95
|
+
? readFeatureRowExistingIdFromSheet(rows, featureSheetRow, headerRow)
|
|
96
|
+
: undefined;
|
|
97
|
+
const issues = [];
|
|
98
|
+
const featureParentId = featureTitle ? FEATURE_ROOT_NODE_ID : null;
|
|
99
|
+
if (featureTitle) {
|
|
100
|
+
issues.push({
|
|
101
|
+
nodeId: FEATURE_ROOT_NODE_ID,
|
|
102
|
+
rowIndex: featureSheetRow,
|
|
103
|
+
level: "feature",
|
|
104
|
+
tracker: BATCH_TRACKER_BY_LEVEL.feature,
|
|
105
|
+
subject: featureTitle,
|
|
106
|
+
description: "",
|
|
107
|
+
targetVersion: "",
|
|
108
|
+
assignedTo: "",
|
|
109
|
+
system: "",
|
|
110
|
+
parentNodeId: null,
|
|
111
|
+
...(existingFeatureId !== undefined
|
|
112
|
+
? { existingRedmineId: existingFeatureId }
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const cols = mapV1Columns(headerRow);
|
|
117
|
+
const sheetWriteBack = buildSheetWriteBackTarget(headerRow);
|
|
118
|
+
let currentUserStoryNodeId = null;
|
|
119
|
+
let ticketGroupContext = "";
|
|
120
|
+
for (let i = headerRowIndex + 1; i < rows.length; i++) {
|
|
121
|
+
const row = rows[i] ?? [];
|
|
122
|
+
const ticket = cols.ticket >= 0 ? cell(row, cols.ticket) : "";
|
|
123
|
+
const task = cell(row, cols.userStory);
|
|
124
|
+
const sub = cell(row, cols.subtask);
|
|
125
|
+
const eta = cols.hours >= 0 ? cell(row, cols.hours) : "";
|
|
126
|
+
const system = cols.system >= 0 ? cell(row, cols.system) : "";
|
|
127
|
+
const comments = cols.comments >= 0 ? cell(row, cols.comments) : "";
|
|
128
|
+
const assignedTo = cols.assignedTo >= 0 ? cell(row, cols.assignedTo) : "";
|
|
129
|
+
const meta = sheetMetaNotes(row, cols);
|
|
130
|
+
const sheetType = cols.sheetType >= 0 ? cell(row, cols.sheetType) : "";
|
|
131
|
+
const sheetPriority = cols.priority >= 0 ? cell(row, cols.priority) : "";
|
|
132
|
+
const rowRedmineId = task || sub
|
|
133
|
+
? readExistingRedmineIdFromRow(row, cols)
|
|
134
|
+
: undefined;
|
|
135
|
+
if (!ticket && !task && !sub)
|
|
136
|
+
continue;
|
|
137
|
+
if (ticket && !task && !sub) {
|
|
138
|
+
currentUserStoryNodeId = null;
|
|
139
|
+
ticketGroupContext = ticket;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (isAggregatingSheetRow({ userStoryText: task, subtaskText: sub })) {
|
|
143
|
+
parseWarnings.push(`Row ${i + 1}: Rollup/summary row skipped (not created on Redmine).`);
|
|
144
|
+
currentUserStoryNodeId = null;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const subHoursCell = hoursForSubtaskOnRow(task, eta);
|
|
148
|
+
const storyHoursCell = hoursForUserStoryOnRow(sub, eta);
|
|
149
|
+
if (task) {
|
|
150
|
+
const groupPrefix = ticketGroupContext
|
|
151
|
+
? `${ticketGroupContext}\n\n`
|
|
152
|
+
: "";
|
|
153
|
+
ticketGroupContext = "";
|
|
154
|
+
const body = groupPrefix + task;
|
|
155
|
+
const node = makeNode({
|
|
156
|
+
rowIndex: i + 1,
|
|
157
|
+
level: "userStory",
|
|
158
|
+
subjectSource: task,
|
|
159
|
+
comments,
|
|
160
|
+
system,
|
|
161
|
+
eta: storyHoursCell,
|
|
162
|
+
assignedTo,
|
|
163
|
+
parentNodeId: featureParentId,
|
|
164
|
+
meta,
|
|
165
|
+
sheetType,
|
|
166
|
+
sheetPriority,
|
|
167
|
+
...(rowRedmineId !== undefined && !sub
|
|
168
|
+
? { existingRedmineId: rowRedmineId }
|
|
169
|
+
: {}),
|
|
170
|
+
});
|
|
171
|
+
if (groupPrefix) {
|
|
172
|
+
node.description = descriptionFromParts({
|
|
173
|
+
body,
|
|
174
|
+
comments,
|
|
175
|
+
system: node.system,
|
|
176
|
+
...(meta ? { meta } : {}),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (!node.subject) {
|
|
180
|
+
parseWarnings.push(`Row ${i + 1}: Task has no subject text.`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
issues.push(node);
|
|
184
|
+
currentUserStoryNodeId = node.nodeId;
|
|
185
|
+
}
|
|
186
|
+
if (sub) {
|
|
187
|
+
if (!currentUserStoryNodeId) {
|
|
188
|
+
parseWarnings.push(`Row ${i + 1}: Sub Task skipped — no User Story (Task column) above.`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const subRowId = readExistingRedmineIdFromRow(row, cols);
|
|
192
|
+
const node = makeNode({
|
|
193
|
+
rowIndex: i + 1,
|
|
194
|
+
level: "subtask",
|
|
195
|
+
subjectSource: sub,
|
|
196
|
+
comments,
|
|
197
|
+
system,
|
|
198
|
+
eta: subHoursCell,
|
|
199
|
+
assignedTo,
|
|
200
|
+
parentNodeId: currentUserStoryNodeId,
|
|
201
|
+
meta,
|
|
202
|
+
sheetType,
|
|
203
|
+
sheetPriority,
|
|
204
|
+
...(subRowId !== undefined
|
|
205
|
+
? { existingRedmineId: subRowId }
|
|
206
|
+
: {}),
|
|
207
|
+
});
|
|
208
|
+
if (!node.subject) {
|
|
209
|
+
parseWarnings.push(`Row ${i + 1}: Sub Task has no subject text.`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
issues.push(node);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
format: "v1",
|
|
217
|
+
scopeNotes,
|
|
218
|
+
headerRowIndex: headerRowIndex + 1,
|
|
219
|
+
sheetColumns: cols,
|
|
220
|
+
...(sheetWriteBack ? { sheetWriteBack } : {}),
|
|
221
|
+
issues,
|
|
222
|
+
parseWarnings,
|
|
223
|
+
...(featureTitle ? { featureTitle } : {}),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=parseBatchCreationCsvV1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseBatchCreationCsvV1.js","sourceRoot":"","sources":["../../src/spreadsheet/parseBatchCreationCsvV1.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BatchParseResult } from "./batchCreationTypes.js";
|
|
2
|
+
export { mapV2Columns, extractFeatureTitle, findV2HeaderRowIndex } from "./sheetColumnMap.js";
|
|
3
|
+
export declare function parseBatchCreationCsvV2(content: string): BatchParseResult;
|
|
4
|
+
//# sourceMappingURL=parseBatchCreationCsvV2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseBatchCreationCsvV2.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/parseBatchCreationCsvV2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAiC,MAAM,yBAAyB,CAAC;AA4B/F,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AA2D9F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAmJzE"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { BATCH_TRACKER_BY_LEVEL, FEATURE_ROOT_NODE_ID, } from "./batchCreationTypes.js";
|
|
2
|
+
import { appendCommentsToDescription, parseCsvRows, parseHours, splitSubjectAndDescription, } from "./csvUtils.js";
|
|
3
|
+
import { isAggregatingSheetRow, hoursForSubtaskOnRow, hoursForUserStoryOnRow, } from "./batchSheetHeuristics.js";
|
|
4
|
+
import { findFeatureSheetRow, readFeatureRowExistingIdFromSheet, } from "./featureRow.js";
|
|
5
|
+
import { buildSheetWriteBackTarget, extractFeatureTitle, findV2HeaderRowIndex, mapV2Columns, readExistingRedmineIdFromRow, } from "./sheetColumnMap.js";
|
|
6
|
+
export { mapV2Columns, extractFeatureTitle, findV2HeaderRowIndex } from "./sheetColumnMap.js";
|
|
7
|
+
function toLegacyV2Columns(cols) {
|
|
8
|
+
return {
|
|
9
|
+
task: cols.userStory,
|
|
10
|
+
subTasks: cols.subtask,
|
|
11
|
+
description: cols.description,
|
|
12
|
+
hours: cols.hours,
|
|
13
|
+
targetVersion: cols.targetVersion,
|
|
14
|
+
assignedTo: cols.assignedTo,
|
|
15
|
+
comments: cols.comments,
|
|
16
|
+
taskId: cols.redmineId,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function cell(row, index) {
|
|
20
|
+
if (index < 0)
|
|
21
|
+
return "";
|
|
22
|
+
return (row[index] ?? "").trim();
|
|
23
|
+
}
|
|
24
|
+
function buildIssue(params) {
|
|
25
|
+
if (!params.subject.trim())
|
|
26
|
+
return null;
|
|
27
|
+
const issue = {
|
|
28
|
+
nodeId: `row-${params.rowIndex}-${params.level}`,
|
|
29
|
+
rowIndex: params.rowIndex,
|
|
30
|
+
level: params.level,
|
|
31
|
+
tracker: BATCH_TRACKER_BY_LEVEL[params.level],
|
|
32
|
+
subject: params.subject.trim(),
|
|
33
|
+
description: appendCommentsToDescription(params.description, params.comments),
|
|
34
|
+
targetVersion: params.targetVersion.trim(),
|
|
35
|
+
assignedTo: params.assignedTo.trim(),
|
|
36
|
+
system: "",
|
|
37
|
+
parentNodeId: params.parentNodeId,
|
|
38
|
+
};
|
|
39
|
+
if (params.hours !== undefined) {
|
|
40
|
+
issue.estimatedHours = params.hours;
|
|
41
|
+
}
|
|
42
|
+
if (params.existingRedmineId !== undefined) {
|
|
43
|
+
issue.existingRedmineId = params.existingRedmineId;
|
|
44
|
+
}
|
|
45
|
+
return issue;
|
|
46
|
+
}
|
|
47
|
+
export function parseBatchCreationCsvV2(content) {
|
|
48
|
+
const rows = parseCsvRows(content);
|
|
49
|
+
const parseWarnings = [];
|
|
50
|
+
const headerRowIndex = findV2HeaderRowIndex(rows);
|
|
51
|
+
if (headerRowIndex < 0) {
|
|
52
|
+
return {
|
|
53
|
+
format: "v2",
|
|
54
|
+
scopeNotes: "",
|
|
55
|
+
headerRowIndex: -1,
|
|
56
|
+
issues: [],
|
|
57
|
+
parseWarnings: [
|
|
58
|
+
'Could not find v2 header: Task, Sub-Tasks, Description, Hours, Target Version, Assigned To, Comments / Question',
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const headerRow = rows[headerRowIndex] ?? [];
|
|
63
|
+
const cols = mapV2Columns(headerRow);
|
|
64
|
+
const sheetWriteBack = buildSheetWriteBackTarget(headerRow);
|
|
65
|
+
const featureTitle = extractFeatureTitle(rows, headerRowIndex);
|
|
66
|
+
const issues = [];
|
|
67
|
+
const featureSheetRow = findFeatureSheetRow(rows, headerRowIndex);
|
|
68
|
+
const existingFeatureId = featureTitle
|
|
69
|
+
? readFeatureRowExistingIdFromSheet(rows, featureSheetRow, headerRow)
|
|
70
|
+
: undefined;
|
|
71
|
+
if (featureTitle) {
|
|
72
|
+
const featureNode = buildIssue({
|
|
73
|
+
rowIndex: featureSheetRow,
|
|
74
|
+
level: "feature",
|
|
75
|
+
subject: featureTitle,
|
|
76
|
+
description: "",
|
|
77
|
+
targetVersion: "",
|
|
78
|
+
assignedTo: "",
|
|
79
|
+
comments: "",
|
|
80
|
+
parentNodeId: null,
|
|
81
|
+
...(existingFeatureId !== undefined
|
|
82
|
+
? { existingRedmineId: existingFeatureId }
|
|
83
|
+
: {}),
|
|
84
|
+
});
|
|
85
|
+
if (featureNode) {
|
|
86
|
+
featureNode.nodeId = FEATURE_ROOT_NODE_ID;
|
|
87
|
+
issues.push(featureNode);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
let currentUserStoryNodeId = null;
|
|
91
|
+
const featureParentId = featureTitle ? FEATURE_ROOT_NODE_ID : null;
|
|
92
|
+
/** Target version for current User Story (set on Task row, applies until next Task) */
|
|
93
|
+
let currentTargetVersion = "";
|
|
94
|
+
for (let i = headerRowIndex + 1; i < rows.length; i++) {
|
|
95
|
+
const row = rows[i] ?? [];
|
|
96
|
+
const taskCell = cell(row, cols.userStory);
|
|
97
|
+
const subCell = cell(row, cols.subtask);
|
|
98
|
+
const descriptionCol = cell(row, cols.description);
|
|
99
|
+
const hoursCol = cols.hours >= 0 ? cell(row, cols.hours) : "";
|
|
100
|
+
const targetVersionCol = cols.targetVersion >= 0 ? cell(row, cols.targetVersion) : "";
|
|
101
|
+
const assignedToCol = cols.assignedTo >= 0 ? cell(row, cols.assignedTo) : "";
|
|
102
|
+
const commentsCol = cols.comments >= 0 ? cell(row, cols.comments) : "";
|
|
103
|
+
const rowRedmineId = readExistingRedmineIdFromRow(row, cols);
|
|
104
|
+
if (!taskCell && !subCell)
|
|
105
|
+
continue;
|
|
106
|
+
if (isAggregatingSheetRow({ userStoryText: taskCell, subtaskText: subCell })) {
|
|
107
|
+
parseWarnings.push(`Row ${i + 1}: Rollup/summary row skipped (not created on Redmine).`);
|
|
108
|
+
currentUserStoryNodeId = null;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (taskCell) {
|
|
112
|
+
if (targetVersionCol) {
|
|
113
|
+
currentTargetVersion = targetVersionCol;
|
|
114
|
+
}
|
|
115
|
+
const { subject, description } = splitSubjectAndDescription(taskCell);
|
|
116
|
+
const storyHoursCell = hoursForUserStoryOnRow(subCell, hoursCol);
|
|
117
|
+
const hours = storyHoursCell ? parseHours(storyHoursCell) : undefined;
|
|
118
|
+
const node = buildIssue({
|
|
119
|
+
rowIndex: i + 1,
|
|
120
|
+
level: "userStory",
|
|
121
|
+
subject,
|
|
122
|
+
description,
|
|
123
|
+
...(hours !== undefined ? { hours } : {}),
|
|
124
|
+
targetVersion: currentTargetVersion,
|
|
125
|
+
assignedTo: "",
|
|
126
|
+
comments: subCell ? "" : commentsCol,
|
|
127
|
+
parentNodeId: featureParentId,
|
|
128
|
+
...(rowRedmineId !== undefined && !subCell
|
|
129
|
+
? { existingRedmineId: rowRedmineId }
|
|
130
|
+
: {}),
|
|
131
|
+
});
|
|
132
|
+
if (!node) {
|
|
133
|
+
parseWarnings.push(`Row ${i + 1}: Task has no subject (first line).`);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
issues.push(node);
|
|
137
|
+
currentUserStoryNodeId = node.nodeId;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (subCell) {
|
|
141
|
+
if (!currentUserStoryNodeId) {
|
|
142
|
+
parseWarnings.push(`Row ${i + 1}: Sub-Task skipped — no User Story (Task column) above.`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const { subject } = splitSubjectAndDescription(subCell);
|
|
146
|
+
const subHoursCell = hoursForSubtaskOnRow(taskCell, hoursCol);
|
|
147
|
+
const hours = subHoursCell ? parseHours(subHoursCell) : undefined;
|
|
148
|
+
const subRowId = readExistingRedmineIdFromRow(row, cols);
|
|
149
|
+
const node = buildIssue({
|
|
150
|
+
rowIndex: i + 1,
|
|
151
|
+
level: "subtask",
|
|
152
|
+
subject,
|
|
153
|
+
description: descriptionCol,
|
|
154
|
+
...(hours !== undefined ? { hours } : {}),
|
|
155
|
+
targetVersion: "",
|
|
156
|
+
assignedTo: assignedToCol,
|
|
157
|
+
comments: commentsCol,
|
|
158
|
+
parentNodeId: currentUserStoryNodeId,
|
|
159
|
+
...(subRowId !== undefined ? { existingRedmineId: subRowId } : {}),
|
|
160
|
+
});
|
|
161
|
+
if (!node) {
|
|
162
|
+
parseWarnings.push(`Row ${i + 1}: Sub-Task has no subject.`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
issues.push(node);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
format: "v2",
|
|
170
|
+
scopeNotes: featureTitle ? `Feature: ${featureTitle}` : "",
|
|
171
|
+
headerRowIndex: headerRowIndex + 1,
|
|
172
|
+
sheetColumns: cols,
|
|
173
|
+
...(sheetWriteBack ? { sheetWriteBack } : {}),
|
|
174
|
+
v2Columns: toLegacyV2Columns(cols),
|
|
175
|
+
...(featureTitle ? { featureTitle } : {}),
|
|
176
|
+
issues,
|
|
177
|
+
parseWarnings,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=parseBatchCreationCsvV2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseBatchCreationCsvV2.js","sourceRoot":"","sources":["../../src/spreadsheet/parseBatchCreationCsvV2.ts"],"names":[],"mappings":"AACA,OAAO,EACH,sBAAsB,EACtB,oBAAoB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACH,2BAA2B,EAC3B,YAAY,EACZ,UAAU,EACV,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACH,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,mBAAmB,EACnB,iCAAiC,GACpC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACH,yBAAyB,EACzB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,4BAA4B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9F,SAAS,iBAAiB,CACtB,IAAkD;IAElD,OAAO;QACH,IAAI,EAAE,IAAI,CAAC,SAAS;QACpB,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,SAAS;KACzB,CAAC;AACN,CAAC;AAED,SAAS,IAAI,CAAC,GAAa,EAAE,KAAa;IACtC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,UAAU,CAAC,MAWnB;IACG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,KAAK,GAAqB;QAC5B,MAAM,EAAE,OAAO,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,EAAE;QAChD,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,sBAAsB,CAAC,MAAM,CAAC,KAAK,CAAC;QAC7C,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAC9B,WAAW,EAAE,2BAA2B,CACpC,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,QAAQ,CAClB;QACD,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE;QAC1C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE;QACpC,MAAM,EAAE,EAAE;QACV,YAAY,EAAE,MAAM,CAAC,YAAY;KACpC,CAAC;IACF,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC7B,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC;IACxC,CAAC;IACD,IAAI,MAAM,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QACzC,KAAK,CAAC,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACnD,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,cAAc,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAElD,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO;YACH,MAAM,EAAE,IAAI;YACZ,UAAU,EAAE,EAAE;YACd,cAAc,EAAE,CAAC,CAAC;YAClB,MAAM,EAAE,EAAE;YACV,aAAa,EAAE;gBACX,iHAAiH;aACpH;SACJ,CAAC;IACN,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAE,CAAC;IACtC,MAAM,cAAc,GAAG,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,MAAM,eAAe,GAAG,mBAAmB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAClE,MAAM,iBAAiB,GAAG,YAAY;QAClC,CAAC,CAAC,iCAAiC,CAAC,IAAI,EAAE,eAAe,EAAE,SAAS,CAAC;QACrE,CAAC,CAAC,SAAS,CAAC;IAEhB,IAAI,YAAY,EAAE,CAAC;QACf,MAAM,WAAW,GAAG,UAAU,CAAC;YAC3B,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,YAAY;YACrB,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,EAAE;YACjB,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,EAAE;YACZ,YAAY,EAAE,IAAI;YAClB,GAAG,CAAC,iBAAiB,KAAK,SAAS;gBAC/B,CAAC,CAAC,EAAE,iBAAiB,EAAE,iBAAiB,EAAE;gBAC1C,CAAC,CAAC,EAAE,CAAC;SACZ,CAAC,CAAC;QACH,IAAI,WAAW,EAAE,CAAC;YACd,WAAW,CAAC,MAAM,GAAG,oBAAoB,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7B,CAAC;IACL,CAAC;IAED,IAAI,sBAAsB,GAAkB,IAAI,CAAC;IACjD,MAAM,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,uFAAuF;IACvF,IAAI,oBAAoB,GAAG,EAAE,CAAC;IAE9B,KAAK,IAAI,CAAC,GAAG,cAAc,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,gBAAgB,GAClB,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,aAAa,GACf,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,YAAY,GAAG,4BAA4B,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAE7D,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO;YAAE,SAAS;QAEpC,IAAI,qBAAqB,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YAC3E,aAAa,CAAC,IAAI,CACd,OAAO,CAAC,GAAG,CAAC,wDAAwD,CACvE,CAAC;YACF,sBAAsB,GAAG,IAAI,CAAC;YAC9B,SAAS;QACb,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACX,IAAI,gBAAgB,EAAE,CAAC;gBACnB,oBAAoB,GAAG,gBAAgB,CAAC;YAC5C,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;YACtE,MAAM,cAAc,GAAG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACjE,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACtE,MAAM,IAAI,GAAG,UAAU,CAAC;gBACpB,QAAQ,EAAE,CAAC,GAAG,CAAC;gBACf,KAAK,EAAE,WAAW;gBAClB,OAAO;gBACP,WAAW;gBACX,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzC,aAAa,EAAE,oBAAoB;gBACnC,UAAU,EAAE,EAAE;gBACd,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;gBACpC,YAAY,EAAE,eAAe;gBAC7B,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,OAAO;oBACtC,CAAC,CAAC,EAAE,iBAAiB,EAAE,YAAY,EAAE;oBACrC,CAAC,CAAC,EAAE,CAAC;aACZ,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;YAC1E,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAClB,sBAAsB,GAAG,IAAI,CAAC,MAAM,CAAC;YACzC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACV,IAAI,CAAC,sBAAsB,EAAE,CAAC;gBAC1B,aAAa,CAAC,IAAI,CACd,OAAO,CAAC,GAAG,CAAC,yDAAyD,CACxE,CAAC;gBACF,SAAS;YACb,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;YACxD,MAAM,YAAY,GAAG,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAClE,MAAM,QAAQ,GAAG,4BAA4B,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,UAAU,CAAC;gBACpB,QAAQ,EAAE,CAAC,GAAG,CAAC;gBACf,KAAK,EAAE,SAAS;gBAChB,OAAO;gBACP,WAAW,EAAE,cAAc;gBAC3B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzC,aAAa,EAAE,EAAE;gBACjB,UAAU,EAAE,aAAa;gBACzB,QAAQ,EAAE,WAAW;gBACrB,YAAY,EAAE,sBAAsB;gBACpC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrE,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBAC7D,SAAS;YACb,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACL,CAAC;IAED,OAAO;QACH,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE;QAC1D,cAAc,EAAE,cAAc,GAAG,CAAC;QAClC,YAAY,EAAE,IAAI;QAClB,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,SAAS,EAAE,iBAAiB,CAAC,IAAI,CAAC;QAClC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,MAAM;QACN,aAAa;KAChB,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task ID / Ticket column: store Redmine issues as clickable HYPERLINK formulas.
|
|
3
|
+
*
|
|
4
|
+
* Composite Ticket column (user story + task on one row):
|
|
5
|
+
* =HYPERLINK(url,"Ticket:#123") & " " & HYPERLINK(url,"MainTicket:#456")
|
|
6
|
+
* Task-only rows: =HYPERLINK(url,"Ticket:#123")
|
|
7
|
+
* Story-only rows: =HYPERLINK(url,"MainTicket:#456")
|
|
8
|
+
*
|
|
9
|
+
* Plain text Ticket:#id / MainTicket:#id (no formula) is still parsed on read.
|
|
10
|
+
*/
|
|
11
|
+
export type TicketCellLabel = "Ticket" | "MainTicket";
|
|
12
|
+
export type ParsedTicketCell = {
|
|
13
|
+
ticketId?: number;
|
|
14
|
+
mainTicketId?: number;
|
|
15
|
+
ticketPart?: string;
|
|
16
|
+
mainTicketPart?: string;
|
|
17
|
+
hasLabels: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function buildRedmineIssueUrl(baseUrl: string, issueId: number): string;
|
|
20
|
+
/** Google Sheets formula — clickable #id linking to the issue (Task ID column). */
|
|
21
|
+
export declare function buildRedmineIssueIdCell(issueId: number, issueUrl: string): string;
|
|
22
|
+
/** HYPERLINK(...) segment without leading = (for joining composite cells). */
|
|
23
|
+
export declare function buildLabeledTicketHyperlinkSegment(label: TicketCellLabel, issueId: number, issueUrl: string): string;
|
|
24
|
+
/** Single labeled Ticket-column formula. */
|
|
25
|
+
export declare function buildLabeledTicketHyperlink(label: TicketCellLabel, issueId: number, issueUrl: string): string;
|
|
26
|
+
export declare function hasCompositeTicketLabels(value: string): boolean;
|
|
27
|
+
/** Parse Ticket / MainTicket segments from a composite Ticket column cell. */
|
|
28
|
+
export declare function parseTicketCell(value: string): ParsedTicketCell;
|
|
29
|
+
/** Merge Ticket / MainTicket segments for sheet write-back (Ticket first). */
|
|
30
|
+
export declare function buildCompositeTicketCell(parts: {
|
|
31
|
+
ticketPart?: string;
|
|
32
|
+
mainTicketPart?: string;
|
|
33
|
+
}): string;
|
|
34
|
+
export declare function mergeTicketCellWriteBack(existingCell: string, level: "userStory" | "subtask", issueId: number, issueUrl: string): string;
|
|
35
|
+
/** Parse issue id from bare id, #id, URL, or HYPERLINK formula text. */
|
|
36
|
+
export declare function parseRedmineIssueId(value: string): number | undefined;
|
|
37
|
+
export declare function isSheetsFormulaValue(value: string | number): boolean;
|
|
38
|
+
//# sourceMappingURL=redmineIssueCell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redmineIssueCell.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/redmineIssueCell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACtB,CAAC;AAMF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACjB,MAAM,CAGR;AAED,8EAA8E;AAC9E,wBAAgB,kCAAkC,CAC9C,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACjB,MAAM,CAGR;AAED,4CAA4C;AAC5C,wBAAgB,2BAA2B,CACvC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACjB,MAAM,CAER;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAM/D;AA0DD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CA6B/D;AAED,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,MAAM,CAST;AAED,wBAAgB,wBAAwB,CACpC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,WAAW,GAAG,SAAS,EAC9B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACjB,MAAM,CA4BR;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA8BrE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAEpE"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task ID / Ticket column: store Redmine issues as clickable HYPERLINK formulas.
|
|
3
|
+
*
|
|
4
|
+
* Composite Ticket column (user story + task on one row):
|
|
5
|
+
* =HYPERLINK(url,"Ticket:#123") & " " & HYPERLINK(url,"MainTicket:#456")
|
|
6
|
+
* Task-only rows: =HYPERLINK(url,"Ticket:#123")
|
|
7
|
+
* Story-only rows: =HYPERLINK(url,"MainTicket:#456")
|
|
8
|
+
*
|
|
9
|
+
* Plain text Ticket:#id / MainTicket:#id (no formula) is still parsed on read.
|
|
10
|
+
*/
|
|
11
|
+
function escapeFormulaUrl(url) {
|
|
12
|
+
return url.replace(/"/g, '""');
|
|
13
|
+
}
|
|
14
|
+
export function buildRedmineIssueUrl(baseUrl, issueId) {
|
|
15
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
16
|
+
return `${base}/issues/${issueId}`;
|
|
17
|
+
}
|
|
18
|
+
/** Google Sheets formula — clickable #id linking to the issue (Task ID column). */
|
|
19
|
+
export function buildRedmineIssueIdCell(issueId, issueUrl) {
|
|
20
|
+
const url = escapeFormulaUrl(issueUrl);
|
|
21
|
+
return `=HYPERLINK("${url}","#${issueId}")`;
|
|
22
|
+
}
|
|
23
|
+
/** HYPERLINK(...) segment without leading = (for joining composite cells). */
|
|
24
|
+
export function buildLabeledTicketHyperlinkSegment(label, issueId, issueUrl) {
|
|
25
|
+
const url = escapeFormulaUrl(issueUrl);
|
|
26
|
+
return `HYPERLINK("${url}","${label}:#${issueId}")`;
|
|
27
|
+
}
|
|
28
|
+
/** Single labeled Ticket-column formula. */
|
|
29
|
+
export function buildLabeledTicketHyperlink(label, issueId, issueUrl) {
|
|
30
|
+
return `=${buildLabeledTicketHyperlinkSegment(label, issueId, issueUrl)}`;
|
|
31
|
+
}
|
|
32
|
+
export function hasCompositeTicketLabels(value) {
|
|
33
|
+
const trimmed = value.trim();
|
|
34
|
+
return (/\b(?:Ticket|MainTicket):/i.test(trimmed) ||
|
|
35
|
+
/HYPERLINK\s*\([^,]+,\s*"(?:Ticket|MainTicket):#/i.test(trimmed));
|
|
36
|
+
}
|
|
37
|
+
function hyperlinkSegment(part) {
|
|
38
|
+
return part.trim().replace(/^=/, "");
|
|
39
|
+
}
|
|
40
|
+
function extractLabeledHyperlinkPart(text, label) {
|
|
41
|
+
const displayRe = new RegExp(`(HYPERLINK\\s*\\([^,]+,\\s*"${label}:#\\d+"\\))`, "i");
|
|
42
|
+
const displayMatch = displayRe.exec(text);
|
|
43
|
+
if (displayMatch?.[1])
|
|
44
|
+
return displayMatch[1];
|
|
45
|
+
const legacyRe = new RegExp(`(${label}:=HYPERLINK\\s*\\([^)]+\\))`, "i");
|
|
46
|
+
const legacyMatch = legacyRe.exec(text);
|
|
47
|
+
if (legacyMatch?.[1])
|
|
48
|
+
return legacyMatch[1];
|
|
49
|
+
const plainRe = new RegExp(`(${label}:#\\d+)`, "i");
|
|
50
|
+
const plainMatch = plainRe.exec(text);
|
|
51
|
+
if (plainMatch?.[1])
|
|
52
|
+
return plainMatch[1];
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
function extractLabeledId(text, label) {
|
|
56
|
+
const displayRe = new RegExp(`HYPERLINK\\s*\\([^,]+,\\s*"${label}:#(\\d+)"`, "i");
|
|
57
|
+
const displayMatch = displayRe.exec(text);
|
|
58
|
+
if (displayMatch?.[1]) {
|
|
59
|
+
const id = Number(displayMatch[1]);
|
|
60
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
61
|
+
}
|
|
62
|
+
const legacyRe = new RegExp(`${label}:=HYPERLINK\\s*\\([^,]+,\\s*"#?(\\d+)"`, "i");
|
|
63
|
+
const legacyMatch = legacyRe.exec(text);
|
|
64
|
+
if (legacyMatch?.[1]) {
|
|
65
|
+
const id = Number(legacyMatch[1]);
|
|
66
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
67
|
+
}
|
|
68
|
+
const part = extractLabeledHyperlinkPart(text, label);
|
|
69
|
+
if (!part)
|
|
70
|
+
return undefined;
|
|
71
|
+
const value = part.slice(label.length + 1).trim();
|
|
72
|
+
return parseRedmineIssueId(value);
|
|
73
|
+
}
|
|
74
|
+
/** Parse Ticket / MainTicket segments from a composite Ticket column cell. */
|
|
75
|
+
export function parseTicketCell(value) {
|
|
76
|
+
const trimmed = value.trim();
|
|
77
|
+
if (!trimmed) {
|
|
78
|
+
return { hasLabels: false };
|
|
79
|
+
}
|
|
80
|
+
const hasLabels = hasCompositeTicketLabels(trimmed);
|
|
81
|
+
if (!hasLabels) {
|
|
82
|
+
const legacyId = parseRedmineIssueId(trimmed);
|
|
83
|
+
return {
|
|
84
|
+
hasLabels: false,
|
|
85
|
+
...(legacyId !== undefined ? { ticketId: legacyId } : {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const ticketPart = extractLabeledHyperlinkPart(trimmed, "Ticket");
|
|
89
|
+
const mainTicketPart = extractLabeledHyperlinkPart(trimmed, "MainTicket");
|
|
90
|
+
const ticketId = ticketPart
|
|
91
|
+
? extractLabeledId(trimmed, "Ticket")
|
|
92
|
+
: undefined;
|
|
93
|
+
const mainTicketId = mainTicketPart
|
|
94
|
+
? extractLabeledId(trimmed, "MainTicket")
|
|
95
|
+
: undefined;
|
|
96
|
+
const parsed = { hasLabels: true };
|
|
97
|
+
if (ticketPart)
|
|
98
|
+
parsed.ticketPart = ticketPart;
|
|
99
|
+
if (mainTicketPart)
|
|
100
|
+
parsed.mainTicketPart = mainTicketPart;
|
|
101
|
+
if (ticketId !== undefined)
|
|
102
|
+
parsed.ticketId = ticketId;
|
|
103
|
+
if (mainTicketId !== undefined)
|
|
104
|
+
parsed.mainTicketId = mainTicketId;
|
|
105
|
+
return parsed;
|
|
106
|
+
}
|
|
107
|
+
/** Merge Ticket / MainTicket segments for sheet write-back (Ticket first). */
|
|
108
|
+
export function buildCompositeTicketCell(parts) {
|
|
109
|
+
const segments = [];
|
|
110
|
+
if (parts.ticketPart)
|
|
111
|
+
segments.push(hyperlinkSegment(parts.ticketPart));
|
|
112
|
+
if (parts.mainTicketPart) {
|
|
113
|
+
segments.push(hyperlinkSegment(parts.mainTicketPart));
|
|
114
|
+
}
|
|
115
|
+
if (segments.length === 0)
|
|
116
|
+
return "";
|
|
117
|
+
if (segments.length === 1)
|
|
118
|
+
return `=${segments[0]}`;
|
|
119
|
+
return `=${segments.join(' & " " & ')}`;
|
|
120
|
+
}
|
|
121
|
+
export function mergeTicketCellWriteBack(existingCell, level, issueId, issueUrl) {
|
|
122
|
+
const label = level === "userStory" ? "MainTicket" : "Ticket";
|
|
123
|
+
const newPart = buildLabeledTicketHyperlinkSegment(label, issueId, issueUrl);
|
|
124
|
+
const parsed = parseTicketCell(existingCell);
|
|
125
|
+
if (!parsed.hasLabels) {
|
|
126
|
+
if (level === "userStory") {
|
|
127
|
+
return buildCompositeTicketCell({ mainTicketPart: newPart });
|
|
128
|
+
}
|
|
129
|
+
return buildCompositeTicketCell({ ticketPart: newPart });
|
|
130
|
+
}
|
|
131
|
+
if (level === "userStory") {
|
|
132
|
+
return buildCompositeTicketCell({
|
|
133
|
+
...(parsed.ticketPart
|
|
134
|
+
? { ticketPart: hyperlinkSegment(parsed.ticketPart) }
|
|
135
|
+
: {}),
|
|
136
|
+
mainTicketPart: newPart,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return buildCompositeTicketCell({
|
|
140
|
+
ticketPart: newPart,
|
|
141
|
+
...(parsed.mainTicketPart
|
|
142
|
+
? { mainTicketPart: hyperlinkSegment(parsed.mainTicketPart) }
|
|
143
|
+
: {}),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/** Parse issue id from bare id, #id, URL, or HYPERLINK formula text. */
|
|
147
|
+
export function parseRedmineIssueId(value) {
|
|
148
|
+
const trimmed = value.trim();
|
|
149
|
+
if (!trimmed)
|
|
150
|
+
return undefined;
|
|
151
|
+
const fromLabeledDisplay = /HYPERLINK\s*\([^,]+,\s*"(?:Ticket|MainTicket):#(\d+)"/i.exec(trimmed);
|
|
152
|
+
if (fromLabeledDisplay?.[1]) {
|
|
153
|
+
const id = Number(fromLabeledDisplay[1]);
|
|
154
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
155
|
+
}
|
|
156
|
+
const fromUrl = /\/issues\/(\d+)/i.exec(trimmed);
|
|
157
|
+
if (fromUrl?.[1]) {
|
|
158
|
+
const id = Number(fromUrl[1]);
|
|
159
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
160
|
+
}
|
|
161
|
+
const fromHyperlink = /HYPERLINK\s*\([^)]*\/issues\/(\d+)/i.exec(trimmed);
|
|
162
|
+
if (fromHyperlink?.[1]) {
|
|
163
|
+
const id = Number(fromHyperlink[1]);
|
|
164
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
165
|
+
}
|
|
166
|
+
const digits = /^#?(\d+)$/.exec(trimmed);
|
|
167
|
+
if (digits?.[1]) {
|
|
168
|
+
const id = Number(digits[1]);
|
|
169
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
170
|
+
}
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
export function isSheetsFormulaValue(value) {
|
|
174
|
+
return typeof value === "string" && value.trimStart().startsWith("=");
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=redmineIssueCell.js.map
|