All files / web/src/db/schema worksheet-mastery.ts

100% Statements 67/67
100% Branches 1/1
100% Functions 0/0
100% Lines 67/67

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 59 60 61 62 63 64 65 66 67 682x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 39x 39x 39x 2x 2x 2x 2x  
import { index, integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { users } from './users'
 
/**
 * Worksheet mastery tracking table - tracks skill mastery per user
 *
 * Supports the mastery mode in worksheet generator, which provides
 * skill-based progression with automatic review mixing.
 *
 * Each row represents a user's mastery state for a specific skill
 * (e.g., "td-ones-regroup" for two-digit addition with ones place regrouping)
 */
export const worksheetMastery = sqliteTable(
  'worksheet_mastery',
  {
    /** Unique identifier (UUID) */
    id: text('id').primaryKey(),
 
    /** User ID (can be guest or authenticated user) */
    userId: text('user_id').notNull(),
 
    /**
     * Skill ID (e.g., "sd-no-regroup", "td-ones-regroup", "3d-full-regroup")
     * See SKILL_DEFINITIONS in src/app/create/worksheets/skills.ts
     */
    skillId: text('skill_id').notNull(),
 
    /** Whether this skill has been mastered (boolean) */
    isMastered: integer('is_mastered', { mode: 'boolean' }).notNull().default(false),
 
    /** Total number of attempts/worksheets for this skill (future use) */
    totalAttempts: integer('total_attempts').notNull().default(0),
 
    /** Number of correct attempts (future use for validation) */
    correctAttempts: integer('correct_attempts').notNull().default(0),
 
    /**
     * Last measured accuracy (0.0-1.0) from most recent worksheet (future use)
     * null if never attempted
     */
    lastAccuracy: real('last_accuracy'),
 
    /** When this skill was first attempted */
    firstAttemptAt: integer('first_attempt_at', { mode: 'timestamp' }),
 
    /** When this skill was marked as mastered (null if not mastered) */
    masteredAt: integer('mastered_at', { mode: 'timestamp' }),
 
    /** When this skill was last practiced */
    lastPracticedAt: integer('last_practiced_at', {
      mode: 'timestamp',
    }).notNull(),
 
    /** Timestamp of last update */
    updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
 
    /** Timestamp of creation */
    createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
  },
  (table) => ({
    // Composite index for fast user+skill lookups
    userSkillIdx: index('worksheet_mastery_user_skill_idx').on(table.userId, table.skillId),
  })
)
 
export type WorksheetMastery = typeof worksheetMastery.$inferSelect
export type NewWorksheetMastery = typeof worksheetMastery.$inferInsert