Skip to content

Commit

Permalink
Merge pull request #140 from sillsdev/retain-translations-when-user-q…
Browse files Browse the repository at this point in the history
…uestios-are-added

Handled case where a user-modified question is later added
  • Loading branch information
tombogle authored Jul 19, 2024
2 parents a49d19e + 6eb7a4f commit 4d2aaa9
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 22 deletions.
37 changes: 31 additions & 6 deletions Transcelerator/UNSQuestionsDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2667,13 +2667,14 @@ public void LoadTranslations(IProgressMessage splashScreen)

Exception e;
ParsedQuestions parsedQuestions;
List<IModifiedPhrase> temporaryAdditionalLookups = null;
if (finfoParsedQuestions.Exists &&
finfoMasterQuestions.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc &&
finfoTxlDll.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc &&
(!finfoKtRules.Exists || finfoKtRules.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc) &&
(!finfoQuestionWords.Exists || finfoQuestionWords.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc) &&
m_fileAccessor.ModifiedTime(DataFileAccessor.DataFileId.QuestionCustomizations).ToUniversalTime() < finfoParsedQuestions.LastWriteTimeUtc &&
m_fileAccessor.ModifiedTime(DataFileAccessor.DataFileId.PhraseSubstitutions).ToUniversalTime() < finfoParsedQuestions.LastWriteTimeUtc)
finfoMasterQuestions.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc &&
finfoTxlDll.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc &&
(!finfoKtRules.Exists || finfoKtRules.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc) &&
(!finfoQuestionWords.Exists || finfoQuestionWords.LastWriteTimeUtc < finfoParsedQuestions.LastWriteTimeUtc) &&
m_fileAccessor.ModifiedTime(DataFileAccessor.DataFileId.QuestionCustomizations).ToUniversalTime() < finfoParsedQuestions.LastWriteTimeUtc &&
m_fileAccessor.ModifiedTime(DataFileAccessor.DataFileId.PhraseSubstitutions).ToUniversalTime() < finfoParsedQuestions.LastWriteTimeUtc)
{
parsedQuestions = XmlSerializationHelper.DeserializeFromFile<ParsedQuestions>(m_parsedQuestionsFilename);
}
Expand Down Expand Up @@ -2701,6 +2702,7 @@ public void LoadTranslations(IProgressMessage splashScreen)
m_parser = new MasterQuestionParser(m_masterQuestionsFilename, GetQuestionWords(),
m_getKeyTerms(), GetKeyTermRules(keyTermRulesFilename), customizations, PhraseSubstitutions);
parsedQuestions = m_parser.Result;
temporaryAdditionalLookups = m_parser.TemporaryAdditionalLookups?.ToList();
Directory.CreateDirectory(Path.GetDirectoryName(m_parsedQuestionsFilename));
XmlSerializationHelper.SerializeToFile(m_parsedQuestionsFilename, parsedQuestions);
}
Expand Down Expand Up @@ -2748,6 +2750,29 @@ public void LoadTranslations(IProgressMessage splashScreen)
TranslatablePhrase phrase = m_helper.GetPhrase(unsTranslation.Reference, unsTranslation.PhraseKey);
if (phrase != null && (!phrase.IsExcluded || phrase.IsUserAdded))
phrase.Translation = unsTranslation.Translation;
else if (temporaryAdditionalLookups != null)
{
// This is the unusual situation where questions that were previously
// modified (and presumably translated) by a user have subsequently
// been added to the official collection of questions or alternatives.
// Because of this, we lose the association and can't hook up the
// user's translation to the question directly. The parser now keeps
// track of those lost associations so we can try to avoid losing the
// translations.
var iLookup = temporaryAdditionalLookups
.IndexOf(m => m.Reference == unsTranslation.Reference &&
m.OriginalPhrase == unsTranslation.PhraseKey);
if (iLookup >= 0)
{
phrase = m_helper.GetPhrase(unsTranslation.Reference,
temporaryAdditionalLookups[iLookup].ModifiedPhrase);
if (phrase != null && !phrase.HasUserTranslation)
phrase.Translation = unsTranslation.Translation;
temporaryAdditionalLookups.RemoveAt(iLookup);
if (!temporaryAdditionalLookups.Any())
temporaryAdditionalLookups = null;
}
}
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions TxlData/IModifiedPhrase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// ---------------------------------------------------------------------------------------------
#region // Copyright (c) 2024, SIL International.
// <copyright from='2024' to='2024' company='SIL International'>
// Copyright (c) 2024, SIL International.
//
// Distributable under the terms of the MIT License (http://sil.mit-license.org/)
// </copyright>
#endregion
// ---------------------------------------------------------------------------------------------
namespace SIL.Transcelerator
{
/// <summary>
/// Represents a phrase/question that the user modified (even if that modification was
/// later superseded by a subsequent program change that added the modified version).
/// </summary>
/// <remarks>Technically, we wouldn't need this interface since the only class that
/// implements it is in this same assembly, but it wouldn't have to be implemented like
/// that, and using this interface clarifies the properties we actually care about.</remarks>
public interface IModifiedPhrase
{
string Reference { get; }
/// --------------------------------------------------------------------------------
/// <summary>
/// Gets the original phrase.
/// </summary>
/// --------------------------------------------------------------------------------
string OriginalPhrase { get; }

/// --------------------------------------------------------------------------------
/// <summary>
/// Gets the edited/customized phrase.
/// </summary>
/// --------------------------------------------------------------------------------
string ModifiedPhrase { get; }
}
}
1 change: 0 additions & 1 deletion TxlData/IPhraseTranslationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
//
// File: IPhraseTranslationHelper.cs
// ---------------------------------------------------------------------------------------------
using SIL.Transcelerator.Localization;
using System.Collections.Generic;
using System.Globalization;

