@prmichaelsen/acp-visualizer 0.1.6 → 0.1.8
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/package.json +1 -1
- package/src/lib/yaml-loader.spec.ts +28 -0
- package/src/lib/yaml-loader.ts +36 -6
package/package.json
CHANGED
|
@@ -186,6 +186,34 @@ milestones:
|
|
|
186
186
|
expect(result.milestones[0].id).toBe('milestone_1')
|
|
187
187
|
})
|
|
188
188
|
|
|
189
|
+
it('maps task keys to milestone IDs when formats differ', () => {
|
|
190
|
+
const yaml = `
|
|
191
|
+
milestones:
|
|
192
|
+
- id: M1
|
|
193
|
+
name: First
|
|
194
|
+
status: completed
|
|
195
|
+
- id: M2
|
|
196
|
+
name: Second
|
|
197
|
+
status: in_progress
|
|
198
|
+
|
|
199
|
+
tasks:
|
|
200
|
+
milestone_1:
|
|
201
|
+
- id: t1
|
|
202
|
+
name: Task A
|
|
203
|
+
status: completed
|
|
204
|
+
milestone_2:
|
|
205
|
+
- id: t2
|
|
206
|
+
name: Task B
|
|
207
|
+
status: in_progress
|
|
208
|
+
`
|
|
209
|
+
const result = parseProgressYaml(yaml)
|
|
210
|
+
// Tasks keyed as milestone_1 should map to milestone ID M1
|
|
211
|
+
expect(result.tasks['M1']).toHaveLength(1)
|
|
212
|
+
expect(result.tasks['M1'][0].name).toBe('Task A')
|
|
213
|
+
expect(result.tasks['M2']).toHaveLength(1)
|
|
214
|
+
expect(result.tasks['M2'][0].name).toBe('Task B')
|
|
215
|
+
})
|
|
216
|
+
|
|
189
217
|
it('handles null values in dates', () => {
|
|
190
218
|
const yaml = `
|
|
191
219
|
milestones:
|
package/src/lib/yaml-loader.ts
CHANGED
|
@@ -35,6 +35,7 @@ const TASK_ALIASES: Record<string, string> = {
|
|
|
35
35
|
done_date: 'completed_date',
|
|
36
36
|
filename: 'file',
|
|
37
37
|
path: 'file',
|
|
38
|
+
document: 'file',
|
|
38
39
|
milestone: 'milestone_id',
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -134,7 +135,11 @@ function normalizeMilestone(raw: unknown, index: number): Milestone {
|
|
|
134
135
|
id: safeString(known.id, `milestone_${index + 1}`),
|
|
135
136
|
name: safeString(known.name, `Milestone ${index + 1}`),
|
|
136
137
|
status: normalizeStatus(known.status),
|
|
137
|
-
progress:
|
|
138
|
+
progress: known.progress != null
|
|
139
|
+
? safeNumber(known.progress)
|
|
140
|
+
: safeNumber(known.tasks_total) > 0
|
|
141
|
+
? Math.round((safeNumber(known.tasks_completed) / safeNumber(known.tasks_total)) * 100)
|
|
142
|
+
: normalizeStatus(known.status) === 'completed' ? 100 : 0,
|
|
138
143
|
started: known.started ? safeString(known.started) : null,
|
|
139
144
|
completed: known.completed ? safeString(known.completed) : null,
|
|
140
145
|
estimated_weeks: safeString(known.estimated_weeks, '0'),
|
|
@@ -171,12 +176,35 @@ function normalizeTask(raw: unknown, milestoneId: string, index: number): Task {
|
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
178
|
|
|
174
|
-
function normalizeTasks(raw: unknown): Record<string, Task[]> {
|
|
179
|
+
function normalizeTasks(raw: unknown, milestones: Milestone[]): Record<string, Task[]> {
|
|
175
180
|
const result: Record<string, Task[]> = {}
|
|
176
181
|
const obj = asRecord(raw)
|
|
177
|
-
|
|
182
|
+
|
|
183
|
+
// Build a map from task key patterns to milestone IDs.
|
|
184
|
+
// Handles mismatch: tasks keyed as "milestone_1" but milestone.id = "M1"
|
|
185
|
+
const keyToMilestoneId = new Map<string, string>()
|
|
186
|
+
for (let i = 0; i < milestones.length; i++) {
|
|
187
|
+
const m = milestones[i]
|
|
188
|
+
// The task key might be the milestone ID itself, or "milestone_N"
|
|
189
|
+
keyToMilestoneId.set(m.id, m.id)
|
|
190
|
+
keyToMilestoneId.set(m.id.toLowerCase(), m.id)
|
|
191
|
+
keyToMilestoneId.set(`milestone_${i + 1}`, m.id)
|
|
192
|
+
// Also handle "milestone_N" where N matches the numeric part of "MN"
|
|
193
|
+
const numMatch = m.id.match(/(\d+)/)
|
|
194
|
+
if (numMatch) {
|
|
195
|
+
keyToMilestoneId.set(`milestone_${numMatch[1]}`, m.id)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const [rawKey, tasks] of Object.entries(obj)) {
|
|
178
200
|
if (Array.isArray(tasks)) {
|
|
179
|
-
|
|
201
|
+
// Resolve the key to a milestone ID, or keep as-is
|
|
202
|
+
const milestoneId = keyToMilestoneId.get(rawKey) || rawKey
|
|
203
|
+
const existing = result[milestoneId] || []
|
|
204
|
+
result[milestoneId] = [
|
|
205
|
+
...existing,
|
|
206
|
+
...tasks.map((t, i) => normalizeTask(t, milestoneId, existing.length + i)),
|
|
207
|
+
]
|
|
180
208
|
}
|
|
181
209
|
}
|
|
182
210
|
return result
|
|
@@ -248,10 +276,12 @@ export function parseProgressYaml(raw: string): ProgressData {
|
|
|
248
276
|
}
|
|
249
277
|
const d = doc as Record<string, unknown>
|
|
250
278
|
|
|
279
|
+
const milestones = normalizeMilestones(d.milestones)
|
|
280
|
+
|
|
251
281
|
return {
|
|
252
282
|
project: normalizeProject(d.project),
|
|
253
|
-
milestones
|
|
254
|
-
tasks: normalizeTasks(d.tasks),
|
|
283
|
+
milestones,
|
|
284
|
+
tasks: normalizeTasks(d.tasks, milestones),
|
|
255
285
|
recent_work: normalizeWorkEntries(d.recent_work),
|
|
256
286
|
next_steps: normalizeStringArray(d.next_steps),
|
|
257
287
|
notes: normalizeStringArray(d.notes),
|