DEV: Return finished and total number of candidates needed for translation rather than a percentage (#34357)

To better support https://github.com/discourse/discourse/pull/34239, we
return the raw values here instead of a derived percentage.
This commit is contained in:
Natalie Tay
2025-08-15 10:25:27 -07:00
committed by GitHub
parent 98e62df173
commit c002d10b11
4 changed files with 47 additions and 47 deletions
@@ -19,8 +19,8 @@ module DiscourseAi
.cache
.fetch(get_completion_cache_key(locale), expires_in: COMPLETION_CACHE_TTL) do
done, total = calculate_completion_per_locale(locale)
return 1.0 if total.zero?
done / total.to_f
return { done: 0, total: 0 } if total.zero?
{ done:, total: }
end
end
@@ -19,48 +19,48 @@ describe DiscourseAi::Translation::CategoryCandidates do
describe ".get_completion_per_locale" do
context "when (scenario A) percentage determined by category's locale" do
it "returns 100% completion if all categories are in the locale" do
it "returns done = total if all categories are in the locale" do
locale = "pt_BR"
Fabricate(:category, locale:)
Category.update_all(locale: locale)
Fabricate(:category, locale: "pt")
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1.0)
expect(completion).to eq({ done: Category.count, total: Category.count })
end
it "returns X% completion if some categories are in the locale" do
it "returns correct done and total if some categories are in the locale" do
locale = "pt_BR"
Fabricate(:category, locale:)
Fabricate(:category, locale: "not_pt")
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1 / Category.count.to_f)
expect(completion).to eq({ done: 1, total: Category.count })
end
end
context "when (scenario B) percentage determined by category localizations" do
it "returns 100% completion if all categories have a localization in the locale" do
it "returns done = total if all categories have a localization in the locale" do
locale = "pt_BR"
Fabricate(:category)
Category.all.each { |category| Fabricate(:category_localization, category:, locale:) }
Fabricate(:category_localization, locale: "pt")
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1.0)
expect(completion).to eq({ done: Category.count, total: Category.count })
end
it "returns X% completion if some categories have a localization in the locale" do
it "returns correct done and total if some categories have a localization in the locale" do
locale = "es"
Fabricate(:category_localization, locale:)
Fabricate(:category_localization, locale: "pt")
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1 / Category.count.to_f)
expect(completion).to eq({ done: 1, total: Category.count })
end
end
it "returns the correct percentage based on (scenario A & B) `category.locale` and `CategoryLocalization` in the specified locale" do
it "returns the correct done and total based on (scenario A & B) `category.locale` and `CategoryLocalization` in the specified locale" do
locale = "pt_BR"
# translated candidates
@@ -80,25 +80,25 @@ describe DiscourseAi::Translation::CategoryCandidates do
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale(locale)
translated_candidates = 2 # category1 + category2
total_candidates = Category.count - 1 # excluding the read restricted category
expect(completion).to eq(translated_candidates / total_candidates.to_f)
expect(completion).to eq({ done: translated_candidates, total: total_candidates })
end
it "does not exceed 100% completion when category.locale and category_localization both exist" do
it "does not allow done to exceed total when category.locale and category_localization both exist" do
locale = "pt_BR"
Category.update_all(locale:)
category = Fabricate(:category, locale:)
Fabricate(:category_localization, category:, locale:)
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale(locale)
expect(completion).to be(1.0)
expect(completion).to eq({ done: Category.count, total: Category.count })
end
it "returns 100% completion when there are no categories" do
it "returns nil - nil for done and total when there are no categories" do
SiteSetting.ai_translation_backfill_limit_to_public_content = false
Category.destroy_all
completion = DiscourseAi::Translation::CategoryCandidates.get_completion_per_locale("pt")
expect(completion).to eq(1.0)
expect(completion).to eq({ done: 0, total: 0 })
end
end
end
@@ -60,49 +60,49 @@ describe DiscourseAi::Translation::PostCandidates do
end
describe ".get_completion_per_locale" do
context "when (scenario A) percentage determined by post's locale" do
it "returns 100% completion if all posts are in the locale" do
context "when (scenario A) 'done' determined by post's locale" do
it "returns total = done if all posts are in the locale" do
locale = "pt_BR"
Fabricate(:post, locale:)
Post.update_all(locale: locale)
Fabricate(:post, locale: "pt")
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1.0)
expect(completion).to eq({ done: 2, total: 2 })
end
it "returns X% completion if some posts are in the locale" do
it "returns correct done and total if some posts are in the locale" do
locale = "es"
Fabricate(:post, locale:)
Fabricate(:post, locale: "not_es")
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1 / Post.count.to_f)
expect(completion).to eq({ done: 1, total: 2 })
end
end
context "when (scenario B) percentage determined by post localizations" do
it "returns 100% completion if all posts have a localization in the locale" do
context "when (scenario B) 'done' determined by post localizations" do
it "returns done = total if all posts have a localization in the locale" do
locale = "pt_BR"
Fabricate(:post)
Post.all.each { |post| Fabricate(:post_localization, post:, locale:) }
Fabricate(:post_localization, locale: "pt")
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1.0)
expect(completion).to eq({ done: Post.count, total: Post.count })
end
it "returns X% completion if some posts have a localization in the locale" do
it "returns correct done and total if some posts have a localization in the locale" do
locale = "es"
Fabricate(:post_localization, locale:)
Fabricate(:post_localization, locale: "not_es")
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1 / Post.count.to_f)
expect(completion).to eq({ done: 1, total: Post.count })
end
end
it "returns the correct percentage based on (scenario A & B) `post.locale` and `PostLocalization` in the specified locale" do
it "returns the correct done and total based on (scenario A & B) `post.locale` and `PostLocalization` in the specified locale" do
locale = "es"
# translated candidates
@@ -121,23 +121,23 @@ describe DiscourseAi::Translation::PostCandidates do
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale(locale)
translated_candidates = 2 # post1 + post2
total_candidates = Post.count - 1 # excluding the bot post
expect(completion).to eq(translated_candidates / total_candidates.to_f)
expect(completion).to eq({ done: translated_candidates, total: total_candidates })
end
it "does not exceed 100% completion when post.locale and post_localization both exist" do
it "does not allow done to exceed total when post.locale and post_localization both exist" do
locale = "es"
post = Fabricate(:post, locale:)
Fabricate(:post_localization, post:, locale:)
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale(locale)
expect(completion).to be(1.0)
expect(completion).to eq({ done: 1, total: 1 })
end
it "returns 100% completion when no posts are present" do
it "returns nil - nil for done and total when no posts are present" do
SiteSetting.ai_translation_backfill_max_age_days = 0
completion = DiscourseAi::Translation::PostCandidates.get_completion_per_locale("es")
expect(completion).to eq(1.0)
expect(completion).to eq({ done: 0, total: 0 })
end
end
end
@@ -52,28 +52,28 @@ describe DiscourseAi::Translation::TopicCandidates do
end
describe ".get_completion_per_locale" do
context "when (scenario A) percentage determined by topic's locale" do
it "returns 100% completion if all topics are in the locale" do
context "when (scenario A) 'done' determined by topic's locale" do
it "returns total = done if all topics are in the locale" do
locale = "pt_BR"
Fabricate(:topic, locale:)
Topic.update_all(locale: locale)
Fabricate(:topic, locale: "pt")
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1.0)
expect(completion).to eq({ done: 2, total: 2 })
end
it "returns X% completion if some topics are in the locale" do
it "returns correct done and total if some topics are in the locale" do
locale = "es"
Fabricate(:topic, locale:)
Fabricate(:topic, locale: "not_es")
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1 / Topic.count.to_f)
expect(completion).to eq({ done: 1, total: 2 })
end
end
context "when (scenario B) percentage determined by topic localizations" do
context "when (scenario B) 'done' determined by topic localizations" do
it "returns 100% completion if all topics have a localization in the locale" do
locale = "pt_BR"
Fabricate(:topic)
@@ -81,20 +81,20 @@ describe DiscourseAi::Translation::TopicCandidates do
Fabricate(:topic_localization, locale: "pt")
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1.0)
expect(completion).to eq({ done: Topic.count, total: Topic.count })
end
it "returns X% completion if some topics have a localization in the locale" do
it "returns correct done and total if some topics have a localization in the locale" do
locale = "es"
Fabricate(:topic_localization, locale:)
Fabricate(:topic_localization, locale: "not_es")
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale(locale)
expect(completion).to eq(1 / Topic.count.to_f)
expect(completion).to eq({ done: 1, total: Topic.count })
end
end
it "returns the correct percentage based on (scenario A & B) `topic.locale` and `TopicLocalization` in the specified locale" do
it "returns the correct done and total based on (scenario A & B) `topic.locale` and `TopicLocalization` in the specified locale" do
locale = "es"
# translated candidates
@@ -113,24 +113,24 @@ describe DiscourseAi::Translation::TopicCandidates do
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale(locale)
translated_candidates = 2 # topic1 + topic2
total_candidates = Topic.count - 1 # excluding the bot topic
expect(completion).to eq(translated_candidates / total_candidates.to_f)
expect(completion).to eq({ done: 2, total: 3 })
end
it "does not exceed 100% completion when topic.locale and topic_localization both exist" do
it "does not allow done to exceed total when topic.locale and topic_localization both exist" do
locale = "es"
topic = Fabricate(:topic, locale:)
Fabricate(:topic_localization, topic:, locale:)
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale(locale)
expect(completion).to be(1.0)
expect(completion).to eq({ done: 1, total: 1 })
end
it "returns 100% if no topics" do
it "returns nil - nil for done and total if no topics" do
SiteSetting.ai_translation_backfill_max_age_days = 0
SiteSetting.ai_translation_backfill_limit_to_public_content = false
completion = DiscourseAi::Translation::TopicCandidates.get_completion_per_locale("es")
expect(completion).to eq(1.0)
expect(completion).to eq({ done: 0, total: 0 })
end
end
end