diff --git a/Backend.Tests/Models/WordTests.cs b/Backend.Tests/Models/WordTests.cs index 479e9da843..713fb955e9 100644 --- a/Backend.Tests/Models/WordTests.cs +++ b/Backend.Tests/Models/WordTests.cs @@ -92,6 +92,60 @@ public void TestIsBlank() Assert.That(new Note { Language = Language }.IsBlank()); Assert.IsFalse(new Note { Text = Text }.IsBlank()); } + + [Test] + public void TestAppendBlank() + { + var note = new Note(Language, Text); + + var blankNote = new Note(); + var newNote = note.Clone(); + blankNote.Append(newNote); + Assert.That(blankNote.Equals(note)); + + blankNote = new Note(); + var oldNote = note.Clone(); + oldNote.Append(blankNote); + Assert.That(oldNote.Equals(note)); + } + + [Test] + public void TestAppendIdentical() + { + var note = new Note(Language, Text); + var oldNote = note.Clone(); + var newNote = note.Clone(); + oldNote.Append(newNote); + Assert.That(oldNote.Equals(note)); + } + + [Test] + public void TestAppendSameLanguage() + { + var note = new Note(Language, Text); + var oldNote = note.Clone(); + var newNote = note.Clone(); + const string newText = "sameLangNewText"; + newNote.Text = newText; + oldNote.Append(newNote); + var expectedNote = note.Clone(); + expectedNote.Text += $"; {newText}"; + Assert.That(oldNote.Equals(expectedNote)); + } + + [Test] + public void TestAppendDiffLanguage() + { + var note = new Note(Language, Text); + var oldNote = note.Clone(); + var newNote = note.Clone(); + const string newLanguage = "diffLang"; + newNote.Language = newLanguage; + oldNote.Append(newNote); + var expectedNote = note.Clone(); + expectedNote.Text += $"; [{newLanguage}] {newNote.Text}"; + Assert.That(oldNote.Equals(expectedNote)); + } } public class SenseTests diff --git a/Backend.Tests/Services/WordServiceTests.cs b/Backend.Tests/Services/WordServiceTests.cs index 8e80e909c1..ab53fc662a 100644 --- a/Backend.Tests/Services/WordServiceTests.cs +++ b/Backend.Tests/Services/WordServiceTests.cs @@ -104,6 +104,29 @@ public void WordIsUniqueFalseAddsDomain() Assert.That(frontierSemDom, Is.Not.Null); } + [Test] + public void WordIsUniqueFalseAddsNote() + { + var oldWord = Util.RandomWord(ProjId); + oldWord.Note.Text = ""; + oldWord = _wordRepo.Create(oldWord).Result; + + // Make the new word a duplicate of the old word, but with a note added. + var newWord = Util.RandomWord(ProjId); + newWord.Vernacular = oldWord.Vernacular; + newWord.Senses = new List { oldWord.Senses.First().Clone() }; + var newNote = new Note("lang", "text"); + newWord.Note = newNote.Clone(); + + var isUnique = _wordService.WordIsUnique(newWord).Result; + Assert.That(isUnique, Is.False); + + // The newNote should be added to the old word. + var frontier = _wordRepo.GetFrontier(ProjId).Result; + Assert.That(frontier, Has.Count.EqualTo(1)); + Assert.That(frontier.First().Note, Is.EqualTo(newNote)); + } + [Test] public void WordIsUniqueSameVernNoDefNoGlossTrue() { diff --git a/Backend/Models/Word.cs b/Backend/Models/Word.cs index 5e5a3cfc5c..8519e8bc1b 100644 --- a/Backend/Models/Word.cs +++ b/Backend/Models/Word.cs @@ -243,6 +243,26 @@ public bool IsBlank() return Text == ""; } + /// Append another note to the existing note. + public void Append(Note note) + { + if (note.IsBlank() || Equals(note)) + { + return; + } + + if (IsBlank()) + { + Language = note.Language; + Text = note.Text; + } + else + { + var langTag = Language == note.Language ? "" : $"[{note.Language}] "; + Text += $"; {langTag}{note.Text}"; + } + } + public override bool Equals(object? obj) { if (obj is not Note other || GetType() != obj.GetType()) diff --git a/Backend/Services/WordService.cs b/Backend/Services/WordService.cs index f7932c60b1..2716829da1 100644 --- a/Backend/Services/WordService.cs +++ b/Backend/Services/WordService.cs @@ -132,7 +132,10 @@ public async Task Update(string projectId, string wordId, Word word) return wordIsInFrontier; } - /// Checks if a word being added is an exact duplicate of a preexisting word + /// + /// Checks if a word being added is a duplicate of a preexisting word. + /// If a duplicate, updates the existing word with any new domains or note. + /// public async Task WordIsUnique(Word word) { // Get all words from frontier @@ -178,11 +181,6 @@ public async Task WordIsUnique(Word word) { foundDuplicateSense = true; - // Add edited by and remove duplicates - matchingVern.EditedBy.AddRange(word.EditedBy); - matchingVern.EditedBy = matchingVern.EditedBy.Distinct().ToList(); - - // Add semantic domains and remove duplicates oldSense.SemanticDomains.AddRange(newSense.SemanticDomains); oldSense.SemanticDomains = oldSense.SemanticDomains.Distinct().ToList(); } @@ -195,10 +193,14 @@ public async Task WordIsUnique(Word word) } } - // Update the word only if all the senses were duplicates + // Update the existing word only if all the senses were duplicates if (foundDuplicateSense) { isUniqueWord = false; + + matchingVern.Note.Append(word.Note); + matchingVern.EditedBy.AddRange(word.EditedBy); + matchingVern.EditedBy = matchingVern.EditedBy.Distinct().ToList(); await Update(matchingVern.ProjectId, matchingVern.Id, matchingVern); } } diff --git a/src/components/DataEntry/DataEntryTable/DataEntryTable.tsx b/src/components/DataEntry/DataEntryTable/DataEntryTable.tsx index 959162339c..89a6c3b8f1 100644 --- a/src/components/DataEntry/DataEntryTable/DataEntryTable.tsx +++ b/src/components/DataEntry/DataEntryTable/DataEntryTable.tsx @@ -164,6 +164,7 @@ export class DataEntryTable extends React.Component< ); return; } + // TODO: add the audio even if the word is a duplicate const wordId = await this.addAudiosToBackend(addedWord.id, audioURLs); const word = await backend.getWord(wordId); await this.updateExisting();