diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.spec.ts index aec508b6c9..7385755652 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.spec.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.spec.ts @@ -363,6 +363,7 @@ describe('DraftGenerationStepsComponent', () => { beforeEach(fakeAsync(() => { when(mockDraftSourceService.getDraftProjectSources()).thenReturn(of(config)); when(mockActivatedProjectService.projectDoc$).thenReturn(of({} as any)); + when(mockActivatedProjectService.projectDoc).thenReturn({} as any); when(mockFeatureFlagService.allowFastTraining).thenReturn(createTestFeatureFlag(false)); when(mockNllbLanguageService.isNllbLanguageAsync(anything())).thenResolve(true); when(mockNllbLanguageService.isNllbLanguageAsync('xyz')).thenResolve(false); @@ -605,6 +606,7 @@ describe('DraftGenerationStepsComponent', () => { beforeEach(fakeAsync(() => { when(mockDraftSourceService.getDraftProjectSources()).thenReturn(of(config)); when(mockActivatedProjectService.projectDoc$).thenReturn(of({} as any)); + when(mockActivatedProjectService.projectDoc).thenReturn({} as any); when(mockFeatureFlagService.allowFastTraining).thenReturn(createTestFeatureFlag(true)); when(mockTrainingDataService.queryTrainingDataAsync(anything(), anything())).thenResolve( instance(mockTrainingDataQuery) @@ -739,6 +741,72 @@ describe('DraftGenerationStepsComponent', () => { }); }); + describe('can add additional training data', () => { + const availableBooks = [{ bookNum: 2 }, { bookNum: 3 }]; + const config = { + trainingSources: [ + { + projectRef: 'source1', + paratextId: 'PT_SP1', + name: 'Source Project 1', + shortName: 'sP1', + writingSystem: { tag: 'eng' }, + texts: availableBooks + }, + undefined + ] as [DraftSource, DraftSource?], + trainingTargets: [ + { + projectRef: mockActivatedProjectService.projectId, + shortName: 'tT', + writingSystem: { tag: 'nllb' }, + texts: availableBooks + } + ] as [DraftSource], + draftingSources: [ + { + projectRef: 'draftingSource', + shortName: 'dS', + writingSystem: { tag: 'eng' }, + texts: availableBooks + } + ] as [DraftSource] + }; + + const mockTargetProjectDoc: SFProjectProfileDoc = { + id: mockActivatedProjectService.projectId, + data: createTestProjectProfile({ + texts: availableBooks, + translateConfig: { + source: { projectRef: 'sourceProject', shortName: 'sP1', writingSystem: { tag: 'eng' } }, + draftConfig: { additionalTrainingData: true } + }, + writingSystem: { tag: 'nllb' } + }) + } as SFProjectProfileDoc; + const targetProjectDoc$ = new BehaviorSubject(mockTargetProjectDoc); + + beforeEach(fakeAsync(() => { + when(mockDraftSourceService.getDraftProjectSources()).thenReturn(of(config)); + when(mockActivatedProjectService.projectDoc$).thenReturn(targetProjectDoc$); + when(mockActivatedProjectService.projectDoc).thenReturn(mockTargetProjectDoc); + when(mockFeatureFlagService.allowFastTraining).thenReturn(createTestFeatureFlag(false)); + when(mockTrainingDataService.queryTrainingDataAsync(anything(), anything())).thenResolve( + instance(mockTrainingDataQuery) + ); + when(mockTrainingDataQuery.docs).thenReturn([]); + + fixture = TestBed.createComponent(DraftGenerationStepsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + tick(); + })); + + it('should allow additional training data', () => { + expect(component.trainingDataFilesAvailable).toBe(true); + }); + }); + describe('confirm step', () => { const availableBooks = [ { bookNum: 1 }, diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.ts index a42c516b26..76359486e1 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation-steps/draft-generation-steps.component.ts @@ -6,8 +6,8 @@ import { Canon } from '@sillsdev/scripture'; import { TranslocoMarkupModule } from 'ngx-transloco-markup'; import { TrainingData } from 'realtime-server/lib/esm/scriptureforge/models/training-data'; import { ProjectScriptureRange, TranslateSource } from 'realtime-server/lib/esm/scriptureforge/models/translate-config'; -import { combineLatest, merge, Subscription } from 'rxjs'; -import { filter, tap } from 'rxjs/operators'; +import { combineLatest, merge } from 'rxjs'; +import { filter, switchMap } from 'rxjs/operators'; import { ActivatedProjectService } from 'xforge-common/activated-project.service'; import { FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service'; import { I18nService } from 'xforge-common/i18n.service'; @@ -105,7 +105,7 @@ export class DraftGenerationStepsComponent implements OnInit { protected translatedBooksWithNoSource: number[] = []; private trainingDataQuery?: RealtimeQuery; - private trainingDataSub?: Subscription; + private isTrainingDataInitialized: boolean = false; constructor( private readonly destroyRef: DestroyRef, @@ -220,39 +220,40 @@ export class DraftGenerationStepsComponent implements OnInit { ); // Get the training data files for the project + this.activatedProject.projectDoc$ + .pipe( + takeUntilDestroyed(this.destroyRef), + filterNullish(), + switchMap(async projectDoc => { + this.isTrainingDataInitialized = false; + // Query for all training data files in the project + this.trainingDataQuery?.dispose(); + this.trainingDataQuery = await this.trainingDataService.queryTrainingDataAsync( + projectDoc.id, + this.destroyRef + ); - this.activatedProject.projectDoc$.pipe( - takeUntilDestroyed(this.destroyRef), - filterNullish(), - tap(async projectDoc => { - // Query for all training data files in the project - this.trainingDataQuery?.dispose(); - this.trainingDataQuery = await this.trainingDataService.queryTrainingDataAsync(projectDoc.id, this.destroyRef); - let projectChanged: boolean = true; - - // Subscribe to this query, and show these - this.trainingDataSub?.unsubscribe(); - this.trainingDataSub = merge( - this.trainingDataQuery.localChanges$, - this.trainingDataQuery.ready$, - this.trainingDataQuery.remoteChanges$, - this.trainingDataQuery.remoteDocChanges$ - ) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - this.availableTrainingFiles = []; - if (projectDoc.data?.translateConfig.draftConfig.additionalTrainingData) { - this.availableTrainingFiles = - this.trainingDataQuery?.docs.filter(d => d.data != null).map(d => d.data!) ?? []; - } - if (projectChanged) { - // Set the selection based on previous builds - projectChanged = false; - this.setInitialTrainingDataFiles(this.availableTrainingFiles.map(d => d.dataId)); - } - }); - }) - ); + // switch to the observable from trainingDataQuery + return merge( + this.trainingDataQuery.localChanges$, + this.trainingDataQuery.ready$, + this.trainingDataQuery.remoteChanges$, + this.trainingDataQuery.remoteDocChanges$ + ); + }) + ) + .subscribe(() => { + this.availableTrainingFiles = []; + if (this.activatedProject.projectDoc?.data?.translateConfig.draftConfig.additionalTrainingData) { + this.availableTrainingFiles = + this.trainingDataQuery?.docs.filter(d => d.data != null).map(d => d.data!) ?? []; + } + if (!this.isTrainingDataInitialized) { + // Set the selection based on previous builds + this.isTrainingDataInitialized = true; + this.setInitialTrainingDataFiles(this.availableTrainingFiles.map(d => d.dataId)); + } + }); } get trainingSourceBooksSelected(): boolean {