Render textbook vocab as paired Spanish→English grid
Previously the chapter reader showed vocab tables as a flat list of OCR lines — because Vision reads columns top-to-bottom, the Spanish column appeared as one block followed by the English column, making pairings illegible. Now every vocab table renders as a 2-column grid with Spanish on the left and English on the right. Supporting changes: - New ocr_all_vocab.swift: bounding-box OCR over all 931 vocab images, cluster lines into rows by Y-coordinate, split rows by largest X-gap, detect 2- / 3- / 4-column layouts automatically. ~2800 pairs extracted this pass vs ~1100 from the old block-alternation heuristic. - merge_pdf_into_book.py now prefers bounding-box pairs when present, falls back to the heuristic, embeds the resulting pairs as vocab_table.cards in book.json. - DataLoader passes cards through to TextbookBlock on seed. - TextbookChapterView renders cards via SwiftUI Grid (2 cols). - fix_vocab.py quarantine rule relaxed — only mis-pairs where both sides are clearly the same language are removed. "unknown" sides stay (bbox pipeline already oriented them correctly). Textbook card count jumps from 1044 → 3118 active pairs. textbookDataVersion bumped to 9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
53
Conjuga/ConjugaUITests/VocabGridTests.swift
Normal file
53
Conjuga/ConjugaUITests/VocabGridTests.swift
Normal file
@@ -0,0 +1,53 @@
|
||||
import XCTest
|
||||
|
||||
final class VocabGridTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
/// Verifies the chapter reader renders vocab tables as a paired Spanish↔English grid.
|
||||
func testChapter4VocabGrid() throws {
|
||||
let app = XCUIApplication()
|
||||
app.launchArguments += ["-onboardingComplete", "YES"]
|
||||
app.launch()
|
||||
|
||||
app.tabBars.buttons["Course"].tap()
|
||||
|
||||
let textbookRow = app.buttons.containing(NSPredicate(
|
||||
format: "label CONTAINS[c] 'Complete Spanish'"
|
||||
)).firstMatch
|
||||
XCTAssertTrue(textbookRow.waitForExistence(timeout: 5))
|
||||
textbookRow.tap()
|
||||
|
||||
let ch4 = app.buttons["textbook-chapter-row-4"]
|
||||
XCTAssertTrue(ch4.waitForExistence(timeout: 3))
|
||||
ch4.tap()
|
||||
|
||||
attach(app, name: "01-ch4-top")
|
||||
|
||||
// Tap the first vocab disclosure — "Vocabulary (N items)"
|
||||
let vocabButton = app.buttons.matching(NSPredicate(
|
||||
format: "label BEGINSWITH 'Vocabulary ('"
|
||||
)).firstMatch
|
||||
XCTAssertTrue(vocabButton.waitForExistence(timeout: 3))
|
||||
vocabButton.tap()
|
||||
Thread.sleep(forTimeInterval: 0.4)
|
||||
|
||||
attach(app, name: "02-ch4-vocab-open")
|
||||
|
||||
// Scroll a little and screenshot a deeper vocab — numbers table is
|
||||
// typically a few screens down in chapter 4.
|
||||
app.swipeUp(velocity: .fast)
|
||||
app.swipeUp(velocity: .fast)
|
||||
attach(app, name: "03-ch4-deeper")
|
||||
}
|
||||
|
||||
private func attach(_ app: XCUIApplication, name: String) {
|
||||
let s = app.screenshot()
|
||||
let a = XCTAttachment(screenshot: s)
|
||||
a.name = name
|
||||
a.lifetime = .keepAlways
|
||||
add(a)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user