Skip to content

Commit 9dde8f7

Browse files
committed
Add a CLI and service to bulk review translations in a repository
- Refactor the current code that reviews a single string to read from the database if a review for a translation already exists. - Query translations that have not yet been reviewed, perform the bulk review, and save the result in AiReviewProto as a JSON blob that will be processed using MySQL JSON queries instead of separate columns. An option to remove an existing review can be added later, but for now this can be done directly in the database.
1 parent c7bded9 commit 9dde8f7

File tree

13 files changed

+931
-157
lines changed

13 files changed

+931
-157
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.box.l10n.mojito.cli.command;
2+
3+
import com.beust.jcommander.Parameter;
4+
import com.beust.jcommander.Parameters;
5+
import com.box.l10n.mojito.cli.command.param.Param;
6+
import com.box.l10n.mojito.cli.console.ConsoleWriter;
7+
import com.box.l10n.mojito.rest.client.RepositoryAiReviewClient;
8+
import com.box.l10n.mojito.rest.entity.PollableTask;
9+
import java.util.List;
10+
import java.util.stream.Collectors;
11+
import org.fusesource.jansi.Ansi.Color;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
import org.springframework.beans.factory.annotation.Autowired;
15+
import org.springframework.context.annotation.Scope;
16+
import org.springframework.stereotype.Component;
17+
18+
/**
19+
* Command to machine review strings in a repository.
20+
*
21+
* @author jaurambault
22+
*/
23+
@Component
24+
@Scope("prototype")
25+
@Parameters(
26+
commandNames = {"repository-ai-review"},
27+
commandDescription = "Ai review translated strings in a repository")
28+
public class RepositoryAiReviewCommand extends Command {
29+
30+
/** logger */
31+
static Logger logger = LoggerFactory.getLogger(RepositoryAiReviewCommand.class);
32+
33+
@Autowired ConsoleWriter consoleWriter;
34+
35+
@Parameter(
36+
names = {Param.REPOSITORY_LONG, Param.REPOSITORY_SHORT},
37+
arity = 1,
38+
required = true,
39+
description = Param.REPOSITORY_DESCRIPTION)
40+
String repositoryParam;
41+
42+
@Parameter(
43+
names = {Param.REPOSITORY_LOCALES_LONG, Param.REPOSITORY_LOCALES_SHORT},
44+
variableArity = true,
45+
description =
46+
"List of locales (bcp47 tags) to review, if not provided review all locales in the repository")
47+
List<String> locales;
48+
49+
@Parameter(
50+
names = {"--source-text-max-count"},
51+
arity = 1,
52+
description =
53+
"Text unit variant max count per locale sent to review (this param is used to avoid "
54+
+ "sending too many strings to MT)")
55+
int sourceTextMaxCount = 100;
56+
57+
@Parameter(
58+
names = {"--text-unit-ids"},
59+
arity = 1,
60+
description = "The list of TmTextUnitIds to review")
61+
List<Long> textUnitIds;
62+
63+
@Parameter(
64+
names = {"--use-batch"},
65+
arity = 1,
66+
description = "To use the batch API or not")
67+
boolean useBatch = false;
68+
69+
@Autowired CommandHelper commandHelper;
70+
71+
@Autowired RepositoryAiReviewClient repositoryAiReviewClient;
72+
73+
@Override
74+
public boolean shouldShowInCommandList() {
75+
return false;
76+
}
77+
78+
@Override
79+
public void execute() throws CommandException {
80+
81+
consoleWriter
82+
.newLine()
83+
.a("Ai review repository: ")
84+
.fg(Color.CYAN)
85+
.a(repositoryParam)
86+
.reset()
87+
.a(" for locales: ")
88+
.fg(Color.CYAN)
89+
.a(locales == null ? "<all>" : locales.stream().collect(Collectors.joining(", ", "[", "]")))
90+
.println(2);
91+
92+
RepositoryAiReviewClient.ProtoAiReviewResponse protoAiTranslateResponse =
93+
repositoryAiReviewClient.reviewRepository(
94+
new RepositoryAiReviewClient.ProtoAiReviewRequest(
95+
repositoryParam, locales, sourceTextMaxCount, textUnitIds, useBatch));
96+
97+
PollableTask pollableTask = protoAiTranslateResponse.pollableTask();
98+
commandHelper.waitForPollableTask(pollableTask.getId());
99+
}
100+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.box.l10n.mojito.rest.client;
2+
3+
import com.box.l10n.mojito.rest.entity.PollableTask;
4+
import java.util.List;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.springframework.stereotype.Component;
8+
9+
/**
10+
* @author jaurambault
11+
*/
12+
@Component
13+
public class RepositoryAiReviewClient extends BaseClient {
14+
15+
/** logger */
16+
static Logger logger = LoggerFactory.getLogger(RepositoryAiReviewClient.class);
17+
18+
@Override
19+
public String getEntityName() {
20+
return "proto-ai-review";
21+
}
22+
23+
/** Ai review strings in a repository for a given list of locales */
24+
public ProtoAiReviewResponse reviewRepository(ProtoAiReviewRequest protoAiReviewRequest) {
25+
26+
return authenticatedRestTemplate.postForObject(
27+
getBasePathForEntity(), protoAiReviewRequest, ProtoAiReviewResponse.class);
28+
}
29+
30+
public record ProtoAiReviewRequest(
31+
String repositoryName,
32+
List<String> targetBcp47tags,
33+
int sourceTextMaxCountPerLocale,
34+
List<Long> tmTextUnitIds,
35+
boolean useBatch) {}
36+
37+
public record ProtoAiReviewResponse(PollableTask pollableTask) {}
38+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.box.l10n.mojito.entity;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.FetchType;
6+
import jakarta.persistence.ForeignKey;
7+
import jakarta.persistence.Index;
8+
import jakarta.persistence.JoinColumn;
9+
import jakarta.persistence.OneToOne;
10+
import jakarta.persistence.Table;
11+
12+
/**
13+
* We keep a single review per text unit variant for now. Meaning it has to be updated / cleaned up
14+
* to re-review. The review is stored a JSON blob defined in the {@link
15+
* com.box.l10n.mojito.service.oaireview.AiReviewService.AiReviewSingleTextUnitOutput} but that
16+
* format could change any time.
17+
*/
18+
@Entity
19+
@Table(
20+
name = "ai_review_proto",
21+
indexes = {
22+
@Index(
23+
name = "UK__AI_REVIEW_PROTO__TM_TEXT_UNIT_VARIANT_ID",
24+
columnList = "tm_text_unit_variant_id",
25+
unique = true)
26+
})
27+
public class AiReviewProto extends AuditableEntity {
28+
29+
@OneToOne(fetch = FetchType.LAZY)
30+
@JoinColumn(
31+
name = "tm_text_unit_variant_id",
32+
foreignKey = @ForeignKey(name = "FK__AI_REVIEW_PROTO__TM_TEXT_UNIT_VARIANT__ID"))
33+
TMTextUnitVariant tmTextUnitVariant;
34+
35+
@Column(name = "json_review", length = Integer.MAX_VALUE)
36+
String jsonReview;
37+
38+
public TMTextUnitVariant getTmTextUnitVariant() {
39+
return tmTextUnitVariant;
40+
}
41+
42+
public void setTmTextUnitVariant(TMTextUnitVariant tmTextUnitVariant) {
43+
this.tmTextUnitVariant = tmTextUnitVariant;
44+
}
45+
46+
public String getJsonReview() {
47+
return jsonReview;
48+
}
49+
50+
public void setJsonReview(String jsonReview) {
51+
this.jsonReview = jsonReview;
52+
}
53+
}

webapp/src/main/java/com/box/l10n/mojito/rest/textunit/AiReviewConfigurationProperties.java

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)