Expand Down
41 changes: 30 additions & 11 deletions TxlData/MasterQuestionParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ---------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------
#region // Copyright (c) 2023, SIL International.
// <copyright from='2013' to='2023' company='SIL International'>
// Copyright (c) 2023, SIL International.
Expand Down Expand Up @@ -35,6 +35,11 @@ public class MasterQuestionParser

private class Customizations // All customizations that share a key (used to match to a question)
{
#region Events and Delegates
internal delegate void ModifiedPhraseEventHandler(Customizations sender, IModifiedPhrase phrase);
internal static event ModifiedPhraseEventHandler ModifiedPhraseFoundInExistingQuestions;
#endregion

private bool m_isResolved = true;
private List<PhraseCustomization> AdditionsAndInsertions { get; }
private List<PhraseCustomization> Deletions { get; }
Expand Down Expand Up @@ -92,6 +97,7 @@ private void SetExcludedAndModified(Question question, IReadOnlyCollection<Quest
Modification.OriginalPhrase == e.Alternatives?.SingleOrDefault(a => a.IsKey)?.Text)))
{
question.IsExcluded = true;
ModifiedPhraseFoundInExistingQuestions?.Invoke(this, Modification);
}
else
question.ModifiedPhrase = ModifiedPhrase;
Expand Down Expand Up @@ -343,8 +349,9 @@ public Question PopQuestion(IQuestionKey keyToUseForReference)

private readonly IDictionary<int, List<List<Word>>> m_questionWordsLookupTable;
private readonly IEnumerable<string> m_questionWords;
private List<IModifiedPhrase> m_temporaryAdditionalLookups;

#endregion
#endregion

