Skip to content

Commit 1cfdd5f

Browse files
SF-3397 Synchronize drafting sources if needed when configured (#3320)
1 parent 34b95f8 commit 1cfdd5f

File tree

4 files changed

+73
-16
lines changed

4 files changed

+73
-16
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-sources/draft-sources.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ <h3>{{ t("overview_source") }} {{ parentheses(sourceLanguageDisplayName) }}</h3>
213213
} @else if (getControlState("projectSettings") === ElementState.Submitted) {
214214
<mat-icon class="success">checkmark</mat-icon> {{ t("all_changes_saved") }}
215215
} @else {
216-
<mat-icon class="failure">error</mat-icon> Failed to save changes
216+
<mat-icon class="failure">error</mat-icon> {{ t("failed_to_save_changes") }}
217217
}
218218
</div>
219219
@for (entry of syncStatus | keyvalue; track entry.key) {

src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@
301301
"confirm_language_codes": "Please confirm that the language codes are correct before saving.",
302302
"discard_changes_confirmation": "Are you sure you want to leave the page with unsaved changes?",
303303
"draft_source": "Draft source",
304+
"failed_to_save_changes": "Failed to save changes.",
304305
"generate_draft_from_language_model": "Generate draft from language model",
305306
"language_code": "Language code: ",
306307
"leave_and_discard": "Leave & discard changes",
@@ -334,7 +335,6 @@
334335
"unknown_language_code": "unknown"
335336
},
336337
"draft_usfm_format": {
337-
"changes_have_been_saved": "Changes have been saved",
338338
"cancel": "Cancel",
339339
"connect_to_the_internet": "Please connect to the internet to change the USFM format for your draft",
340340
"draft_format_description": "The draft has been created. Here are some formatting options for the text. You can try each option to see the difference it makes.",

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public async Task<string> CreateProjectAsync(string curUserId, SFProjectCreateSe
172172
curUserId,
173173
projectDoc.Id,
174174
settings.SourceParatextId,
175-
syncIfCreated: false,
175+
skipSync: true,
176176
updatePermissions: false,
177177
ptProjects,
178178
resources
@@ -418,7 +418,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
418418
curUserId,
419419
projectId,
420420
settings.SourceParatextId,
421-
syncIfCreated: false,
421+
skipSync: true,
422422
updatePermissions: true,
423423
ptProjects,
424424
resources,
@@ -440,7 +440,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
440440
curUserId,
441441
projectId,
442442
settings.AlternateSourceParatextId,
443-
syncIfCreated: true,
443+
skipSync: false,
444444
// Only update permissions if this project is different to the preceding project
445445
updatePermissions: settings.SourceParatextId != settings.AlternateSourceParatextId,
446446
ptProjects,
@@ -463,7 +463,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
463463
curUserId,
464464
projectId,
465465
settings.AlternateTrainingSourceParatextId,
466-
syncIfCreated: true,
466+
skipSync: false,
467467
// Only update permissions if this project is different to the preceding projects
468468
updatePermissions: settings.SourceParatextId != settings.AlternateTrainingSourceParatextId
469469
&& settings.AlternateSourceParatextId != settings.AlternateTrainingSourceParatextId,
@@ -487,7 +487,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
487487
curUserId,
488488
projectId,
489489
settings.AdditionalTrainingSourceParatextId,
490-
syncIfCreated: true,
490+
skipSync: false,
491491
// Only update permissions if this project is different to the preceding projects
492492
updatePermissions: settings.SourceParatextId != settings.AdditionalTrainingSourceParatextId
493493
&& settings.AlternateSourceParatextId != settings.AdditionalTrainingSourceParatextId
@@ -1927,7 +1927,7 @@ private async Task<string> CreateResourceProjectInternalAsync(IConnection conn,
19271927
/// <param name="curUserId">The current user identifier.</param>
19281928
/// <param name="sfProjectId">The Scripture Forge project identifier.</param>
19291929
/// <param name="paratextId">The paratext identifier.</param>
1930-
/// <param name="syncIfCreated">If <c>true</c> sync the project if it is created.</param>
1930+
/// <param name="skipSync">If <c>true</c> the project will not be synced even if it is not already synced.</param>
19311931
/// <param name="updatePermissions">If <c>true</c> update the project's permissions.</param>
19321932
/// <param name="ptProjects">The paratext projects.</param>
19331933
/// <param name="resources">The paratext resources.</param>
@@ -1939,7 +1939,7 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19391939
string curUserId,
19401940
string sfProjectId,
19411941
string paratextId,
1942-
bool syncIfCreated,
1942+
bool skipSync,
19431943
bool updatePermissions,
19441944
IEnumerable<ParatextProject> ptProjects,
19451945
IEnumerable<ParatextResource> resources,
@@ -1948,25 +1948,25 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19481948
{
19491949
ParatextProject sourcePTProject = ptProjects.SingleOrDefault(p => p.ParatextId == paratextId);
19501950
string sourceProjectRef;
1951-
if (sourcePTProject == null)
1951+
if (sourcePTProject is null)
19521952
{
19531953
// If it is not a project, see if there is a matching resource
19541954
sourcePTProject = resources.SingleOrDefault(r => r.ParatextId == paratextId);
1955-
if (sourcePTProject == null)
1955+
if (sourcePTProject is null)
19561956
{
19571957
throw new DataNotFoundException("The source paratext project does not exist.");
19581958
}
19591959
}
19601960

19611961
// Get the users who will access this source resource or project
1962-
IEnumerable<string> userIds = userRoles != null ? userRoles.Keys : [curUserId];
1962+
IEnumerable<string> userIds = userRoles is not null ? userRoles.Keys : [curUserId];
19631963

19641964
// Get the project reference
19651965
SFProject sourceProject = RealtimeService
19661966
.QuerySnapshots<SFProject>()
19671967
.FirstOrDefault(p => p.ParatextId == paratextId);
19681968
bool projectCreated;
1969-
if (sourceProject != null)
1969+
if (sourceProject is not null)
19701970
{
19711971
sourceProjectRef = sourceProject.Id;
19721972
projectCreated = false;
@@ -1983,7 +1983,7 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19831983
try
19841984
{
19851985
// Add the user to the project, if the user does not have a role in it
1986-
if (sourceProject == null || !sourceProject.UserRoles.ContainsKey(userId))
1986+
if (sourceProject is null || !sourceProject.UserRoles.ContainsKey(userId))
19871987
{
19881988
await AddUserAsync(conn, userId, sourceProjectRef, null);
19891989
}
@@ -1994,9 +1994,11 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19941994
}
19951995
}
19961996

1997-
// If the project is created, sync it only if we need to
1997+
// If the project is created or the last sync was not successful, sync it unless explicitly skipped.
19981998
// This is usually because this is an alternate source for drafting
1999-
if (projectCreated && syncIfCreated)
1999+
bool syncNeeded =
2000+
projectCreated || (sourceProject is not null && sourceProject.Sync.LastSyncSuccessful == false);
2001+
if (syncNeeded && !skipSync)
20002002
{
20012003
string jobId = await _syncService.SyncAsync(
20022004
new SyncConfig { ProjectId = sourceProjectRef, UserId = curUserId }

test/SIL.XForge.Scripture.Tests/Services/SFProjectServiceTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public class SFProjectServiceTests
3939
private const string Resource01 = "resource_project";
4040
private const string SourceOnly = "source_only";
4141
private const string Resource01PTId = "resid_is_16_char";
42+
private const string ResourceNeedsSync = "resource_sync_needed";
43+
private const string ResourceNeedsSyncPTId = "resource_sync_id";
4244
private const string User01 = "user01";
4345
private const string User02 = "user02";
4446
private const string User03 = "user03";
@@ -2423,6 +2425,40 @@ await env
24232425
);
24242426
}
24252427

2428+
[Test]
2429+
public async Task UpdateSettingsAsync_ChangeAlternateSource_SyncProjectWhenSyncFailed()
2430+
{
2431+
var env = new TestEnvironment();
2432+
const string newProjectParatextId = ResourceNeedsSyncPTId;
2433+
2434+
// Ensure that the new project does not exist
2435+
Assert.That(
2436+
env.RealtimeService.GetRepository<SFProject>().Query().Any(p => p.ParatextId == newProjectParatextId),
2437+
Is.True
2438+
);
2439+
2440+
// SUT
2441+
await env.Service.UpdateSettingsAsync(
2442+
User01,
2443+
Project01,
2444+
new SFProjectSettings { AlternateSourceParatextId = newProjectParatextId }
2445+
);
2446+
2447+
SFProject project = env.GetProject(Project01);
2448+
Assert.That(project.TranslateConfig.DraftConfig.AlternateSource?.ProjectRef, Is.Not.Null);
2449+
Assert.That(project.TranslateConfig.DraftConfig.AlternateSource?.ParatextId, Is.EqualTo(newProjectParatextId));
2450+
Assert.That(project.TranslateConfig.DraftConfig.AlternateSource?.Name, Is.EqualTo("Resource Needs Sync"));
2451+
2452+
SFProject alternateSourceProject = env.GetProject(
2453+
project.TranslateConfig.DraftConfig.AlternateSource!.ProjectRef
2454+
);
2455+
Assert.That(alternateSourceProject.ParatextId, Is.EqualTo(newProjectParatextId));
2456+
Assert.That(alternateSourceProject.Name, Is.EqualTo("Resource Needs Sync"));
2457+
2458+
await env.SyncService.Received(1).SyncAsync(Arg.Any<SyncConfig>());
2459+
env.BackgroundJobClient.Received().Create(Arg.Any<Job>(), Arg.Any<IState>());
2460+
}
2461+
24262462
[Test]
24272463
public async Task UpdateSettingsAsync_ChangeAlternateTrainingSource_CannotUseTargetProject()
24282464
{
@@ -4972,6 +5008,15 @@ public TestEnvironment()
49725008
ShortName = "DSP",
49735009
UserRoles = { { User01, SFProjectRole.Administrator } },
49745010
},
5011+
new SFProject
5012+
{
5013+
Id = ResourceNeedsSync,
5014+
ParatextId = ResourceNeedsSyncPTId,
5015+
Name = "Resource Needs Sync",
5016+
ShortName = "RNSP",
5017+
UserRoles = { { User01, SFProjectRole.Administrator } },
5018+
Sync = new Sync { LastSyncSuccessful = false },
5019+
},
49755020
]
49765021
)
49775022
);
@@ -5295,6 +5340,12 @@ public TestEnvironment()
52955340
LanguageTag = "qaa",
52965341
},
52975342
new ParatextResource { ParatextId = GetProject(Resource01).ParatextId },
5343+
new ParatextResource
5344+
{
5345+
ParatextId = ResourceNeedsSyncPTId,
5346+
Name = "Resource Needs Sync",
5347+
LanguageTag = "en",
5348+
},
52985349
];
52995350
ParatextService.GetResourcesAsync(Arg.Any<string>()).Returns(ptResources);
53005351
ParatextService.CanUserAuthenticateToPTRegistryAsync(Arg.Any<UserSecret>()).Returns(Task.FromResult(true));
@@ -5421,6 +5472,10 @@ public TestEnvironment()
54215472
callInfo.ArgAt<string?>(0)?.Length == SFInstallableDblResource.ResourceIdentifierLength
54225473
);
54235474

5475+
ParatextService
5476+
.GetResourcePermissionAsync(Arg.Any<string>(), Arg.Any<string>(), CancellationToken.None)
5477+
.Returns(TextInfoPermission.Read);
5478+
54245479
Service = new SFProjectService(
54255480
RealtimeService,
54265481
siteOptions,

0 commit comments

Comments
 (0)