Skip to content
Merged
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
2 changes: 2 additions & 0 deletions src/RealtimeServer/scriptureforge/models/translate-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export interface DraftConfig {
sendEmailOnBuildFinished?: boolean;
currentScriptureRange?: string;
draftedScriptureRange?: string;
draftInProgress?: boolean;
lastDraftSuccessful?: boolean;
}

export interface TranslateConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ export class SFProjectService extends ProjectService<SFProject> {
},
draftedScriptureRange: {
bsonType: 'string'
},
lastDraftSuccessful: {
bsonType: 'bool'
},
draftInProgress: {
bsonType: 'bool'
}
},
additionalProperties: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
<a mat-list-item [appRouterLink]="draftGenerationLink" [class.active]="draftGenerationActive">
<mat-icon class="mirror-rtl">auto_awesome</mat-icon>
{{ t("generate_draft") }}
@if (draftInProgress) {
<mat-spinner class="status-icon" [diameter]="20" [strokeWidth]="3"></mat-spinner>
} @else if (lastDraftFailed) {
<mat-icon class="status-icon" color="warn">error</mat-icon>
}
</a>
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,13 @@ mat-nav-list .mdc-list-item {
color: var(--sf-disabled-foreground) !important;
}
}

.status-icon {
margin-inline-start: auto;
margin-inline-end: 8px;
}

