Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/RealtimeServer/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,24 @@ export = {
}

// Build the ops from a diff
// NOTE: We do not use diff-patch-match, as that may result in
// op conflicts when ops are submitted from multiple sources.
// diff-patch-match mutates the string, but we want to replace it.
const ops = json0OtDiff(doc.data, data);
let ops: any;
let hasOps: boolean;
if (doc.type?.name == OTJson0.type.name) {
// NOTE: We do not use diff-patch-match, as that may result in
// op conflicts when ops are submitted from multiple sources.
// diff-patch-match mutates the string, but we want to replace it.
ops = json0OtDiff(doc.data, data);
hasOps = ops.length > 0;
} else if (doc.type?.name == RichText.type.name) {
ops = new RichText.Delta(doc.data.ops).diff(new RichText.Delta(data.ops));
hasOps = ops.ops.length > 0;
} else {
callback(new Error('Unsupported document type.'));
return;
}

// Submit the ops
if (ops.length > 0) {
if (hasOps) {
const options: any = {};
doc.submitSource = source != null;
if (source != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ describe('SFProjectService', () => {
}));
});

describe('onlineApplyPreTranslationToProject', () => {
it('should invoke the command service', fakeAsync(async () => {
const env = new TestEnvironment();
const projectId = 'project01';
const scriptureRange = 'GEN-REV';
const targetProjectId = 'project01';
const timestamp = new Date();
await env.service.onlineApplyPreTranslationToProject(projectId, scriptureRange, targetProjectId, timestamp);
verify(mockedCommandService.onlineInvoke(anything(), 'applyPreTranslationToProject', anything())).once();
expect().nothing();
}));
});

class TestEnvironment {
readonly httpTestingController: HttpTestingController;
readonly service: SFProjectService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,20 @@ export class SFProjectService extends ProjectService<SFProject, SFProjectDoc> {
return this.onlineInvoke('cancelSync', { projectId: id });
}

onlineApplyPreTranslationToProject(
projectId: string,
scriptureRange: string,
targetProjectId: string,
timestamp: Date
): Promise<void> {
return this.onlineInvoke<void>('applyPreTranslationToProject', {
projectId,
scriptureRange,
targetProjectId,
timestamp: timestamp.toISOString()
});
}

onlineSetPreTranslate(projectId: string, preTranslate: boolean): Promise<void> {
return this.onlineInvoke<void>('setPreTranslate', {
projectId,
Expand Down
39 changes: 39 additions & 0 deletions src/SIL.XForge.Scripture/Controllers/SFProjectsRpcController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using EdjCase.JsonRpc.Router.Abstractions;
Expand Down Expand Up @@ -34,6 +35,44 @@ IUserAccessor userAccessor
// Keep a reference in this class to prevent duplicate allocation (Warning CS9107)
private readonly IExceptionHandler _exceptionHandler = exceptionHandler;

public IRpcMethodResult ApplyPreTranslationToProject(
string projectId,
string scriptureRange,
string targetProjectId,
DateTime timestamp
)
{
try
{
// Run the background job
backgroundJobClient.Enqueue<IMachineApiService>(r =>
r.ApplyPreTranslationToProjectAsync(
UserId,
projectId,
scriptureRange,
targetProjectId,
timestamp,
CancellationToken.None
)
);
return Ok();
}
catch (Exception)
{
_exceptionHandler.RecordEndpointInfoForException(
new Dictionary<string, string>
{
{ "method", "ApplyPreTranslationToProject" },
{ "projectId", projectId },
{ "scriptureRange", scriptureRange },
{ "targetProjectId", targetProjectId },
{ "timestamp", timestamp.ToString(CultureInfo.InvariantCulture) },
}
);
throw;
}
}

public async Task<IRpcMethodResult> Create(SFProjectCreateSettings settings)
{
try
Expand Down
24 changes: 24 additions & 0 deletions src/SIL.XForge.Scripture/Models/DraftApplyResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;

namespace SIL.XForge.Scripture.Models;

/// <summary>
/// The result of applying a draft.
/// </summary>
public class DraftApplyResult
{
/// <summary>
/// Whether changes were saved to the database.
/// </summary>
public bool ChangesSaved { get; set; }

/// <summary>
/// A list of any chapters that failed to apply in the format "GEN 1".
/// </summary>
public List<string> Failures = [];

/// <summary>
/// A log containing any warnings or errors that occurred while applying the draft.
/// </summary>
public string Log { get; set; } = string.Empty;
}
8 changes: 8 additions & 0 deletions src/SIL.XForge.Scripture/Models/DraftApplyState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SIL.XForge.Scripture.Models;

public class DraftApplyState
{
public bool Failed { get; set; }
public string? State { get; set; }
public bool Success { get; set; }
}
12 changes: 11 additions & 1 deletion src/SIL.XForge.Scripture/Services/DeltaUsxMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.Schema;
using Microsoft.Extensions.Logging;
Expand All @@ -11,7 +12,7 @@

namespace SIL.XForge.Scripture.Services;

public class DeltaUsxMapper(
public partial class DeltaUsxMapper(
IGuidService guidService,
ILogger<DeltaUsxMapper> logger,
IExceptionHandler exceptionHandler
Expand Down Expand Up @@ -171,6 +172,15 @@ private static bool CanParaContainVerseText(string? style)
return ParagraphPoetryListStyles.Contains(style);
}

[GeneratedRegex(@"\\id\s+(\w+)", RegexOptions.Compiled)]
private static partial Regex BookIdRegex();

public static string ExtractBookId(string usfm)
{
string firstLine = usfm.Split('\n').FirstOrDefault()?.Trim() ?? string.Empty;
return BookIdRegex().Match(firstLine).Groups[1].Value;
}

/// <summary>
/// Create list of ChapterDelta objects from USX.
///
Expand Down
11 changes: 11 additions & 0 deletions src/SIL.XForge.Scripture/Services/IMachineApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ namespace SIL.XForge.Scripture.Services;
[Intercept(typeof(EventMetricLogger))]
public interface IMachineApiService
{
[Mutex]
[LogEventMetric(EventScope.Drafting, nameof(curUserId), nameof(sfProjectId), captureReturnValue: true)]
Task<DraftApplyResult> ApplyPreTranslationToProjectAsync(
string curUserId,
string sfProjectId,
string scriptureRange,
string targetProjectId,
DateTime timestamp,
CancellationToken cancellationToken
);

[LogEventMetric(EventScope.Drafting, nameof(sfProjectId))]
Task BuildCompletedAsync(string sfProjectId, string buildId, string buildState, Uri websiteUrl);

Expand Down
1 change: 1 addition & 0 deletions src/SIL.XForge.Scripture/Services/INotifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace SIL.XForge.Scripture.Services;
public interface INotifier
{
Task NotifyBuildProgress(string sfProjectId, ServalBuildState buildState);
Task NotifyDraftApplyProgress(string sfProjectId, DraftApplyState draftApplyState);
Task NotifySyncProgress(string sfProjectId, ProgressState progressState);
Task SubscribeToProject(string projectId);
}
1 change: 1 addition & 0 deletions src/SIL.XForge.Scripture/Services/ISFProjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Task UpdatePermissionsAsync(
string curUserId,
IDocument<SFProject> projectDoc,
IReadOnlyList<ParatextProjectUser>? users = null,
IReadOnlyList<int>? books = null,
CancellationToken token = default
);
Task EnsureWritingSystemTagIsSetAsync(string curUserId, string projectId);
Expand Down
Loading
Loading