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
8 changes: 8 additions & 0 deletions dbschema/language.gel
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ module default {
constraint regexp(r'^[0-9]{5}$');
}

property usesAIAssistance := exists (
select .engagements
filter .usingAIAssistedTranslation not in {
Engagement::AIAssistedTranslation.None,
Engagement::AIAssistedTranslation.Unknown
}
);

property population := .populationOverride ?? .ethnologue.population;
populationOverride: population;

Expand Down
11 changes: 11 additions & 0 deletions dbschema/migrations/00020-m1cpuxn.edgeql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions src/components/language/dto/language.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,17 @@ export class Language extends Interfaces {
})
readonly presetInventory: SecuredBoolean;

@Calculated()
@Field({
description: stripIndent`
Whether any engagement for this language is using AI-assisted translation.

This is true if any engagement's "usingAIAssistedTranslation" property is not "None" or "Unknown".
Used to track and filter languages that have AI assistance in their translation process.
`,
})
readonly usesAIAssistance: SecuredBoolean;

// Not returned, only used to cache the sensitivity for determining permissions
readonly effectiveSensitivity: Sensitivity;
}
Expand Down
6 changes: 6 additions & 0 deletions src/components/language/dto/list-language.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export abstract class LanguageFilters {
})
readonly presetInventory?: boolean;

@OptionalField({
description:
'Only languages that have an AI-assisted translation engagement',
})
readonly usesAIAssistance?: boolean;

@OptionalField({
description:
'Only languages that are pinned/unpinned by the requesting user',
Expand Down
1 change: 1 addition & 0 deletions src/components/language/language.gel.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class LanguageGelRepository
sensitivity: lang.ownSensitivity,
effectiveSensitivity: lang.sensitivity,
presetInventory: e.bool(false), // Not implemented going forward
usesAIAssistance: lang.usesAIAssistance,
}),
omit: ['create'],
})
Expand Down
30 changes: 30 additions & 0 deletions src/components/language/language.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ export class LanguageRepository extends DtoRepository<
])
.apply(matchProps({ nodeName: 'eth', outputVar: 'ethProps' }))
.apply(isPresetInventory)
.apply(usingAIAssistance)
.optionalMatch([
node('node'),
relation('in', '', 'language', ACTIVE),
Expand All @@ -222,6 +223,7 @@ export class LanguageRepository extends DtoRepository<
ethnologue: 'ethProps',
pinned,
presetInventory: 'presetInventory',
usesAIAssistance: 'usesAIAssistance',
firstScriptureEngagement: 'firstScriptureEngagement { .id }',
scope: 'scopedRoles',
changeset: 'changeset.id',
Expand Down Expand Up @@ -339,6 +341,10 @@ export const languageFilters = filter.define(() => LanguageFilters, {
const condition = equals('true', true);
return { presetInventory: value ? condition : not(condition) };
},
usesAIAssistance: ({ value, query }) => {
query.apply(usingAIAssistance).with(['node', 'usesAIAssistance']);
return { usesAIAssistance: value };
},
});

const ethnologueFilters = filter.define(() => EthnologueLanguageFilters, {
Expand Down Expand Up @@ -374,6 +380,25 @@ const isPresetInventory = (query: Query) =>
),
);

const usingAIAssistance = (query: Query) =>
query.subQuery('node', (sub) =>
sub
.optionalMatch([
node('node'),
relation('in', '', 'language', ACTIVE),
node('eng', 'LanguageEngagement'),
])
.optionalMatch([
node('eng'),
relation('out', '', 'usingAIAssistedTranslation', ACTIVE),
node('prop', 'Property'),
])
.with([
`any(val in collect(prop.value) WHERE val IS NOT NULL AND val <> 'None' AND val <> 'Unknown') as usesAIAssistance`,
])
.return(['usesAIAssistance']),
);

export const languageSorters = defineSorters(Language, {
// eslint-disable-next-line @typescript-eslint/naming-convention
'ethnologue.*': (query, input) =>
Expand Down Expand Up @@ -405,6 +430,11 @@ export const languageSorters = defineSorters(Language, {
.return<{ sortValue: unknown }>(
coalesce('override.value', 'canonical.value').as('sortValue'),
),
usesAIAssistance: (query) =>
query
.apply(usingAIAssistance)
.with(['node', 'usesAIAssistance as sortValue'])
.return<{ sortValue: unknown }>('sortValue'),
});

const ethnologueSorters = defineSorters(EthnologueLanguage, {});
Expand Down