All files / web/src/lib/song-share sessionFacts.ts

74.13% Statements 43/58
66.66% Branches 2/3
33.33% Functions 1/3
74.13% Lines 43/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 591x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x 10x 1x 1x 1x                           1x 1x 1x      
/**
 * Shared session-fact extractors for song share surfaces.
 *
 * Factored out of `app/observe/[token]/opengraph-image.tsx` so the OG image,
 * the public `/song/[code]` page, and the lyric annotation engine all derive
 * session facts from one place (never fork).
 *
 * These are pure helpers over a session plan's `parts` and a session song's
 * `promptInput.currentSession` — safe to call from server components, route
 * handlers, and `next/og` images.
 */
 
/** Subset of `SongPromptInput.currentSession` used across share surfaces. */
export interface SessionStats {
  accuracy: number
  problemsDone: number
  problemsTotal: number
  bestCorrectStreak: number
  partTypes: string[]
  durationMinutes: number
  skillsPracticed: string[]
}
 
export interface SessionProblem {
  terms: number[]
  answer: number
}
 
/** Format a skill key like "basic.directAddition" into "Direct Addition". */
export function formatSkill(skill: string): string {
  const name = skill.includes('.') ? skill.split('.').pop()! : skill
  return name
    .replace(/([A-Z])/g, ' $1')
    .replace(/^./, (s) => s.toUpperCase())
    .replace(/^\+/, 'Plus ')
    .trim()
}
 
/** Extract sample problems from session plan `parts`. */
export function extractProblems(parts: unknown): SessionProblem[] {
  if (!Array.isArray(parts)) return []
  const problems: SessionProblem[] = []
  for (const part of parts) {
    if (part?.slots && Array.isArray(part.slots)) {
      for (const slot of part.slots) {
        if (slot?.problem?.terms && slot.problem.answer != null) {
          problems.push({ terms: slot.problem.terms, answer: slot.problem.answer })
        }
      }
    }
  }
  return problems
}
 
/** Format a problem as a string like "13 + 11 + 10 = 34". */
export function formatProblem(p: SessionProblem): string {
  return `${p.terms.join(' + ')} = ${p.answer}`
}