Skip to content

Commit acd14da

Browse files
committed
SF-3757 Add simplified draft config page as experimental feature
1 parent 2eb056a commit acd14da

25 files changed

Lines changed: 2091 additions & 34 deletions

scripts/db_tools/parse-version.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,17 @@ class ParseVersion {
3333
'Stillness (non-distracting progress indicators)',
3434
'Use In-Process Machine for Suggestions',
3535
'Use Serval for Suggestions',
36-
'Use Echo for Pre-Translation Drafting',
36+
'Allow Echo for Pre-Translation Drafting',
3737
'Allow Fast Pre-Translation Training',
3838
'Upload Paratext Zip Files for Pre-Translation Drafting',
3939
'Allow mixing in an additional training source',
4040
'Updated Learning Rate For Serval',
41-
'Dark Mode',
41+
'Dark mode',
4242
'Enable Lynx insights',
4343
'Preview new draft history interface',
4444
'USFM Format',
45-
'Show in-app draft signup form instead of external link'
45+
'Show in-app draft signup form instead of external link',
46+
'Show new configure sources page'
4647
];
4748

4849
constructor() {

src/SIL.XForge.Scripture/ClientApp/src/app/app.component.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@
149149
<button mat-menu-item appRouterLink="/projects" id="project-home-link">
150150
<mat-icon>home</mat-icon> {{ "my_projects.my_projects" | transloco }}
151151
</button>
152+
@if (experimentalFeatures.showExperimentalFeaturesInMenu) {
153+
<button mat-menu-item (click)="openExperimentalFeaturesDialog()">
154+
<mat-icon>science</mat-icon> {{ t("experimental_features") }}
155+
</button>
156+
}
152157
@if (featureFlags.showDeveloperTools.enabled) {
153158
<button mat-menu-item [matMenuTriggerFor]="appearanceMenu">
154159
<mat-icon>palette</mat-icon>

src/SIL.XForge.Scripture/ClientApp/src/app/app.component.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { DataLoadingComponent } from 'xforge-common/data-loading-component';
2828
import { DiagnosticOverlayService } from 'xforge-common/diagnostic-overlay.service';
2929
import { DialogService } from 'xforge-common/dialog.service';
3030
import { ErrorReportingService } from 'xforge-common/error-reporting.service';
31+
import { ExperimentalFeaturesDialogComponent } from 'xforge-common/experimental-features/experimental-features-dialog.component';
32+
import { ExperimentalFeaturesService } from 'xforge-common/experimental-features/experimental-features.service';
3133
import { ExternalUrlService } from 'xforge-common/external-url.service';
3234
import { FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service';
3335
import { FeatureFlagsDialogComponent } from 'xforge-common/feature-flags/feature-flags-dialog.component';
@@ -138,6 +140,7 @@ export class AppComponent extends DataLoadingComponent implements OnInit, OnDest
138140
private readonly themeService: ThemeService,
139141
private readonly fontService: FontService,
140142
onlineStatusService: OnlineStatusService,
143+
readonly experimentalFeatures: ExperimentalFeaturesService,
141144
private destroyRef: DestroyRef
142145
) {
143146
super(noticeService);
@@ -478,6 +481,10 @@ export class AppComponent extends DataLoadingComponent implements OnInit, OnDest
478481
this.dialogService.openMatDialog(FeatureFlagsDialogComponent);
479482
}
480483

484+
openExperimentalFeaturesDialog(): void {
485+
this.dialogService.openMatDialog(ExperimentalFeaturesDialogComponent);
486+
}
487+
481488
openDiagnosticOverlay(): void {
482489
this.diagnosticOverlayService.open();
483490
}

src/SIL.XForge.Scripture/ClientApp/src/app/app.routes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { DraftGenerationComponent } from './translate/draft-generation/draft-gen
3131
import { DraftOnboardingFormComponent } from './translate/draft-generation/draft-signup-form/draft-onboarding-form.component';
3232
import { DraftSourcesComponent } from './translate/draft-generation/draft-sources/draft-sources.component';
3333
import { DraftUsfmFormatComponent } from './translate/draft-generation/draft-usfm-format/draft-usfm-format.component';
34+
import { NewConfigureSourcesComponent } from './translate/draft-generation/new-configure-sources/new-configure-sources.component';
3435
import { EditorComponent } from './translate/editor/editor.component';
3536
import { TranslateOverviewComponent } from './translate/translate-overview/translate-overview.component';
3637
import { UsersComponent } from './users/users.component';
@@ -72,6 +73,12 @@ export const APP_ROUTES: Routes = [
7273
canActivate: [NmtDraftAuthGuard],
7374
canDeactivate: [DraftNavigationAuthGuard]
7475
},
76+
{
77+
path: 'projects/:projectId/draft-generation/new-configure-sources',
78+
component: NewConfigureSourcesComponent,
79+
canActivate: [NmtDraftAuthGuard],
80+
canDeactivate: [DraftNavigationAuthGuard]
81+
},
7582
{
7683
path: 'projects/:projectId/draft-generation/signup',
7784
component: DraftOnboardingFormComponent,

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,18 @@ <h1>
234234
{{ t("generate_draft_button") }}
235235
</button>
236236
@if (hasConfigureSourcePermission) {
237-
<button mat-button data-test-id="configure-button" [routerLink]="['sources']">
237+
<button mat-button data-test-id="configure-button" [routerLink]="configureSourcesPath">
238238
<mat-icon>settings</mat-icon>
239239
{{ t("configure_sources") }}
240240
</button>
241241
}
242242
} @else if (hasConfigureSourcePermission) {
243-
<button mat-flat-button data-test-id="configure-button" color="primary" [routerLink]="['sources']">
243+
<button
244+
mat-flat-button
245+
data-test-id="configure-button"
246+
color="primary"
247+
[routerLink]="configureSourcesPath"
248+
>
244249
<mat-icon>settings</mat-icon>
245250
{{ t("configure_sources") }}
246251
</button>
@@ -402,7 +407,7 @@ <h3>{{ t("draft_finishing_header") }}</h3>
402407
<mat-icon>add</mat-icon> {{ t("generate_new_draft") }}
403408
</button>
404409
@if (hasConfigureSourcePermission) {
405-
<button mat-button data-test-id="configure-button" [routerLink]="['sources']">
410+
<button mat-button data-test-id="configure-button" [routerLink]="configureSourcesPath">
406411
<mat-icon>settings</mat-icon> {{ t("configure_sources") }}
407412
</button>
408413
}
@@ -414,7 +419,7 @@ <h3>{{ t("draft_finishing_header") }}</h3>
414419
This first check regards what leads up to the first configure button. -->
415420
@if (!isGenerationSupported || isDraftInProgress(draftJob) || hasAnyCompletedBuild) {
416421
@if (draftEnabled && isOnline) {
417-
<button mat-button data-test-id="configure-button" color="primary" [routerLink]="['sources']">
422+
<button mat-button data-test-id="configure-button" color="primary" [routerLink]="configureSourcesPath">
418423
<mat-icon>settings</mat-icon> {{ t("configure_sources") }}
419424
</button>
420425
}

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ describe('DraftGenerationComponent', () => {
158158
mockFeatureFlagService = jasmine.createSpyObj<FeatureFlagService>({
159159
newDraftHistory: createTestFeatureFlag(false),
160160
usfmFormat: createTestFeatureFlag(false),
161-
inAppDraftSignupForm: createTestFeatureFlag(true)
161+
inAppDraftSignupForm: createTestFeatureFlag(true),
162+
newConfigureSourcesPage: createTestFeatureFlag(false)
162163
});
163164
}
164165

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ export class DraftGenerationComponent extends DataLoadingComponent implements On
342342
);
343343
}
344344

345+
get configureSourcesPath(): string[] {
346+
const projectId = this.activatedProject.projectId;
347+
if (projectId == null) return [];
348+
const minorPath = this.featureFlags.newConfigureSourcesPage.enabled ? 'new-configure-sources' : 'sources';
349+
return ['/projects', projectId, 'draft-generation', minorPath];
350+
}
351+
345352
async generateDraftClicked({ withConfirm = false } = {}): Promise<void> {
346353
if (this.formattingOptionsRequired) {
347354
const dialogRef = this.dialogService.openGenericDialog({

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
@@ -97,7 +97,7 @@ <h1>{{ t("configure_draft_sources") }}</h1>
9797
[placeholder]="projectPlaceholder(source)"
9898
></app-project-select>
9999
}
100-
<p class="no-bottom-margin">
100+
<p>
101101
{{ t("training_files_description", { sourceLanguageDisplayName, targetLanguageDisplayName }) }}
102102
</p>
103103
<div class="training-files">

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,3 @@ strong {
239239
.training-files {
240240
margin-bottom: 30px;
241241
}
242-
243-
.no-bottom-margin {
244-
margin-bottom: 0;
245-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
<ng-container *transloco="let t; read: 'draft_sources'">
2+
<h1>{{ t("configure_draft_sources") }}</h1>
3+
4+
<app-notice class="experimental-notice" type="primary" mode="fill-dark" icon="science">
5+
This is an experimental simplified page for configuring sources. Please
6+
<a href="mailto:{{ issueEmail }}" target="_blank">leave us feedback</a> on how it works for you.
7+
</app-notice>
8+
9+
<mat-expansion-panel class="introduction">
10+
<mat-expansion-panel-header>How to configure draft sources</mat-expansion-panel-header>
11+
<p>
12+
Generating a draft requires two steps. First we need to teach a model how to translate from one language into
13+
another. The 'from language' is also known as the 'Source', and the 'to language' is also known as the 'Target'.
14+
</p>
15+
<p>
16+
Source languages already known to the model, or closely related to the Target language usually work best.
17+
<a href="#" (click)="showModelLanguages($event)">Here is a list of the languages that the model knows already.</a>
18+
</p>
19+
<p>
20+
The current project <strong>{{ currentProjectShortName }}</strong> will be used to train a model that can
21+
translate into <strong>{{ targetLanguageDisplayName }}</strong
22+
>.
23+
</p>
24+
<h3>Choosing Source Projects for training the model</h3>
25+
<p>
26+
Choose one or two texts in the 'Source Language'. Sometimes choosing a Back Translation improves the model. It is
27+
worth testing with and without the Back Translation to see which setting gives you the best results.
28+
</p>
29+
<p>
30+
There is an option to include other translation examples in training by uploading a spreadsheet with two columns.
31+
The first column contains the Source text and the second column contains its translation in the Target language.
32+
</p>
33+
34+
<h3>Choosing a Drafting Source</h3>
35+
<p>
36+
The model will create drafts by translating books from the 'Drafting Source Project'. It is rare that the model
37+
will give useful results if the Drafting Source Project isn't included in the Source projects for training the
38+
model.
39+
</p>
40+
41+
<p>
42+
You will be able to look at the drafts before you need to decide how to use them. Scripture Forge will not
43+
automatically add the generated drafts to any project. They will only be available in Scripture Forge until you
44+
add them to a project.
45+
</p>
46+
47+
<p><a [href]="urls.configuringSources" target="_blank">More help on Configuring Sources</a></p>
48+
</mat-expansion-panel>
49+
50+
<div class="overview">
51+
<mat-card>
52+
<mat-card-header>
53+
<mat-card-title>{{ "new_configure_sources.train_language_model" | transloco }}</mat-card-title>
54+
</mat-card-header>
55+
<mat-card-content class="training-data">
56+
<div class="sources">
57+
<h3>{{ t("overview_reference") }} {{ parentheses(referenceLanguageDisplayName) }}</h3>
58+
59+
<div>{{ t("select_primary_reference") }}</div>
60+
@for (source of trainingSources; track $index) {
61+
<app-project-select
62+
[isDisabled]="loading || !appOnline"
63+
[projects]="projects"
64+
[resources]="resources"
65+
[nonSelectableProjects]="nonSelectableProjects"
66+
[value]="source?.paratextId"
67+
[hiddenParatextIds]="getHiddenParatextIds(trainingSources, source?.paratextId)"
68+
(valueChange)="sourceSelected(trainingSources, $index, $event)"
69+
[placeholder]="projectPlaceholder(source)"
70+
></app-project-select>
71+
}
72+
73+
@if (allowAddingATrainingSource) {
74+
<div>{{ t("some_projects_use_back_translation") }}</div>
75+
<button
76+
mat-button
77+
(click)="trainingSources.push(undefined); $event.preventDefault()"
78+
class="add-another-project"
79+
>
80+
<mat-icon>add</mat-icon> {{ t("add_another_reference_project") }}
81+
</button>
82+
}
83+
</div>
84+
<span class="arrow mirror-rtl"><mat-icon>arrow_right_alt</mat-icon></span>
85+
<div class="targets">
86+
<h3>{{ t("overview_translated_project") }} {{ parentheses(targetLanguageDisplayName) }}</h3>
87+
<div>
88+
@for (portion of i18n.interpolateVariables("draft_sources.project_always_used"); track $index) {
89+
@if (portion.id === "currentProjectShortName") {
90+
<strong>{{ currentProjectShortName }}</strong>
91+
}
92+
<!-- prettier-ignore -->
93+
@else {{{ portion.text }}}
94+
}
95+
<!-- Select here any other projects to be used on the target side. All of these should be in the language of the target (<strong>{{ targetLanguageDisplayName }}</strong>). -->
96+
</div>
97+
@for (source of trainingTargets; track $index) {
98+
<app-project-select
99+
[isDisabled]="true"
100+
[projects]="projects"
101+
[resources]="resources"
102+
[nonSelectableProjects]="nonSelectableProjects"
103+
[value]="source?.paratextId"
104+
[hiddenParatextIds]="getHiddenParatextIds(trainingTargets, source?.paratextId)"
105+
[placeholder]="projectPlaceholder(source)"
106+
></app-project-select>
107+
}
108+
<div>
109+
{{ t("training_files_description", { sourceLanguageDisplayName, targetLanguageDisplayName }) }}
110+
</div>
111+
<div class="training-files">
112+
<app-training-data-multi-select
113+
[availableTrainingData]="availableTrainingFiles"
114+
(trainingDataSelect)="onTrainingDataSelect($event)"
115+
></app-training-data-multi-select>
116+
</div>
117+
</div>
118+
</mat-card-content>
119+
</mat-card>
120+
121+
<mat-card>
122+
<mat-card-header>
123+
<mat-card-title>
124+
{{ "new_configure_sources.generate_a_draft_from_the_language_model" | transloco }}
125+
</mat-card-title>
126+
</mat-card-header>
127+
<mat-card-content>
128+
<div class="translation-data">
129+
<h3>{{ t("overview_source") }} {{ parentheses(sourceLanguageDisplayName) }}</h3>
130+
<div>{{ t("select_project_to_translate") }}</div>
131+
@for (source of draftingSources; track $index) {
132+
<app-project-select
133+
[isDisabled]="loading || !appOnline"
134+
[projects]="projects"
135+
[resources]="resources"
136+
[nonSelectableProjects]="nonSelectableProjects"
137+
[value]="source?.paratextId"
138+
[hiddenParatextIds]="getHiddenParatextIds(draftingSources, source?.paratextId)"
139+
(valueChange)="sourceSelected(draftingSources, $index, $event)"
140+
[placeholder]="projectPlaceholder(source)"
141+
></app-project-select>
142+
}
143+
</div>
144+
</mat-card-content>
145+
</mat-card>
146+
</div>
147+
148+
<app-language-codes-confirmation
149+
class="confirm-language-codes"
150+
[sources]="draftSourcesAsArray"
151+
[clearCheckbox]="clearLanguageCodeConfirmationCheckbox"
152+
(messageIfUserTriesToContinue)="languageCodeConfirmationMessageIfUserTriesToContinue = $event"
153+
></app-language-codes-confirmation>
154+
155+
<div class="component-footer">
156+
@if (!appOnline) {
157+
<mat-error id="offline-message">
158+
{{ t("offline_message") }}
159+
</mat-error>
160+
}
161+
<div class="page-actions">
162+
<button mat-button (click)="cancel()"><mat-icon>close</mat-icon>{{ t("cancel") }}</button>
163+
<button id="save_button" mat-flat-button color="primary" (click)="save()" [disabled]="!appOnline">
164+
<mat-icon>check</mat-icon>{{ t("save_and_sync") }}
165+
</button>
166+
</div>
167+
</div>
168+
169+
@if (getControlState("projectSettings") != null) {
170+
<mat-card class="saving">
171+
<mat-card-header>
172+
<mat-card-title>{{ t("saving_draft_sources") }}</mat-card-title>
173+
</mat-card-header>
174+
<mat-card-content>
175+
<div class="saving-indicator">
176+
@if (getControlState("projectSettings") === ElementState.Submitting) {
177+
<mat-spinner [diameter]="24" color="primary"></mat-spinner> {{ t("saving") }}
178+
} @else if (getControlState("projectSettings") === ElementState.Submitted) {
179+
<mat-icon class="success">checkmark</mat-icon> {{ t("all_changes_saved") }}
180+
} @else {
181+
<mat-icon class="failure">error</mat-icon> {{ t("failed_to_save_changes") }}
182+
}
183+
</div>
184+
@for (entry of syncStatus | keyvalue; track entry.key) {
185+
<div>
186+
@if (entry.value.knownToBeOnSF) {
187+
@if (entry.value.isSyncing) {
188+
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_syncing") }}
189+
} @else {
190+
@if (entry.value.lastSyncSuccessful) {
191+
<mat-icon class="success">check</mat-icon> {{ entry.value.shortName }} -
192+
{{ t("state_sync_successful") }}
193+
} @else {
194+
<mat-icon class="failure">error</mat-icon> {{ entry.value.shortName }} -
195+
{{ t("state_sync_failed") }}
196+
}
197+
}
198+
} @else {
199+
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_connecting") }}
200+
}
201+
</div>
202+
}
203+
</mat-card-content>
204+
@if (allProjectsSavedAndSynced || getControlState("projectSettings") === ElementState.Error) {
205+
<mat-card-actions align="end">
206+
<button mat-button (click)="navigateToDrafting()"><mat-icon>close</mat-icon> {{ t("close") }}</button>
207+
</mat-card-actions>
208+
}
209+
</mat-card>
210+
}
211+
</ng-container>

0 commit comments

Comments
 (0)