From 8d24eff73cd2bf70f8d8b9c19e64c1462ec8ea0a Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 5 Jul 2024 13:25:06 -0400 Subject: [PATCH] Add "UseSourceUsfm" option to USFM endpoint. (#420) * Add "UseSourceUsfm" option to USFM endpoint. * Add template parameter when generating USFM --------- Co-authored-by: Damien Daspit --- .vscode/settings.json | 1 + src/Serval/src/Serval.Client/Client.g.cs | 67 ++- .../Contracts/PretranslationUsfmTemplate.cs | 8 + .../TranslationEnginesController.cs | 24 +- .../Services/IPretranslationService.cs | 1 + .../Services/PretranslationService.cs | 143 ++++--- .../Services/NUnitExtensions.cs | 11 + .../Services/PretranslationServiceTests.cs | 386 ++++++++++++------ .../test/Serval.Translation.Tests/Usings.cs | 1 + 9 files changed, 427 insertions(+), 215 deletions(-) create mode 100644 src/Serval/src/Serval.Translation/Contracts/PretranslationUsfmTemplate.cs create mode 100644 src/Serval/test/Serval.Translation.Tests/Services/NUnitExtensions.cs diff --git a/.vscode/settings.json b/.vscode/settings.json index dd52926b..9981ae9c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,6 +26,7 @@ "Protobuf", "ptcc", "Rebinder", + "stylesheet", "upserted", "USFM" ], diff --git a/src/Serval/src/Serval.Client/Client.g.cs b/src/Serval/src/Serval.Client/Client.g.cs index 1d45b1e5..afcf241a 100644 --- a/src/Serval/src/Serval.Client/Client.g.cs +++ b/src/Serval/src/Serval.Client/Client.g.cs @@ -1627,17 +1627,19 @@ public partial interface ITranslationEnginesClient /// Get a pretranslated Scripture book in USFM format. /// /// - /// If the USFM book exists in the target corpus, then the pretranslated text will be inserted into any empty - ///
segments in the the target book and returned. If the USFM book does not exist in the target corpus, then the - ///
pretranslated text will be inserted into an empty template created from the source USFM book and returned. - ///
Only pretranslations for the most recent successful build of the engine are returned. - ///
- ///
The text that populates the USFM structure can be controlled by the `textOrigin` parameter where with these options: - ///
* `PreferExisting`: The existing and pretranslated texts are merged into the USFM, preferring existing text. **This is the default**. + /// The text that populates the USFM structure can be controlled by the `textOrigin` parameter: + ///
* `PreferExisting`: The existing and pretranslated texts are merged into the USFM, preferring existing text. **This is the default**. ///
* `PreferPretranslated`: The existing and pretranslated texts are merged into the USFM, preferring pretranslated text. - ///
* `OnlyExisting`: Return the existing target USFM file with no modifications (except updating the USFM id if needed) - ///
* `OnlyPretranslated`: Only the pretranslated text is returned; all existing text in the target USFM is removed - ///
Both scripture and non-scripture text in the USFM is parsed and grouped according to [this wiki](https://github.com/sillsdev/serval/wiki/USFM-Parsing-and-Translation) + ///
* `OnlyExisting`: Return the existing target USFM file with no modifications (except updating the USFM id if needed). + ///
* `OnlyPretranslated`: Only the pretranslated text is returned; all existing text in the target USFM is removed. + ///
+ ///
The source or target book can be used as the USFM template for the pretranslated text. The template can be controlled by the `template` parameter: + ///
* `Auto`: The target book is used as the template if it exists; otherwise, the source book is used. **This is the default**. + ///
* `Source`: The source book is used as the template. + ///
* `Target`: The target book is used as the template. + ///
+ ///
Only pretranslations for the most recent successful build of the engine are returned. + ///
Both scripture and non-scripture text in the USFM is parsed and grouped according to [this wiki](https://github.com/sillsdev/serval/wiki/USFM-Parsing-and-Translation). ///
/// The translation engine id /// The corpus id @@ -1645,7 +1647,7 @@ public partial interface ITranslationEnginesClient /// The source[s] of the data to populate the USFM file with. /// The book in USFM format /// A server side error occurred. - System.Threading.Tasks.Task GetPretranslatedUsfmAsync(string id, string corpusId, string textId, PretranslationUsfmTextOrigin? textOrigin = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task GetPretranslatedUsfmAsync(string id, string corpusId, string textId, PretranslationUsfmTextOrigin? textOrigin = null, PretranslationUsfmTemplate? template = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -3595,17 +3597,19 @@ public string BaseUrl /// Get a pretranslated Scripture book in USFM format. /// /// - /// If the USFM book exists in the target corpus, then the pretranslated text will be inserted into any empty - ///
segments in the the target book and returned. If the USFM book does not exist in the target corpus, then the - ///
pretranslated text will be inserted into an empty template created from the source USFM book and returned. - ///
Only pretranslations for the most recent successful build of the engine are returned. - ///
- ///
The text that populates the USFM structure can be controlled by the `textOrigin` parameter where with these options: - ///
* `PreferExisting`: The existing and pretranslated texts are merged into the USFM, preferring existing text. **This is the default**. + /// The text that populates the USFM structure can be controlled by the `textOrigin` parameter: + ///
* `PreferExisting`: The existing and pretranslated texts are merged into the USFM, preferring existing text. **This is the default**. ///
* `PreferPretranslated`: The existing and pretranslated texts are merged into the USFM, preferring pretranslated text. - ///
* `OnlyExisting`: Return the existing target USFM file with no modifications (except updating the USFM id if needed) - ///
* `OnlyPretranslated`: Only the pretranslated text is returned; all existing text in the target USFM is removed - ///
Both scripture and non-scripture text in the USFM is parsed and grouped according to [this wiki](https://github.com/sillsdev/serval/wiki/USFM-Parsing-and-Translation) + ///
* `OnlyExisting`: Return the existing target USFM file with no modifications (except updating the USFM id if needed). + ///
* `OnlyPretranslated`: Only the pretranslated text is returned; all existing text in the target USFM is removed. + ///
+ ///
The source or target book can be used as the USFM template for the pretranslated text. The template can be controlled by the `template` parameter: + ///
* `Auto`: The target book is used as the template if it exists; otherwise, the source book is used. **This is the default**. + ///
* `Source`: The source book is used as the template. + ///
* `Target`: The target book is used as the template. + ///
+ ///
Only pretranslations for the most recent successful build of the engine are returned. + ///
Both scripture and non-scripture text in the USFM is parsed and grouped according to [this wiki](https://github.com/sillsdev/serval/wiki/USFM-Parsing-and-Translation). ///
/// The translation engine id /// The corpus id @@ -3613,7 +3617,7 @@ public string BaseUrl /// The source[s] of the data to populate the USFM file with. /// The book in USFM format /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetPretranslatedUsfmAsync(string id, string corpusId, string textId, PretranslationUsfmTextOrigin? textOrigin = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public virtual async System.Threading.Tasks.Task GetPretranslatedUsfmAsync(string id, string corpusId, string textId, PretranslationUsfmTextOrigin? textOrigin = null, PretranslationUsfmTemplate? template = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { if (id == null) throw new System.ArgumentNullException("id"); @@ -3648,6 +3652,10 @@ public string BaseUrl { urlBuilder_.Append(System.Uri.EscapeDataString("text-origin")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(textOrigin, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); } + if (template != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("template")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(template, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } urlBuilder_.Length--; PrepareRequest(client_, request_, urlBuilder_); @@ -6009,6 +6017,21 @@ public enum PretranslationUsfmTextOrigin } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.2.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + public enum PretranslationUsfmTemplate + { + + [System.Runtime.Serialization.EnumMember(Value = @"Auto")] + Auto = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"Source")] + Source = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"Target")] + Target = 2, + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.2.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TranslationBuild { diff --git a/src/Serval/src/Serval.Translation/Contracts/PretranslationUsfmTemplate.cs b/src/Serval/src/Serval.Translation/Contracts/PretranslationUsfmTemplate.cs new file mode 100644 index 00000000..cf630d02 --- /dev/null +++ b/src/Serval/src/Serval.Translation/Contracts/PretranslationUsfmTemplate.cs @@ -0,0 +1,8 @@ +namespace Serval.Translation.Contracts; + +public enum PretranslationUsfmTemplate +{ + Auto, + Source, + Target +} diff --git a/src/Serval/src/Serval.Translation/Controllers/TranslationEnginesController.cs b/src/Serval/src/Serval.Translation/Controllers/TranslationEnginesController.cs index fa94ec3d..25cc3e5d 100644 --- a/src/Serval/src/Serval.Translation/Controllers/TranslationEnginesController.cs +++ b/src/Serval/src/Serval.Translation/Controllers/TranslationEnginesController.cs @@ -617,17 +617,19 @@ CancellationToken cancellationToken /// Get a pretranslated Scripture book in USFM format. /// /// - /// If the USFM book exists in the target corpus, then the pretranslated text will be inserted into any empty - /// segments in the the target book and returned. If the USFM book does not exist in the target corpus, then the - /// pretranslated text will be inserted into an empty template created from the source USFM book and returned. - /// Only pretranslations for the most recent successful build of the engine are returned. - /// - /// The text that populates the USFM structure can be controlled by the `textOrigin` parameter where with these options: - /// * `PreferExisting`: The existing and pretranslated texts are merged into the USFM, preferring existing text. **This is the default**. + /// The text that populates the USFM structure can be controlled by the `textOrigin` parameter: + /// * `PreferExisting`: The existing and pretranslated texts are merged into the USFM, preferring existing text. **This is the default**. /// * `PreferPretranslated`: The existing and pretranslated texts are merged into the USFM, preferring pretranslated text. - /// * `OnlyExisting`: Return the existing target USFM file with no modifications (except updating the USFM id if needed) - /// * `OnlyPretranslated`: Only the pretranslated text is returned; all existing text in the target USFM is removed - /// Both scripture and non-scripture text in the USFM is parsed and grouped according to [this wiki](https://github.com/sillsdev/serval/wiki/USFM-Parsing-and-Translation) + /// * `OnlyExisting`: Return the existing target USFM file with no modifications (except updating the USFM id if needed). + /// * `OnlyPretranslated`: Only the pretranslated text is returned; all existing text in the target USFM is removed. + /// + /// The source or target book can be used as the USFM template for the pretranslated text. The template can be controlled by the `template` parameter: + /// * `Auto`: The target book is used as the template if it exists; otherwise, the source book is used. **This is the default**. + /// * `Source`: The source book is used as the template. + /// * `Target`: The target book is used as the template. + /// + /// Only pretranslations for the most recent successful build of the engine are returned. + /// Both scripture and non-scripture text in the USFM is parsed and grouped according to [this wiki](https://github.com/sillsdev/serval/wiki/USFM-Parsing-and-Translation). /// /// The translation engine id /// The corpus id @@ -658,6 +660,7 @@ public async Task GetPretranslatedUsfmAsync( [NotNull] string corpusId, [NotNull] string textId, [FromQuery(Name = "text-origin")] PretranslationUsfmTextOrigin? textOrigin, + [FromQuery] PretranslationUsfmTemplate? template, CancellationToken cancellationToken ) { @@ -674,6 +677,7 @@ CancellationToken cancellationToken corpusId, textId, textOrigin ?? PretranslationUsfmTextOrigin.PreferExisting, + template ?? PretranslationUsfmTemplate.Auto, cancellationToken ); if (usfm == "") diff --git a/src/Serval/src/Serval.Translation/Services/IPretranslationService.cs b/src/Serval/src/Serval.Translation/Services/IPretranslationService.cs index dc626553..f0c15ba8 100644 --- a/src/Serval/src/Serval.Translation/Services/IPretranslationService.cs +++ b/src/Serval/src/Serval.Translation/Services/IPretranslationService.cs @@ -16,6 +16,7 @@ Task GetUsfmAsync( string corpusId, string textId, PretranslationUsfmTextOrigin textOrigin, + PretranslationUsfmTemplate template, CancellationToken cancellationToken = default ); } diff --git a/src/Serval/src/Serval.Translation/Services/PretranslationService.cs b/src/Serval/src/Serval.Translation/Services/PretranslationService.cs index 02fd41a7..8f167ce9 100644 --- a/src/Serval/src/Serval.Translation/Services/PretranslationService.cs +++ b/src/Serval/src/Serval.Translation/Services/PretranslationService.cs @@ -35,6 +35,7 @@ public async Task GetUsfmAsync( string corpusId, string textId, PretranslationUsfmTextOrigin textOrigin, + PretranslationUsfmTemplate template, CancellationToken cancellationToken = default ) { @@ -68,81 +69,87 @@ await GetAllAsync(engineId, modelRevision, corpusId, textId, cancellationToken) .OrderBy(p => p.Refs[0]); // Update the target book if it exists - string? usfm = await _scriptureDataFileService.ReadParatextProjectBookAsync(targetFile.Filename, textId); - if (usfm is not null) + if (template is PretranslationUsfmTemplate.Auto or PretranslationUsfmTemplate.Target) { - // the pretranslations are generated from the source book and inserted into the target book - // use relaxed references since the USFM structure may not be the same - pretranslations = pretranslations.Select(p => - ((IReadOnlyList)p.Refs.Select(r => r.ToRelaxed()).ToArray(), p.Translation) - ); - switch (textOrigin) + string? usfm = await _scriptureDataFileService.ReadParatextProjectBookAsync(targetFile.Filename, textId); + if (usfm is not null) { - case PretranslationUsfmTextOrigin.PreferExisting: - return UpdateUsfm( - targetSettings, - usfm, - pretranslations, - fullName: targetSettings.FullName, - stripAllText: false, - preferExistingText: true - ); - case PretranslationUsfmTextOrigin.PreferPretranslated: - return UpdateUsfm( - targetSettings, - usfm, - pretranslations, - fullName: targetSettings.FullName, - stripAllText: false, - preferExistingText: false - ); - case PretranslationUsfmTextOrigin.OnlyExisting: - return UpdateUsfm( - targetSettings, - usfm, - pretranslations: [], // don't put any pretranslations, we only want the existing text. - fullName: targetSettings.FullName, - stripAllText: false, - preferExistingText: false - ); - case PretranslationUsfmTextOrigin.OnlyPretranslated: - return UpdateUsfm( - targetSettings, - usfm, - pretranslations, - fullName: targetSettings.FullName, - stripAllText: true, - preferExistingText: false - ); + // the pretranslations are generated from the source book and inserted into the target book + // use relaxed references since the USFM structure may not be the same + pretranslations = pretranslations.Select(p => + ((IReadOnlyList)p.Refs.Select(r => r.ToRelaxed()).ToArray(), p.Translation) + ); + switch (textOrigin) + { + case PretranslationUsfmTextOrigin.PreferExisting: + return UpdateUsfm( + targetSettings, + usfm, + pretranslations, + fullName: targetSettings.FullName, + stripAllText: false, + preferExistingText: true + ); + case PretranslationUsfmTextOrigin.PreferPretranslated: + return UpdateUsfm( + targetSettings, + usfm, + pretranslations, + fullName: targetSettings.FullName, + stripAllText: false, + preferExistingText: false + ); + case PretranslationUsfmTextOrigin.OnlyExisting: + return UpdateUsfm( + targetSettings, + usfm, + pretranslations: [], // don't put any pretranslations, we only want the existing text. + fullName: targetSettings.FullName, + stripAllText: false, + preferExistingText: false + ); + case PretranslationUsfmTextOrigin.OnlyPretranslated: + return UpdateUsfm( + targetSettings, + usfm, + pretranslations, + fullName: targetSettings.FullName, + stripAllText: true, + preferExistingText: false + ); + } } } - // Copy and update the source book if it exists - usfm = await _scriptureDataFileService.ReadParatextProjectBookAsync(sourceFile.Filename, textId); - if (usfm is not null) + if (template is PretranslationUsfmTemplate.Auto or PretranslationUsfmTemplate.Source) { - switch (textOrigin) + // Copy and update the source book if it exists + string? usfm = await _scriptureDataFileService.ReadParatextProjectBookAsync(sourceFile.Filename, textId); + if (usfm is not null) { - case PretranslationUsfmTextOrigin.PreferExisting: - case PretranslationUsfmTextOrigin.PreferPretranslated: - case PretranslationUsfmTextOrigin.OnlyPretranslated: - return UpdateUsfm( - sourceSettings, - usfm, - pretranslations, - fullName: targetSettings.FullName, - stripAllText: true, - preferExistingText: true - ); - case PretranslationUsfmTextOrigin.OnlyExisting: - return UpdateUsfm( - sourceSettings, - usfm, - pretranslations: [], // don't pass the pretranslations, we only want the existing text. - fullName: targetSettings.FullName, - stripAllText: true, - preferExistingText: true - ); + switch (textOrigin) + { + case PretranslationUsfmTextOrigin.PreferExisting: + case PretranslationUsfmTextOrigin.PreferPretranslated: + case PretranslationUsfmTextOrigin.OnlyPretranslated: + return UpdateUsfm( + sourceSettings, + usfm, + pretranslations, + fullName: targetSettings.FullName, + stripAllText: true, + preferExistingText: true + ); + case PretranslationUsfmTextOrigin.OnlyExisting: + return UpdateUsfm( + sourceSettings, + usfm, + pretranslations: [], // don't pass the pretranslations, we only want the existing text. + fullName: targetSettings.FullName, + stripAllText: true, + preferExistingText: true + ); + } } } diff --git a/src/Serval/test/Serval.Translation.Tests/Services/NUnitExtensions.cs b/src/Serval/test/Serval.Translation.Tests/Services/NUnitExtensions.cs new file mode 100644 index 00000000..1b138714 --- /dev/null +++ b/src/Serval/test/Serval.Translation.Tests/Services/NUnitExtensions.cs @@ -0,0 +1,11 @@ +namespace Serval.Translation.Services; + +public static class NUnitExtensions +{ + public static EqualConstraint IgnoreLineEndings(this EqualConstraint constraint) + { + return constraint.Using( + (actual, expected) => actual.ReplaceLineEndings() == expected.ReplaceLineEndings() + ); + } +} diff --git a/src/Serval/test/Serval.Translation.Tests/Services/PretranslationServiceTests.cs b/src/Serval/test/Serval.Translation.Tests/Services/PretranslationServiceTests.cs index 6495b8b6..966d1023 100644 --- a/src/Serval/test/Serval.Translation.Tests/Services/PretranslationServiceTests.cs +++ b/src/Serval/test/Serval.Translation.Tests/Services/PretranslationServiceTests.cs @@ -3,29 +3,267 @@ [TestFixture] public class PretranslationServiceTests { + private const string SourceUsfm = + $@"\id MAT - SRC +\c 1 +\v 1 SRC - Chapter one, verse one. +\v 2 +\v 3 SRC - Chapter one, verse three. +"; + + private const string TargetUsfm = + @"\id MAT - TRG +\c 1 +\v 1 TRG - Chapter one, verse one. +\v 2 +\v 3 TRG - Chapter one, verse three. +"; + + [Test] + public async Task GetUsfmAsync_Source_PreferExisting() + { + TestEnvironment env = new(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferExisting, + PretranslationUsfmTemplate.Source + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 +" + ) + .IgnoreLineEndings() + ); + } + + [Test] + public async Task GetUsfmAsync_Source_PreferPretranslated() + { + TestEnvironment env = new(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferPretranslated, + PretranslationUsfmTemplate.Source + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 +" + ) + .IgnoreLineEndings() + ); + } + + [Test] + public async Task GetUsfmAsync_Source_OnlyExisting() + { + TestEnvironment env = new(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.OnlyExisting, + PretranslationUsfmTemplate.Source + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 +\v 2 +\v 3 +" + ) + .IgnoreLineEndings() + ); + } + + [Test] + public async Task GetUsfmAsync_Source_OnlyPretranslated() + { + TestEnvironment env = new(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.OnlyPretranslated, + PretranslationUsfmTemplate.Source + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 +" + ) + .IgnoreLineEndings() + ); + } + + [Test] + public async Task GetUsfmAsync_Target_PreferExisting() + { + TestEnvironment env = new(); + env.AddMatthewToTarget(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferExisting, + PretranslationUsfmTemplate.Target + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 TRG - Chapter one, verse one. +\v 2 Chapter 1, verse 2. +\v 3 TRG - Chapter one, verse three. +" + ) + .IgnoreLineEndings() + ); + } + + [Test] + public async Task GetUsfmAsync_Target_PreferPretranslated() + { + TestEnvironment env = new(); + env.AddMatthewToTarget(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferPretranslated, + PretranslationUsfmTemplate.Target + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 TRG - Chapter one, verse three. +" + ) + .IgnoreLineEndings() + ); + } + + [Test] + public async Task GetUsfmAsync_Target_TargetBookDoesNotExist() + { + TestEnvironment env = new(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferPretranslated, + PretranslationUsfmTemplate.Target + ); + + Assert.That(usfm, Is.EqualTo("")); + } + + [Test] + public async Task GetUsfmAsync_Auto_TargetBookDoesNotExist() + { + TestEnvironment env = new(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferPretranslated, + PretranslationUsfmTemplate.Auto + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 +" + ) + .IgnoreLineEndings() + ); + } + [Test] - [TestCase(PretranslationUsfmTextOrigin.PreferPretranslated, "OnlyPretranslated")] - [TestCase(PretranslationUsfmTextOrigin.PreferExisting, "OnlyPretranslated")] - [TestCase(PretranslationUsfmTextOrigin.OnlyPretranslated, "OnlyPretranslated")] - [TestCase(PretranslationUsfmTextOrigin.OnlyExisting, "Blank")] - public async Task GetUsfmAsync_SourceBook(PretranslationUsfmTextOrigin textOrigin, string returnUsfmType) + public async Task GetUsfmAsync_Auto_TargetBookExists() { TestEnvironment env = new(); - string usfm = await env.Service.GetUsfmAsync("engine1", 1, "corpus1", "MAT", textOrigin: textOrigin); - Assert.That(usfm.Replace("\r\n", "\n"), Is.EqualTo(TestEnvironment.GetUsfm(returnUsfmType))); + env.AddMatthewToTarget(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.PreferPretranslated, + PretranslationUsfmTemplate.Auto + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 TRG - Chapter one, verse three. +" + ) + .IgnoreLineEndings() + ); } [Test] - [TestCase(PretranslationUsfmTextOrigin.PreferPretranslated, "PreferPretranslated")] - [TestCase(PretranslationUsfmTextOrigin.PreferExisting, "PreferExisting")] - [TestCase(PretranslationUsfmTextOrigin.OnlyPretranslated, "OnlyPretranslated")] - [TestCase(PretranslationUsfmTextOrigin.OnlyExisting, "OnlyExisting")] - public async Task GetUsfmAsync_TargetBook(PretranslationUsfmTextOrigin textOrigin, string returnUsfmType) + public async Task GetUsfmAsync_Target_OnlyExisting() { TestEnvironment env = new(); env.AddMatthewToTarget(); - string usfm = await env.Service.GetUsfmAsync("engine1", 1, "corpus1", "MAT", textOrigin: textOrigin); - Assert.That(usfm.Replace("\r\n", "\n"), Is.EqualTo(TestEnvironment.GetUsfm(returnUsfmType))); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.OnlyExisting, + PretranslationUsfmTemplate.Target + ); + + Assert.That(usfm, Is.EqualTo(TargetUsfm).IgnoreLineEndings()); + } + + [Test] + public async Task GetUsfmAsync_Target_OnlyPretranslated() + { + TestEnvironment env = new(); + env.AddMatthewToTarget(); + + string usfm = await env.GetUsfmAsync( + PretranslationUsfmTextOrigin.OnlyPretranslated, + PretranslationUsfmTemplate.Target + ); + + Assert.That( + usfm, + Is.EqualTo( + @"\id MAT - TRG +\c 1 +\v 1 Chapter 1, verse 1. +\v 2 Chapter 1, verse 2. +\v 3 +" + ) + .IgnoreLineEndings() + ); } private class TestEnvironment @@ -96,16 +334,6 @@ public TestEnvironment() TextId = "MAT", Refs = ["MAT 1:2"], Translation = "Chapter 1, verse 2." - }, - new() - { - Id = "pt3", - EngineRef = "engine1", - ModelRevision = 1, - CorpusRef = "corpus1", - TextId = "MAT", - Refs = ["MAT 2:1"], - Translation = "Chapter 2, verse 1." } ] ); @@ -114,7 +342,7 @@ public TestEnvironment() ScriptureDataFileService.GetParatextProjectSettings("file2.zip").Returns(CreateProjectSettings("TRG")); ScriptureDataFileService .ReadParatextProjectBookAsync("file1.zip", "MAT") - .Returns(Task.FromResult(CreateExisting(book: "MAT", id: "MAT - SRC"))); + .Returns(Task.FromResult(SourceUsfm)); ScriptureDataFileService .ReadParatextProjectBookAsync("file2.zip", "MAT") .Returns(Task.FromResult(null)); @@ -126,11 +354,28 @@ public TestEnvironment() public MemoryRepository Engines { get; } public IScriptureDataFileService ScriptureDataFileService { get; } + public async Task GetUsfmAsync( + PretranslationUsfmTextOrigin textOrigin, + PretranslationUsfmTemplate template + ) + { + return ( + await Service.GetUsfmAsync( + engineId: "engine1", + modelRevision: 1, + corpusId: "corpus1", + textId: "MAT", + textOrigin: textOrigin, + template: template + ) + ).Replace("\r\n", "\n"); + } + public void AddMatthewToTarget() { ScriptureDataFileService .ReadParatextProjectBookAsync("file2.zip", "MAT") - .Returns(Task.FromResult(CreateExisting(book: "MAT", id: "MAT - TRG"))); + .Returns(Task.FromResult(TargetUsfm)); } private static ParatextProjectSettings CreateProjectSettings(string name) @@ -149,94 +394,5 @@ private static ParatextProjectSettings CreateProjectSettings(string name) biblicalTermsFileName: "BiblicalTerms.xml" ); } - - private static string CreateExisting(string book = "MAT", string id = "MAT - TRG") - { - return $@"\id {id} -\h {Canon.BookIdToEnglishName(book)} -\c 1 -\p -\v 1 Chapter one, verse one. -\v 2 -\c 2 -\p -\v 1 Chapter two, verse one. -\v 2 Chapter two, verse two. -"; - } - - private static string CreatePretranslationsOnly(string id = "MAT - TRG") - { - return $@"\id {id} -\h -\c 1 -\p -\v 1 Chapter 1, verse 1. -\v 2 Chapter 1, verse 2. -\c 2 -\p -\v 1 Chapter 2, verse 1. -\v 2 -"; - } - - private static string CreatePreferPretranslations(string book = "MAT", string id = "MAT - TRG") - { - return $@"\id {id} -\h {Canon.BookIdToEnglishName(book)} -\c 1 -\p -\v 1 Chapter 1, verse 1. -\v 2 Chapter 1, verse 2. -\c 2 -\p -\v 1 Chapter 2, verse 1. -\v 2 Chapter two, verse two. -"; - } - - private static string CreatePreferExisting(string book = "MAT", string id = "MAT - TRG") - { - return $@"\id {id} -\h {Canon.BookIdToEnglishName(book)} -\c 1 -\p -\v 1 Chapter one, verse one. -\v 2 Chapter 1, verse 2. -\c 2 -\p -\v 1 Chapter two, verse one. -\v 2 Chapter two, verse two. -"; - } - - private static string CreateBlank(string id = "MAT - TRG") - { - return $@"\id {id} -\h -\c 1 -\p -\v 1 -\v 2 -\c 2 -\p -\v 1 -\v 2 -"; - } - - public static string GetUsfm(string type, string book = "MAT", string id = "MAT - TRG") - { - string usfm = type switch - { - "OnlyPretranslated" => CreatePretranslationsOnly(id), - "PreferPretranslated" => CreatePreferPretranslations(book, id), - "PreferExisting" => CreatePreferExisting(book, id), - "OnlyExisting" => CreateExisting(book, id), - "Blank" => CreateBlank(id), - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; - return usfm.Replace("\r\n", "\n"); - } } } diff --git a/src/Serval/test/Serval.Translation.Tests/Usings.cs b/src/Serval/test/Serval.Translation.Tests/Usings.cs index ef8a3ff7..74d21283 100644 --- a/src/Serval/test/Serval.Translation.Tests/Usings.cs +++ b/src/Serval/test/Serval.Translation.Tests/Usings.cs @@ -6,6 +6,7 @@ global using Microsoft.Extensions.Options; global using NSubstitute; global using NUnit.Framework; +global using NUnit.Framework.Constraints; global using Serval.Shared.Configuration; global using Serval.Shared.Services; global using Serval.Shared.Utils;