Skip to content

Commit 0f5e300

Browse files
committed
fix webhook failure
1 parent f8801b8 commit 0f5e300

File tree

6 files changed

+365
-3
lines changed

6 files changed

+365
-3
lines changed

src/SIL.XForge.Scripture/Models/ParatextSettings.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using SIL.Scripture;
23

34
namespace SIL.XForge.Scripture.Models;
45

@@ -37,4 +38,5 @@ public class ParatextSettings
3738
public string? BaseProjectParatextId { get; init; }
3839
public string? CopyrightBanner { get; init; }
3940
public string? CopyrightNotice { get; init; }
41+
public ScrVers? Versification { get; init; }
4042
}

src/SIL.XForge.Scripture/SIL.XForge.Scripture.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<PackageReference Include="ParatextData" Version="9.5.0.8" />
4343
<PackageReference Include="Serval.Client" Version="1.9.1" />
4444
<PackageReference Include="SharpZipLib" Version="1.4.2" />
45+
<PackageReference Include="SIL.Machine" Version="3.6.4" />
4546
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.0.0" />
4647
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="8.0.0" />
4748
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />

src/SIL.XForge.Scripture/Services/MachineApiService.cs

+87-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Serval.Client;
1717
using SIL.Converters.Usj;
1818
using SIL.ObjectModel;
19+
using SIL.Scripture;
1920
using SIL.XForge.Configuration;
2021
using SIL.XForge.DataAccess;
2122
using SIL.XForge.EventMetrics;
@@ -380,7 +381,7 @@ CancellationToken cancellationToken
380381
ServalBuildDto? buildDto = null;
381382

382383
// Ensure that the user has permission
383-
await EnsureProjectPermissionAsync(curUserId, sfProjectId, isServalAdmin);
384+
SFProject project = await EnsureProjectPermissionAsync(curUserId, sfProjectId, isServalAdmin);
384385

