sillsdev / sillsdev/interlinearizer-extension
Dedupe identical token analysis payloads on PT9 import
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2
- Forks
- 0
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 55
Description
PT9 import writes its converted TextAnalysis straight to storage (convertPt9Project.ts:163-170 → savePt9Import), never reaching the find-or-create in appendApprovedAnalysis (src/store/analysisSlice.ts:253-266). Two tokens the import glosses identically therefore get two payloads, where a hand-glossed pair gets one shared payload at frequency 2.
Reproduction. PIA (test-data/pt9-projects/PIA, #283) approves four plovs tokens across PHP 1:6-1:7. The pool key plovs holds four entries, each frequency 1:
| payload | gloss | breakdown |
|---|---|---|
pt9:ta:PHP 1:6:5:0 |
catorce | plov s |
pt9:ta:PHP 1:6:15:0 |
catorce | plov s |
pt9:ta:PHP 1:7:0:0 |
catorce | — |
pt9:ta:PHP 1:7:15:0 |
decimocuarto | plov s |
The first two are analysesAreIdentical and render as indistinguishable rows in the suggestion dropdown.
Consequences.
- The frequency ranking in
comparePoolEntries(suggestion-engine.ts:77-80) never sees a count above 1 on an imported project, so the id tiebreak decides every homograph. - #186's panel is already on
main(AnalysisCatalogPanel,selectCatalogRows), so this is live rather than anticipated: every PT9-imported project renders each twin as its own row at one usage, against #186's own "one row per distinct token analysis". - Storage bloat, proportional to how repetitive the source is.
What to do
One content-dedupe pass in convertPt9Project.ts, just before TextAnalysis is assembled — both merged.tokenAnalyses and barePayloads exist by then, so one pass covers both. Inside mergeLanguageAnalyses would miss the bare payloads.
Take (tokenAnalyses, tokenAnalysisLinks), return them reduced:
- Bucket by
normalizeSurfaceForm(surfaceText), thenfindwithin the bucket withanalysesAreIdentical(both inutils/analysis-identity.ts). A flat linear find is O(n²) — fine for the store's one-write-at-a-time growth, not for a whole Bible. Bucketing is sound becauseanalysesAreIdenticalalready requires equal folded surface; same structure asbuildPoolIndex(suggestion-engine.ts:95-113). - Keep the first payload, drop the rest, repoint every link's
analysisId— whatmergeIntoIdenticalPayload(analysisSlice.ts:353) does after an edit. - Leave the links otherwise untouched:
token.surfaceTextstays each token's own, which is what keeps per-token drift detection accurate.
Run this pass after #324, which strips blank morpheme forms at import — stripping a morpheme turns previously-distinct payloads into twins, so the content fold must see the final shape.
Add identicalPayloadsMerged to Pt9MergeReport (report.ts:54) and seed it at report.ts:159. No UI change — Pt9ImportModal reads only report.languages.
Why the fold stays
Considered and rejected: an import-only exact-surfaceText rule instead of analysesAreIdentical's case fold.
Against it — appendApprovedAnalysis folds, so a later hand-gloss would adopt whichever duplicate sits earlier in tokenAnalyses, splitting the links on array order. buildPoolIndex folds too, so the sentence-initial twin would stay an indistinguishable extra row. And sentence-initial is the common case in scripture, so the rule would decline to merge precisely where it matters most.
Its one real cost, accepted: the surviving payload's surfaceText is whichever occurrence came first. That is presentation, not identity, and hand-glossed data already has it — appendApprovedAnalysis's own doc describes "a sentence-initial form links to a payload first created from a mid-sentence form". The fix belongs in #186: derive a row's displayed surface from its links — the most common token.surfaceText among them — recomputed on read rather than stored, since any stored choice goes stale on the next write.
Checked, no work needed. The generated ids (pt9:ta:, pt9:wa:, pt9:pa:) and producer are write-only, so an adopted payload keeping the first token's id is safe. clusterParseIdentities is a Set keyed by folded surface plus signature and already collapses duplicates. approvedTokenSeen runs before payload creation, so the one-approved-per-token invariant is unaffected.
Decide.
- Keep
buildBareWordAnalyses' ownseenByWordcheck (bareWordAnalyses.ts:52-64)? It keys on the raw word and the lexeme-id signature, so a content pass is strictly wider. Keeping both leavesskippedExistingIdenticalits current meaning.
Tests. Twins collapse with both links surviving; a bare payload identical to a linked one; case variants collapse; and the three near-misses that must not collapse — different gloss, breakdown vs none, different breakdown. PIA is the manual counterpart.
Related: #187 (same gap for phrases, on the store write path), #186 and #254 (both compute against pool-bucket contents and assume payloads are deduped).
Size: S
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in convertPt9Project.ts after #324's import changes, then read analysesAreIdentical and normalizeSurfaceForm in utils/analysis-identity.ts, plus Pt9MergeReport in report.ts. Verify the PT9 import tests cover linked and bare twins, case variants, and the three near-misses; done means duplicates collapse with links preserved and identicalPayloadsMerged is reported without UI changes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, data, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100