:host-context([dir='rtl']) .status-icon {
margin-inline-end: auto;
margin-inline-start: 8px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Component, DestroyRef, EventEmitter, Output } from '@angular/core';
import { MatBadge } from '@angular/material/badge';
import { MatIcon } from '@angular/material/icon';
import { MatListItem, MatNavList } from '@angular/material/list';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { Router } from '@angular/router';
import { TranslocoModule } from '@ngneat/transloco';
import { Operation } from 'realtime-server/lib/esm/common/models/project-rights';
Expand All @@ -28,7 +29,16 @@ import { NmtDraftAuthGuard, SettingsAuthGuard, SyncAuthGuard, UsersAuthGuard } f
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss'],
imports: [TranslocoModule, MatNavList, MatListItem, RouterLinkDirective, MatIcon, MatBadge, AsyncPipe]
imports: [
AsyncPipe,
MatBadge,
MatIcon,
MatListItem,
MatNavList,
MatProgressSpinner,
RouterLinkDirective,
TranslocoModule
]
})
export class NavigationComponent {
private readonly projectChanges$ = this.activatedProjectService.changes$.pipe(
Expand Down Expand Up @@ -92,6 +102,14 @@ export class NavigationComponent {
return this.onlineStatusService.isOnline;
}

get lastDraftFailed(): boolean {
return this.selectedProjectDoc?.data?.translateConfig.draftConfig.lastDraftSuccessful === false;
}

get draftInProgress(): boolean {
return this.selectedProjectDoc?.data?.translateConfig.draftConfig.draftInProgress === true;
}

get lastSyncFailed(): boolean {
return this.selectedProjectDoc?.data?.sync.lastSyncSuccessful === false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ function setUpMocks(args: StoryState): void {
queuedCount: args.syncInProgress ? 1 : 0,
lastSyncSuccessful: args.lastSyncSuccessful
},
translateConfig: {
draftConfig: {
draftInProgress: args.draftInProgress,
lastDraftSuccessful: args.lastDraftSuccessful
}
},
userRoles: {
[userId]: args.role
}
Expand Down Expand Up @@ -162,6 +168,8 @@ export default meta;
interface StoryState {
role: SFProjectRole;
online: boolean;
draftInProgress: boolean;
lastDraftSuccessful: boolean;
syncInProgress: boolean;
lastSyncSuccessful: boolean;
checkingEnabled: boolean;
Expand All @@ -174,6 +182,8 @@ export const Default: Story = {
args: {
role: SFProjectRole.ParatextAdministrator,
online: true,
draftInProgress: false,
lastDraftSuccessful: true,
syncInProgress: false,
lastSyncSuccessful: true,
checkingEnabled: true,
Expand Down Expand Up @@ -246,6 +256,40 @@ export const Commenter: Story = {
}
};

export const DraftNotGenerated: Story = {
args: { ...Default.args, lastDraftSuccessful: undefined, draftInProgress: false },
play: async ({ canvasElement }) => {
expect(canvasElement.querySelector('mat-spinner.status-icon')).toBeNull();
expect(canvasElement.querySelector('mat-icon.status-icon')).toBeNull();
}
};

export const DraftInProgress: Story = {
args: { ...Default.args, draftInProgress: true },
play: async ({ canvasElement }) => {
expect(canvasElement.querySelector('mat-icon.status-icon')).toBeNull();
expect(canvasElement.querySelector('mat-spinner.status-icon')).toBeTruthy();
}
};

export const DraftInProgressRTL: Story = {
args: { ...Default.args, draftInProgress: true },
parameters: { locale: 'ar' }
};

export const DraftFailed: Story = {
args: { ...Default.args, lastDraftSuccessful: false },
play: async ({ canvasElement }) => {
expect(canvasElement.querySelector('mat-spinner.status-icon')).toBeNull();
expect(canvasElement.querySelector('mat-icon.status-icon')).toBeTruthy();
}
};

export const DraftFailedRTL: Story = {
args: { ...Default.args, lastDraftSuccessful: false },
parameters: { locale: 'ar' }
};

export const SyncInProgress: Story = {
args: { ...Default.args, syncInProgress: true },
play: async ({ canvasElement }) => {
Expand Down
2 changes: 2 additions & 0 deletions src/SIL.XForge.Scripture/Models/DraftConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ public class DraftConfig
/// This is a combination of the scripture ranges of previous drafts.
/// </remarks>
public string? DraftedScriptureRange { get; set; }
public bool? DraftInProgress { get; set; }
public bool? LastDraftSuccessful { get; set; }
}
25 changes: 25 additions & 0 deletions src/SIL.XForge.Scripture/Services/MachineApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,19 @@ CancellationToken cancellationToken
Activity.Current?.AddTag(MachineProjectService.DraftGenerationRequestIdKey, draftGenerationRequestId);
}

// Load the project from the realtime service
await using IConnection conn = await realtimeService.ConnectAsync();
IDocument<SFProject> projectDoc = await conn.FetchAsync<SFProject>(sfProjectId);
if (projectDoc.IsLoaded)
{
// Notify the UI that a draft is in progress
await projectDoc.SubmitJson0OpAsync(op =>
{
op.Set(pd => pd.TranslateConfig.DraftConfig.DraftInProgress, false);
op.Set(pd => pd.TranslateConfig.DraftConfig.LastDraftSuccessful, buildState != JobState.Faulted);
});
}

// Retrieve the build started from the event metric. We do this as there may be multiple builds started,
// and this ensures that only builds that want to send an email will have one sent.
var eventMetrics = await eventMetricService.GetEventMetricsAsync(
Expand Down Expand Up @@ -879,6 +892,17 @@ await projectSecrets.UpdateAsync(
);
}

// Load the project from the realtime service
await using IConnection conn = await realtimeService.ConnectAsync(curUserId);
IDocument<SFProject> projectDoc = await conn.FetchAsync<SFProject>(sfProjectId);
if (projectDoc.IsLoaded)
{
// Notify the UI that the draft is no longer in progress
await projectDoc.SubmitJson0OpAsync(op =>
op.Set(pd => pd.TranslateConfig.DraftConfig.DraftInProgress, false)
);
}

// Get the translation engine id
string translationEngineId = GetTranslationEngineId(projectSecret);

Expand Down Expand Up @@ -3334,6 +3358,7 @@ await projectDoc.SubmitJson0OpAsync(op =>
[.. buildConfig.TranslationScriptureRanges],
_listProjectScriptureRangeComparer
);
op.Set(pd => pd.TranslateConfig.DraftConfig.DraftInProgress, true);
op.Set(p => p.TranslateConfig.DraftConfig.FastTraining, buildConfig.FastTraining);
op.Set(p => p.TranslateConfig.DraftConfig.SendEmailOnBuildFinished, buildConfig.SendEmailOnBuildFinished);
op.Set(p => p.TranslateConfig.DraftConfig.UseEcho, buildConfig.UseEcho);
Expand Down
24 changes: 24 additions & 0 deletions src/SIL.XForge.Scripture/Services/MachineProjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ await projectSecrets.UpdateAsync(
cancellationToken: cancellationToken
);

// Load the project from the realtime service
await using IConnection conn = await realtimeService.ConnectAsync(curUserId);
IDocument<SFProject> projectDoc = await conn.FetchAsync<SFProject>(buildConfig.ProjectId);
if (projectDoc.IsLoaded)
{
// Notify the UI that the draft is no longer in progress
await projectDoc.SubmitJson0OpAsync(op =>
op.Set(pd => pd.TranslateConfig.DraftConfig.DraftInProgress, false)
);
}

// Send the cancellation email, if specified
if (buildConfig.SendEmailOnBuildFinished)
{
Expand Down Expand Up @@ -188,6 +199,19 @@ await projectSecrets.UpdateAsync(
cancellationToken: cancellationToken
);

// Load the project from the realtime service
await using IConnection conn = await realtimeService.ConnectAsync(curUserId);
IDocument<SFProject> projectDoc = await conn.FetchAsync<SFProject>(buildConfig.ProjectId);
if (projectDoc.IsLoaded)
{
// Notify the UI that the draft failed
await projectDoc.SubmitJson0OpAsync(op =>
{
op.Set(pd => pd.TranslateConfig.DraftConfig.DraftInProgress, false);
op.Set(pd => pd.TranslateConfig.DraftConfig.LastDraftSuccessful, false);
});
}

// Send the failure email, if specified
if (buildConfig.SendEmailOnBuildFinished)
{
Expand Down
13 changes: 13 additions & 0 deletions test/SIL.XForge.Scripture.Tests/Services/MachineApiServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ await env.Service.BuildCompletedAsync(
CancellationToken.None
);
env.MockLogger.AssertHasEvent(logEvent => logEvent.LogLevel == LogLevel.Information);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress, Is.False);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastDraftSuccessful, Is.True);
}

[Test]
Expand All @@ -488,6 +490,8 @@ await env.Service.BuildCompletedAsync(
CancellationToken.None
);
env.MockLogger.AssertHasEvent(logEvent => logEvent.LogLevel == LogLevel.Information);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress, Is.False);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastDraftSuccessful, Is.True);
}

[Test]
Expand All @@ -510,6 +514,8 @@ await env.Service.BuildCompletedAsync(
);
env.MockLogger.AssertHasEvent(logEvent => logEvent.Exception == ex);
env.ExceptionHandler.Received().ReportException(ex);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress, Is.False);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastDraftSuccessful, Is.True);
}