385386
// Get the translation engine
386387
string translationEngineId = await GetTranslationIdAsync(sfProjectId, preTranslate: true);
@@ -395,6 +396,91 @@ await translationEnginesClient.GetAllBuildsAsync(translationEngineId, cancellati
395396
.MaxBy(b => b.DateFinished);
396397
if (translationBuild is not null)
397398
{
399+
// Verify that each book/chapter from the translationBuild is marked HasDraft = true
400+
// If the projects texts chapters are not all marked as having a draft, then the webhook likely failed
401+
// and we want to retrieve the pre-translation status to update the chapters as having a draft
402+
Dictionary<string, List<int>> scriptureRanges = [];
403+
404+
IList<PretranslateCorpus> pretranslateCorpus = translationBuild.Pretranslate ?? [];
405+
406+
// Retrieve the user secret
407+
Attempt<UserSecret> attempt = await userSecrets.TryGetAsync(curUserId);
408+
if (!attempt.TryResult(out UserSecret userSecret))
409+
{
410+
throw new DataNotFoundException("The user does not exist.");
411+
}
412+
413+
ScrVers versification = paratextService
414+
.GetParatextSettings(userSecret, project.ParatextId)
415+
.Versification;
416+
417+
ScriptureRangeParser scriptureRangeParser = new ScriptureRangeParser(versification);
418+
419+
// Create the dictionary of scripture range bookIds and bookNums to check against the project texts
420+
Dictionary<string, int> scriptureRangeBooks = [];
421+
422+
foreach (PretranslateCorpus ptc in pretranslateCorpus)
423+
{
424+
// We are using the TranslationBuild.Pretranslate.SourceFilters.ScriptureRange to find the
425+
// books selected for drafting. Some projects may have used the now obsolete field
426+
// TranslationBuild.Pretranslate.ScriptureRange and will not get checked for webhook failures.
427+
foreach (ParallelCorpusFilter source in ptc.SourceFilters ?? [])
428+
{
429+
foreach (
430+
(string book, List<int> bookChapters) in scriptureRangeParser.GetChapters(
431+
source.ScriptureRange
432+
)
433+
)
434+
{
435+
int bookNum = Canon.BookIdToNumber(book);
436+
scriptureRangeBooks.Add(book, bookNum);
437+
// Ensure that if chapters is blank, it contains every chapter in the book
438+
List<int> chapters = bookChapters;
439+
if (chapters.Count == 0)
440+
{
441+
chapters = [.. Enumerable.Range(1, versification.GetLastChapter(bookNum))];
442+
}
443+
444+
// Set or merge the list of chapters
445+
if (!scriptureRanges.TryGetValue(book, out List<int> existingChapters))
446+
{
447+
scriptureRanges[book] = chapters;
448+
}
449+
else
450+
{
451+
// Merge new chapters into existing list, avoiding duplicates
452+
foreach (int chapter in chapters.Where(chapter => !existingChapters.Contains(chapter)))
453+
{
454+
existingChapters.Add(chapter);
455+
}
456+
// add existing chapters to the books chapter list
457+
scriptureRanges[book].AddRange(existingChapters);
458+
}
459+
}
460+
}
461+
}
462+
463+
string[] scriptureRangeIds = [.. scriptureRanges.Keys];
464+
465+
// check if any chapters from the scripture range are marked as HasDraft = false or null
466+
bool hasDraftIsFalseOrNullInScriptureRange =
467+
scriptureRangeBooks.Count > 0
468+
? scriptureRangeBooks.All(kvp =>
469+
{
470+
return project.Texts.Any(text =>
471+
text.BookNum == kvp.Value
472+
&& text.Chapters.Where(chapter => scriptureRanges[kvp.Key].Contains(chapter.Number))
473+
.Any(c => !c.HasDraft ?? false)
474+
);
475+
})
476+
: false;
477+
478+
if (hasDraftIsFalseOrNullInScriptureRange)
479+
{
480+
// Chapters HasDraft is missing or false but should be true, retrieve the pre-translation status to update them.
481+
await RetrievePreTranslationStatusAsync(sfProjectId, cancellationToken);
482+
}
483+
398484
buildDto = CreateDto(translationBuild);
399485
}
400486
}

src/SIL.XForge.Scripture/Services/ParatextService.cs

+1
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,7 @@ string contextInformation
988988
BaseProjectShortName = scrText.Settings.TranslationInfo.BaseProjectName,
989989
CopyrightBanner = copyrightBanner,
990990
CopyrightNotice = copyrightNotice,
991+
Versification = scrText.Settings.Versification,
991992
};
992993
}
993994

src/SIL.XForge.Scripture/packages.lock.json

