Show results whose plan item stopped as failed
The engine can stop a plan item without updating its drafts: a run paused on material_quote_changed leaves the draft "preparing" for good, so the result showed Preparing forever. A draft still at work now counts as failed once its plan item is failed, blocked, cancelled or skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { correctIndex, questionsOf, toMarkdown, type Material } from './results'
|
import { correctIndex, questionsOf, toMarkdown, withPlanItem, type Material } from './results'
|
||||||
import { sentencesOf, scriptLang } from '../components/AudioPlayer'
|
import { sentencesOf, scriptLang } from '../components/AudioPlayer'
|
||||||
|
|
||||||
describe('correctIndex', () => {
|
describe('correctIndex', () => {
|
||||||
@@ -52,3 +52,16 @@ describe('audio script helpers', () => {
|
|||||||
expect(scriptLang('This is the lesson that we will share with the team.')).toBe('en')
|
expect(scriptLang('This is the lesson that we will share with the team.')).toBe('en')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('withPlanItem', () => {
|
||||||
|
it('fails a draft still at work when its plan item stopped', () => {
|
||||||
|
expect(withPlanItem('preparing', 'failed')).toBe('failed')
|
||||||
|
expect(withPlanItem('generating', 'blocked')).toBe('failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps the draft status otherwise', () => {
|
||||||
|
expect(withPlanItem('preparing', 'generating')).toBe('preparing')
|
||||||
|
expect(withPlanItem('ready', 'failed')).toBe('ready')
|
||||||
|
expect(withPlanItem('preparing', undefined)).toBe('preparing')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import { request } from '../api/client'
|
import { request } from '../api/client'
|
||||||
import { t } from '../i18n'
|
import { t } from '../i18n'
|
||||||
import { kindLabel, type ContentKind, type Rule } from './parse'
|
import { kindLabel, type ContentKind, type Rule } from './parse'
|
||||||
import { expressAgents, PLAN_ACTIVE, productionStatus, type Schedule } from './schedules'
|
import { expressAgents, PLAN_ACTIVE, productionStatus, type PlanItem, type Schedule } from './schedules'
|
||||||
|
|
||||||
const AGENTS = '/organizations/current/agents'
|
const AGENTS = '/organizations/current/agents'
|
||||||
|
|
||||||
@@ -79,6 +79,12 @@ interface RawSource {
|
|||||||
const ORDER: MaterialStatus[] = ['failed', 'rejected', 'preparing', 'generating', 'ready', 'accepted']
|
const ORDER: MaterialStatus[] = ['failed', 'rejected', 'preparing', 'generating', 'ready', 'accepted']
|
||||||
const combined = (parts: Part[]) => ORDER.find(s => parts.some(p => p.status === s)) ?? 'preparing'
|
const combined = (parts: Part[]) => ORDER.find(s => parts.some(p => p.status === s)) ?? 'preparing'
|
||||||
|
|
||||||
|
// The engine can stop a plan item without updating its drafts (e.g. a run paused on a changed
|
||||||
|
// quote leaves the draft "preparing" for good), so a stopped item overrides a draft still at work.
|
||||||
|
const STOPPED = new Set<PlanItem['status']>(['failed', 'blocked', 'cancelled', 'skipped'])
|
||||||
|
export const withPlanItem = (status: MaterialStatus, item: PlanItem['status'] | undefined): MaterialStatus =>
|
||||||
|
item && STOPPED.has(item) && (status === 'preparing' || status === 'generating') ? 'failed' : status
|
||||||
|
|
||||||
async function materialsOf(schedule: Schedule): Promise<Material[]> {
|
async function materialsOf(schedule: Schedule): Promise<Material[]> {
|
||||||
if (schedule.rule.type === 'content' && schedule.rule.kind === 'video') return videosOf(schedule)
|
if (schedule.rule.type === 'content' && schedule.rule.kind === 'video') return videosOf(schedule)
|
||||||
const { agent, rule } = schedule
|
const { agent, rule } = schedule
|
||||||
@@ -87,9 +93,9 @@ async function materialsOf(schedule: Schedule): Promise<Material[]> {
|
|||||||
request<RawMaterial[]>(`${AGENTS}/${agent.id}/material-drafts`),
|
request<RawMaterial[]>(`${AGENTS}/${agent.id}/material-drafts`),
|
||||||
productionStatus(agent.id).catch(() => null),
|
productionStatus(agent.id).catch(() => null),
|
||||||
])
|
])
|
||||||
const titles = new Map<number, string>()
|
const items = new Map<number, PlanItem>()
|
||||||
for (const plan of status?.plans ?? []) {
|
for (const plan of status?.plans ?? []) {
|
||||||
for (const item of plan.items ?? []) if (item.run_id) titles.set(item.run_id, item.title)
|
for (const item of plan.items ?? []) if (item.run_id) items.set(item.run_id, item)
|
||||||
}
|
}
|
||||||
// One run makes one result; its drafts are its parts (one, or the lesson + quiz of a SCORM module).
|
// One run makes one result; its drafts are its parts (one, or the lesson + quiz of a SCORM module).
|
||||||
const byRun = new Map<number, RawMaterial[]>()
|
const byRun = new Map<number, RawMaterial[]>()
|
||||||
@@ -101,8 +107,8 @@ async function materialsOf(schedule: Schedule): Promise<Material[]> {
|
|||||||
const parts = group.map(g => ({ id: g.id, revision: g.revision, kind: g.output_kind, status: g.status }))
|
const parts = group.map(g => ({ id: g.id, revision: g.revision, kind: g.output_kind, status: g.status }))
|
||||||
return {
|
return {
|
||||||
id: d.id, agentId: agent.id, kind: kind === 'audio' || kind === 'scorm' ? kind : d.output_kind,
|
id: d.id, agentId: agent.id, kind: kind === 'audio' || kind === 'scorm' ? kind : d.output_kind,
|
||||||
status: combined(parts), revision: d.revision, parts,
|
status: withPlanItem(combined(parts), items.get(d.run_id)?.status), revision: d.revision, parts,
|
||||||
planTitle: titles.get(d.run_id) ?? null,
|
planTitle: items.get(d.run_id)?.title ?? null,
|
||||||
composeSetId: d.compose_set_id, sources: d.sources.map(s => s.title), createdAt: d.created_at, rule,
|
composeSetId: d.compose_set_id, sources: d.sources.map(s => s.title), createdAt: d.created_at, rule,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user