[Test]
Expand Down Expand Up @@ -539,6 +545,8 @@ await env
env.SiteOptions.Value.WebsiteUrl,
CancellationToken.None
);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress, Is.False);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastDraftSuccessful, Is.True);
}

[Test]
Expand Down Expand Up @@ -568,6 +576,8 @@ await env
Arg.Any<Uri>(),
CancellationToken.None
);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress, Is.False);
Assert.That(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastDraftSuccessful, Is.True);
}

[Test]
Expand Down Expand Up @@ -691,6 +701,7 @@ public async Task CancelPreTranslationBuildAsync_Success()
env.BackgroundJobClient.Received(1).ChangeState(HangfireJobId, Arg.Any<DeletedState>(), null); // Same as Delete()
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationJobId);
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationQueuedAt);
Assert.IsFalse(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress);
}

[Test]
Expand Down Expand Up @@ -5166,6 +5177,7 @@ await env.Service.StartPreTranslationBuildAsync(
Assert.AreEqual(HangfireJobId, env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationJobId);
Assert.IsNotNull(env.ProjectSecrets.Get(Project01).ServalData?.PreTranslationQueuedAt);
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData?.PreTranslationErrorMessage);
Assert.IsTrue(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress);
Assert.IsEmpty(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastSelectedTrainingScriptureRanges);
Assert.IsEmpty(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastSelectedTrainingDataFiles);
Assert.IsEmpty(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastSelectedTranslationScriptureRanges);
Expand Down Expand Up @@ -5265,6 +5277,7 @@ await env.Service.StartPreTranslationBuildAsync(
Data01,
env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastSelectedTrainingDataFiles.First()
);
Assert.IsTrue(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress);
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ await env.Service.BuildProjectForBackgroundJobAsync(
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationJobId);
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationQueuedAt);
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationErrorMessage);
Assert.IsFalse(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress);
}

[Test]
Expand Down Expand Up @@ -272,6 +273,8 @@ await env.Service.BuildProjectForBackgroundJobAsync(
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationJobId);
Assert.IsNull(env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationQueuedAt);
Assert.AreEqual(ex.Message, env.ProjectSecrets.Get(Project01).ServalData!.PreTranslationErrorMessage);
Assert.IsFalse(env.Projects.Get(Project01).TranslateConfig.DraftConfig.DraftInProgress);
Assert.IsFalse(env.Projects.Get(Project01).TranslateConfig.DraftConfig.LastDraftSuccessful);
}

[Test]
Expand Down
Loading