Commit graph

5 commits

Author SHA1 Message Date
lila
3f7bc4111e chore: rename project from glossa to lila
- Update all package names from @glossa/* to @lila/*
- Update all imports, container names, volume names
- Update documentation references
- Recreate database with new credentials
2026-04-13 10:00:52 +02:00
lila
0755c57439 feat(api): wire GameSessionStore into createGameSession
The service now tracks the correct optionId for each question and
stores the answer key in the GameSessionStore after building the
session. The client response is unchanged — the store is invisible
to the outside.

- Build answerKey (questionId → correctOptionId) during question
  assembly by finding the correct answer's position after shuffle
- Store the answer key via gameSessionStore.create() before returning
- Add excludeText parameter to getDistractors to prevent a distractor
  from having identical text to the correct answer (different term,
  same translation). Solved at the query level, not with post-filtering.
- Module-level InMemoryGameSessionStore singleton in the service
2026-04-11 11:52:38 +02:00
lila
f53ac618bb feat(api): assemble full GameSession with shuffled answer options
Extend the game flow from raw term rows to a complete GameSession
matching the shared schema:

- Add getDistractors model query: fetches N same-POS, same-difficulty,
  same-target-language terms excluding a given termId. Returns bare
  strings since distractors only need their display text.
- Fix getGameTerms select clause to use the neutral field names
  (sourceText, targetText, sourceGloss) that the type already declared.
- Rename gameService entry point to createGameSession; signature now
  takes a GameRequest and returns a GameSession.
- Per question: fetch 3 distractors, combine with the correct answer,
  shuffle (Fisher-Yates), assign optionIds 0-3 by post-shuffle index,
  and assemble into a GameQuestion with a fresh UUID.
- Wrap the questions in a GameSession with its own UUID.
- Run per-question distractor fetches in parallel via Promise.all.

Known gap: the correct option is not yet remembered server-side, so
/game/answer cannot evaluate submissions. SessionStore lands next.
2026-04-10 21:44:36 +02:00
lila
b59fac493d feat(api): implement game terms query with double join
- Add double join on translations for source/target languages
- Left join term_glosses for optional source-language glosses
- Filter difficulty on target side only (intentionally asymmetric:
  a word's difficulty can differ between languages, and what matters
  is the difficulty of the word being learned)
- Return neutral field names (sourceText, targetText, sourceGloss)
  instead of quiz semantics; service layer maps to prompt/answer
- Tighten term_glosses unique constraint to (term_id, language_code)
  to prevent the left join from multiplying question rows
- Add TODO for ORDER BY RANDOM() scaling post-MVP
2026-04-10 18:02:03 +02:00
lila
9fc3ba375a feat: scaffold quiz API vertical slice
- Add GameRequestSchema and derived types to packages/shared
- Add SupportedLanguageCode, SupportedPos, DifficultyLevel type exports
- Add getGameTerms() model to packages/db with pos/language/difficulty/limit filters
- Add prepareGameQuestions() service skeleton in apps/api
- Add createGame controller with Zod safeParse validation
- Wire POST /api/v1/game/start route
- Add scripts/gametest/test-game.ts for manual end-to-end testing
2026-04-09 13:47:01 +02:00