diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.html
index 33b92c532c9..6ea82f88bda 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.html
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.html
@@ -21,7 +21,7 @@
- @if (this.featureFlags.usfmFormat.enabled && !formattingOptionsSelected && isLatestBuild && draftIsAvailable) {
+ @if (formattingOptionsSupported && !formattingOptionsSelected && isLatestBuild && draftIsAvailable) {
{{ t("select_formatting_options") }}
@@ -49,7 +49,7 @@
- @if (featureFlags.usfmFormat.enabled && isLatestBuild) {
+ @if (formattingOptionsSupported && isLatestBuild) {
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.spec.ts
index 5f9e5b4e3f3..dd035d16c99 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.spec.ts
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.spec.ts
@@ -22,6 +22,7 @@ import { SFProjectService } from '../../../../core/sf-project.service';
import { BuildDto } from '../../../../machine-api/build-dto';
import { BuildStates } from '../../../../machine-api/build-states';
import { DraftGenerationService } from '../../draft-generation.service';
+import { FORMATTING_OPTIONS_SUPPORTED_DATE } from '../../draft-utils';
import { TrainingDataService } from '../../training-data/training-data.service';
import { DraftHistoryEntryComponent } from './draft-history-entry.component';
@@ -33,6 +34,10 @@ const mockedTrainingDataService = mock(TrainingDataService);
const mockedActivatedProjectService = mock(ActivatedProjectService);
const mockedFeatureFlagsService = mock(FeatureFlagService);
+const oneDay = 1000 * 60 * 60 * 24;
+const dateBeforeFormattingSupported = new Date(FORMATTING_OPTIONS_SUPPORTED_DATE.getTime() - oneDay).toISOString();
+const dateAfterFormattingSupported = new Date(FORMATTING_OPTIONS_SUPPORTED_DATE.getTime() + oneDay).toISOString();
+
describe('DraftHistoryEntryComponent', () => {
let component: DraftHistoryEntryComponent;
let fixture: ComponentFixture
;
@@ -99,7 +104,7 @@ describe('DraftHistoryEntryComponent', () => {
it('should handle builds with additional info', fakeAsync(() => {
when(mockedI18nService.enumerateList(anything())).thenReturn('src');
const user = 'user-display-name';
- const date = 'formatted-date';
+ const date = dateAfterFormattingSupported;
const trainingBooks = ['EXO'];
const translateBooks = ['GEN'];
const trainingDataFiles: Map = new Map([['file01', 'training-data.txt']]);
@@ -144,7 +149,7 @@ describe('DraftHistoryEntryComponent', () => {
it('should state that the model did not have training configuration', fakeAsync(() => {
when(mockedI18nService.enumerateList(anything())).thenReturn('src');
const user = 'user-display-name';
- const date = 'formatted-date';
+ const date = dateAfterFormattingSupported;
const trainingBooks = [];
const translateBooks = ['GEN'];
const trainingDataFiles = [];
@@ -161,7 +166,7 @@ describe('DraftHistoryEntryComponent', () => {
it('should show the USFM format option when the project is the latest draft', fakeAsync(() => {
const user = 'user-display-name';
- const date = 'formatted-date';
+ const date = dateAfterFormattingSupported;
const trainingBooks = ['EXO'];
const translateBooks = ['GEN'];
const trainingDataFiles = ['file01'];
@@ -244,8 +249,9 @@ describe('DraftHistoryEntryComponent', () => {
when(mockedActivatedProjectService.changes$).thenReturn(of(targetProjectDoc));
const entry = {
additionalInfo: {
- dateGenerated: new Date().toISOString(),
- dateRequested: new Date().toISOString(),
+ dateGenerated: dateAfterFormattingSupported,
+ dateRequested: dateAfterFormattingSupported,
+ dateFinished: dateAfterFormattingSupported,
requestedByUserId: 'sf-user-id',
translationScriptureRanges: [{ projectId: 'project01', scriptureRange: 'GEN' }]
}
@@ -337,14 +343,16 @@ describe('DraftHistoryEntryComponent', () => {
const projectDoc = getProjectProfileDoc();
when(mockedActivatedProjectService.projectDoc).thenReturn(projectDoc);
when(mockedActivatedProjectService.changes$).thenReturn(of(projectDoc));
+ when(mockedI18nService.formatDate(anything())).thenReturn('formatted-date');
});
it('should show set draft format UI', fakeAsync(() => {
+ const date = dateAfterFormattingSupported;
component.entry = {
id: 'build01',
state: BuildStates.Completed,
message: 'Completed',
- additionalInfo: { dateGenerated: '2025-09-01' }
+ additionalInfo: { dateGenerated: date, dateFinished: date }
} as BuildDto;
component.isLatestBuild = true;
component.draftIsAvailable = true;
@@ -360,7 +368,8 @@ describe('DraftHistoryEntryComponent', () => {
state: BuildStates.Completed,
message: 'Completed',
additionalInfo: {
- dateGenerated: '2025-09-01',
+ dateGenerated: dateAfterFormattingSupported,
+ dateFinished: dateAfterFormattingSupported,
translationScriptureRanges: [{ projectId: 'source01', scriptureRange: 'EXO' }]
}
} as BuildDto;
@@ -377,7 +386,8 @@ describe('DraftHistoryEntryComponent', () => {
state: BuildStates.Completed,
message: 'Completed',
additionalInfo: {
- dateGenerated: '2025-09-01',
+ dateGenerated: dateAfterFormattingSupported,
+ dateFinished: dateAfterFormattingSupported,
translationScriptureRanges: [{ projectId: 'source01', scriptureRange: 'EXO' }]
}
} as BuildDto;
@@ -389,13 +399,38 @@ describe('DraftHistoryEntryComponent', () => {
}));
it('should hide draft format UI if the draft is not completed', fakeAsync(() => {
- component.entry = { id: 'build01', state: BuildStates.Canceled, message: 'Cancelled' } as BuildDto;
+ component.entry = {
+ id: 'build01',
+ state: BuildStates.Canceled,
+ message: 'Cancelled',
+ additionalInfo: { dateGenerated: dateAfterFormattingSupported, dateFinished: dateAfterFormattingSupported }
+ } as BuildDto;
component.isLatestBuild = true;
component.draftIsAvailable = false;
tick();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.require-formatting-options')).toBeNull();
}));
+
+ it('should not show the USFM format option for drafts created before the supported date', fakeAsync(() => {
+ const user = 'user-display-name';
+ const date = dateBeforeFormattingSupported;
+ const trainingBooks = ['EXO'];
+ const translateBooks = ['GEN'];
+ const trainingDataFiles = ['file01'];
+ const entry = getStandardBuildDto({ user, date, trainingBooks, translateBooks, trainingDataFiles });
+
+ // SUT
+ component.entry = entry;
+ component.isLatestBuild = true;
+ tick();
+ fixture.detectChanges();
+
+ expect(component.scriptureRange).toEqual('GEN');
+ expect(component.draftIsAvailable).toBe(true);
+ expect(fixture.nativeElement.querySelector('.format-usfm')).toBeNull();
+ expect(component.formattingOptionsSupported).toBe(false);
+ }));
});
describe('formatDate', () => {
@@ -457,8 +492,9 @@ describe('DraftHistoryEntryComponent', () => {
id: 'project01'
},
additionalInfo: {
- dateGenerated: new Date().toISOString(),
- dateRequested: new Date().toISOString(),
+ dateGenerated: new Date(date).toISOString(),
+ dateFinished: new Date(date).toISOString(),
+ dateRequested: new Date(date).toISOString(),
requestedByUserId: 'sf-user-id',
trainingScriptureRanges:
trainingBooks.length > 0 ? [{ projectId: 'project02', scriptureRange: trainingBooks.join(';') }] : [],
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.ts
index 1d2afe47d62..33d83ce8f8b 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.ts
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-entry/draft-history-entry.component.ts
@@ -19,6 +19,7 @@ import { BuildStates } from '../../../../machine-api/build-states';
import { RIGHT_TO_LEFT_MARK } from '../../../../shared/utils';
import { DraftDownloadButtonComponent } from '../../draft-download-button/draft-download-button.component';
import { DraftPreviewBooksComponent } from '../../draft-preview-books/draft-preview-books.component';
+import { FORMATTING_OPTIONS_SUPPORTED_DATE } from '../../draft-utils';
import { TrainingDataService } from '../../training-data/training-data.service';
const STATUS_INFO: Record = {
@@ -271,6 +272,12 @@ export class DraftHistoryEntryComponent {
return this.activatedProjectService.projectDoc?.data?.translateConfig.draftConfig.usfmConfig != null;
}
+ get formattingOptionsSupported(): boolean {
+ return this.featureFlags.usfmFormat.enabled && this.entry?.additionalInfo?.dateFinished != null
+ ? new Date(this.entry.additionalInfo.dateFinished) > FORMATTING_OPTIONS_SUPPORTED_DATE
+ : false;
+ }
+
@Input() isLatestBuild: boolean = false;
trainingConfigurationOpen = false;
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-utils.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-utils.ts
index fe87365ee43..7881d5df669 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-utils.ts
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-utils.ts
@@ -3,6 +3,9 @@ import { TranslateSource } from 'realtime-server/lib/esm/scriptureforge/models/t
import language_code_mapping from '../../../../../language_code_mapping.json';
import { SelectableProjectWithLanguageCode } from '../../core/paratext.service';
+// Corresponds to Serval 1.11.0 release
+export const FORMATTING_OPTIONS_SUPPORTED_DATE: Date = new Date('2025-09-25T00:00:00Z');
+
/** Represents draft sources as a set of two {@link TranslateSource} arrays, and one {@link SFProjectProfile} array. */
export interface DraftSourcesAsTranslateSourceArrays {
trainingSources: TranslateSource[];
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/editor-draft/editor-draft.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/editor-draft/editor-draft.component.html
index a4f45494219..9a904618ce5 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/editor-draft/editor-draft.component.html
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/editor-draft/editor-draft.component.html
@@ -50,12 +50,12 @@
- } @else {
+ } @else if (formattingOptionsSupported) {
-