#region SubPhraseMatch class
private class SubPhraseMatch
Expand Down Expand Up @@ -400,7 +407,7 @@ public MasterQuestionParser(QuestionSections sections,
IEnumerable<PhraseCustomization> customizations,
IEnumerable<Substitution> phraseSubstitutions)
{
m_sections = sections;
m_sections = sections;
m_questionWords = questionWords;
if (questionWords != null)
{
Expand All @@ -416,6 +423,8 @@ public MasterQuestionParser(QuestionSections sections,
}
if (customizations != null)
{
Customizations.ModifiedPhraseFoundInExistingQuestions += Customizations_ModifiedPhraseFoundInExistingQuestions;

m_customizations = new Dictionary<int, SortedDictionary<QuestionKey, Customizations>>();
foreach (var customization in customizations)
{
Expand Down Expand Up @@ -447,12 +456,19 @@ public MasterQuestionParser(QuestionSections sections,
m_partsTable = new SortedDictionary<int, Dictionary<Word, List<ParsedPart>>>();
}

/// ------------------------------------------------------------------------------------
/// <summary>
/// Performs the parsing logic to divide question text into translatable parts and key term parts.
/// </summary>
/// ------------------------------------------------------------------------------------
private void Parse()
private void Customizations_ModifiedPhraseFoundInExistingQuestions(Customizations sender, IModifiedPhrase phrase)
{
if (m_temporaryAdditionalLookups == null)
m_temporaryAdditionalLookups = new List<IModifiedPhrase>();
m_temporaryAdditionalLookups.Add(phrase);
}

/// ------------------------------------------------------------------------------------
/// <summary>
/// Performs the parsing logic to divide question text into translatable parts and key term parts.
/// </summary>
/// ------------------------------------------------------------------------------------
private void Parse()
{
if (m_partsTable.Any())
throw new InvalidOperationException("Parse called more than once.");
Expand Down Expand Up @@ -568,7 +584,10 @@ public ParsedQuestions Result
return result;
}
}
#endregion

public IEnumerable<IModifiedPhrase> TemporaryAdditionalLookups => m_temporaryAdditionalLookups;

#endregion

#region Private helper methods
/// ------------------------------------------------------------------------------------
Expand All @@ -582,7 +601,7 @@ private static IEnumerable<Question> GetCustomizations(Question q,
SortedDictionary<QuestionKey, Customizations> customizations,
bool processAllAdditionsForRef = false)
{
if (TryPopCustomizationForQuestion(customizations, q, sectionRange, out var customizationsForQuestion))
if (TryPopCustomizationForQuestion(customizations, q, sectionRange, out var customizationsForQuestion))
{
customizationsForQuestion.ApplyToQuestion(q, category.Questions);
if (q.InsertedQuestionBefore != null && !category.Questions.Any(existing => !existing.IsExcluded && existing.Matches(q.InsertedQuestionBefore)))
Expand Down
8 changes: 4 additions & 4 deletions TxlData/PhraseCustomization.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ---------------------------------------------------------------------------------------------
#region // Copyright (c) 2023, SIL International.
// <copyright from='2011' to='2023' company='SIL International'>
// Copyright (c) 2023, SIL International.
#region // Copyright (c) 2024, SIL International.
// <copyright from='2011' to='2024' company='SIL International'>
// Copyright (c) 2024, SIL International.
//
// Distributable under the terms of the MIT License (http://sil.mit-license.org/)
// </copyright>
Expand All @@ -24,7 +24,7 @@ namespace SIL.Transcelerator
/// </summary>
/// ------------------------------------------------------------------------------------
[XmlType("PhraseCustomization")]
public class PhraseCustomization
public class PhraseCustomization : IModifiedPhrase
{
private BCVRef m_scrStartReference;
private BCVRef m_scrEndReference;
Expand Down
9 changes: 9 additions & 0 deletions TxlData/TxlDataTests/MasterQuestionParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,7 @@ public void GetResult_ModifiedPhrases_ResultIncludesModifications()
}
Assert.IsNull(pq.KeyTerms);
Assert.AreEqual(5, pq.TranslatableParts.Length);
Assert.That(qp.TemporaryAdditionalLookups, Is.Null);
}

///--------------------------------------------------------------------------------------
Expand Down Expand Up @@ -4602,6 +4603,11 @@ public void GetResult_ModifiedQuestionNowInInMasterList_OriginalExcluded(string
Assert.AreEqual(32003010, actQuestion.EndRef);

Assert.AreEqual(iQuestion, actCategory.Questions.Count);

var lookup = qp.TemporaryAdditionalLookups.Single();
Assert.That(lookup.Reference, Is.EqualTo(pc.Reference));
Assert.That(lookup.OriginalPhrase, Is.EqualTo(pc.OriginalPhrase));
Assert.That(lookup.ModifiedPhrase, Is.EqualTo(pc.ModifiedPhrase));
}

private List<PhraseCustomization> GetJonahC1V3Customizations(PhraseCustomization.CustomizationType firstAdditionType)
Expand Down Expand Up @@ -4819,6 +4825,8 @@ public void GetResult_CompoundAdditionAndInsertionOfPhrases_PhrasesAreInCorrectO

Assert.IsNull(pq.KeyTerms);
Assert.AreEqual(11, pq.TranslatableParts.Length);

Assert.That(qp.TemporaryAdditionalLookups, Is.Null);
}

///--------------------------------------------------------------------------------------
Expand Down Expand Up @@ -4891,6 +4899,7 @@ public void GetResult_PhraseAddedAfterInsertionBefore_PhrasesAreInCorrectOrder()

Assert.IsNull(pq.KeyTerms);
Assert.AreEqual(3, pq.TranslatableParts.Length);
Assert.That(qp.TemporaryAdditionalLookups, Is.Null);
}

///--------------------------------------------------------------------------------------
Expand Down

0 comments on commit 4d2aaa9

Please sign in to comment.