+121
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@
173173
"resolved": "1.4.2",
174174
"contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A=="
175175
},
176+
"SIL.Machine": {
177+
"type": "Direct",
178+
"requested": "[3.6.4, )",
179+
"resolved": "3.6.4",
180+
"contentHash": "TLdST7VKN1yu3GuvpSfnSK70hq8VQmJ0ftdOmjEtf5oHu5sSDIjc7N+KJ95ayd58I0MEGvFFlFHDz3awNNvo3Q==",
181+
"dependencies": {
182+
"CaseExtensions": "1.1.0",
183+
"Newtonsoft.Json": "13.0.2",
184+
"Nito.AsyncEx": "5.1.2",
185+
"SIL.Scripture": "12.0.1",
186+
"Sandwych.QuickGraph.Core": "1.0.0",
187+
"System.Text.Encoding.CodePages": "6.0.0",
188+
"System.Threading.Tasks.Dataflow": "6.0.0"
189+
}
190+
},
176191
"Swashbuckle.AspNetCore": {
177192
"type": "Direct",
178193
"requested": "[8.0.0, )",
@@ -279,6 +294,11 @@
279294
"Microsoft.Extensions.DiagnosticAdapter": "3.1.32"
280295
}
281296
},
297+
"CaseExtensions": {
298+
"type": "Transitive",
299+
"resolved": "1.1.0",
300+
"contentHash": "zEvNeodNmNai8aQ4YfhHKCTWmlMwXM8NHR+8x8WsgY5eT4eDW0yFGrO+voQ1lCzyAXJ1Djv6dUAiyraVTueapA=="
301+
},
282302
"Castle.Core": {
283303
"type": "Transitive",
284304
"resolved": "5.1.1",
@@ -882,6 +902,81 @@
882902
"Newtonsoft.Json": "12.0.1"
883903
}
884904
},
905+
"Nito.AsyncEx": {
906+
"type": "Transitive",
907+
"resolved": "5.1.2",
908+
"contentHash": "hq+N63M/2znx2z1VzvPDHNg+HIWKdIloEZre+P7E0O+2iRf1Q4HBOgeiJU6SzFD/fWoyKyKSSSrekk4RgiXaeQ==",
909+
"dependencies": {
910+
"Nito.AsyncEx.Context": "5.1.2",
911+
"Nito.AsyncEx.Coordination": "5.1.2",
912+
"Nito.AsyncEx.Interop.WaitHandles": "5.1.2",
913+
"Nito.AsyncEx.Oop": "5.1.2",
914+
"Nito.AsyncEx.Tasks": "5.1.2",
915+
"Nito.Cancellation": "1.1.2"
916+
}
917+
},
918+
"Nito.AsyncEx.Context": {
919+
"type": "Transitive",
920+
"resolved": "5.1.2",
921+
"contentHash": "rMwL7Nj3oNyvFu/jxUzQ/YBobEkM2RQHe+5mpCDRyq6mfD7vCj7Z3rjB6XgpM6Mqcx1CA2xGv0ascU/2Xk8IIg==",
922+
"dependencies": {
923+
"Nito.AsyncEx.Tasks": "5.1.2"
924+
}
925+
},
926+
"Nito.AsyncEx.Coordination": {
927+
"type": "Transitive",
928+
"resolved": "5.1.2",
929+
"contentHash": "QMyUfsaxov//0ZMbOHWr9hJaBFteZd66DV1ay4J5wRODDb8+K/uHC7+3VsOflo6SVw/29mu8OWZp8vMDSuzc0w==",
930+
"dependencies": {
931+
"Nito.AsyncEx.Tasks": "5.1.2",
932+
"Nito.Collections.Deque": "1.1.1"
933+
}
934+
},
935+
"Nito.AsyncEx.Interop.WaitHandles": {
936+
"type": "Transitive",
937+
"resolved": "5.1.2",
938+
"contentHash": "qym29lFBCSIacKvFcJDW+beXzuO+6y9lWdd1KecxzzAqtNuvlYgNPwIsxwdhEINLhTT4aDuCM3JalpUZYWI51Q==",
939+
"dependencies": {
940+
"Nito.AsyncEx.Tasks": "5.1.2"
941+
}
942+
},
943+
"Nito.AsyncEx.Oop": {
944+
"type": "Transitive",
945+
"resolved": "5.1.2",
946+
"contentHash": "MxQl/NFoPgMApyjbB2fSZBrjdf9r6ODd/BTrWLyJKYX6UeNfw0Ocr0cPiTg2LRN0Ayud8Gj4dh67AdasNn709Q==",
947+
"dependencies": {
948+
"Nito.AsyncEx.Coordination": "5.1.2"
949+
}
950+
},
951+
"Nito.AsyncEx.Tasks": {
952+
"type": "Transitive",
953+
"resolved": "5.1.2",
954+
"contentHash": "jEkCfR2/M26OK/U4G7SEN063EU/F4LiVA06TtpZILMdX/quIHCg+wn31Zerl2LC+u1cyFancjTY3cNAr2/89PA==",
955+
"dependencies": {
956+
"Nito.Disposables": "2.2.1"
957+
}
958+
},
959+
"Nito.Cancellation": {
960+
"type": "Transitive",
961+
"resolved": "1.1.2",
962+
"contentHash": "Z+SZKp0KxMC6tEVbXe8ah4pBJadyqP0pObQMaZcBavhIDEIsGuxt7PL+B9AiNJD3Ni5VgnZsnii5HPJgVDE81w==",
963+
"dependencies": {
964+
"Nito.Disposables": "2.2.1"
965+
}
966+
},
967+
"Nito.Collections.Deque": {
968+
"type": "Transitive",
969+
"resolved": "1.1.1",
970+
"contentHash": "CU0/Iuv5VDynK8I8pDLwkgF0rZhbQoZahtodfL0M3x2gFkpBRApKs8RyMyNlAi1mwExE4gsmqQXk4aFVvW9a4Q=="
971+
},
972+
"Nito.Disposables": {
973+
"type": "Transitive",
974+
"resolved": "2.2.1",
975+
"contentHash": "6sZ5uynQeAE9dPWBQGKebNmxbY4xsvcc5VplB5WkYEESUS7oy4AwnFp0FhqxTSKm/PaFrFqLrYr696CYN8cugg==",
976+
"dependencies": {
977+
"System.Collections.Immutable": "1.7.1"
978+
}
979+
},
885980
"ParatextCorePluginInterfaces": {
886981
"type": "Transitive",
887982
"resolved": "2.0.100",
@@ -1069,6 +1164,14 @@
10691164
"resolved": "4.4.0",
10701165
"contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
10711166
},
1167+
"Sandwych.QuickGraph.Core": {
1168+
"type": "Transitive",
1169+
"resolved": "1.0.0",
1170+
"contentHash": "OJEAm5kik+51yBaDfDuSD9+bx3dI23IvcfFcSL+H6v/YfAYtHbiMkAn5aOljNlkHSig3uakMRGhCTYr3lDKrEA==",
1171+
"dependencies": {
1172+
"System.Diagnostics.Contracts": "4.3.0"
1173+
}
1174+
},
10721175
"SharpCompress": {
10731176
"type": "Transitive",
10741177
"resolved": "0.30.1",
@@ -1186,6 +1289,11 @@
11861289
"System.Threading.Tasks": "4.3.0"
11871290
}
11881291
},
1292+
"System.Collections.Immutable": {
1293+
"type": "Transitive",
1294+
"resolved": "1.7.1",
1295+
"contentHash": "B43Zsz5EfMwyEbnObwRxW5u85fzJma3lrDeGcSAV1qkhSRTNY5uXAByTn9h9ddNdhM+4/YoLc/CI43umjwIl9Q=="
1296+
},
11891297
"System.ComponentModel.Annotations": {
11901298
"type": "Transitive",
11911299
"resolved": "5.0.0",
@@ -1228,6 +1336,14 @@
12281336
"System.Diagnostics.PerformanceCounter": "6.0.2"
12291337
}
12301338
},
1339+
"System.Diagnostics.Contracts": {
1340+
"type": "Transitive",
1341+
"resolved": "4.3.0",
1342+
"contentHash": "eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==",
1343+
"dependencies": {
1344+
"System.Runtime": "4.3.0"
1345+
}
1346+
},
12311347
"System.Diagnostics.Debug": {
12321348
"type": "Transitive",
12331349
"resolved": "4.3.0",
@@ -1939,6 +2055,11 @@
19392055
"System.Runtime": "4.3.0"
19402056
}
19412057
},
2058+
"System.Threading.Tasks.Dataflow": {
2059+
"type": "Transitive",
2060+
"resolved": "6.0.0",
2061+
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
2062+
},
19422063
"System.ValueTuple": {
19432064
"type": "Transitive",
19442065
"resolved": "4.5.0",

0 commit comments

Comments
 (0)