Repository: oppia/oppia

Issue:
[CD] Translations review dashboard does not display lessons for certain in-review translations
Occurring for: 
* Spanish translations in topic Division.
* Hinglish translations in Addition and Subtraction topic.


Discussion:

Resolving patch:
diff --git a/core/controllers/contributor_dashboard.py b/core/controllers/contributor_dashboard.py
--- a/core/controllers/contributor_dashboard.py
+++ b/core/controllers/contributor_dashboard.py
@@ -307,19 +307,27 @@ def _get_reviewable_exploration_opportunity_summaries(
             for topic in topics
             for reference in topic.get_all_story_references()
             if reference.story_is_published])
-        in_review_suggestions = (
-            suggestion_services.get_reviewable_translation_suggestions(user_id))
-        in_review_suggestion_target_ids = [
+        topic_exp_ids = [
+            node.exploration_id
+            for story in topic_stories
+            for node in story.story_contents.get_ordered_nodes()
+        ]
+        in_review_suggestions, _ = (
+            suggestion_services
+            .get_reviewable_translation_suggestions_by_offset(
+                user_id, topic_exp_ids, None, 0))
+        # Filter out suggestions that should not be shown to the user.
+        # This is defined as a set as we only care about the unique IDs.
+        in_review_suggestion_target_ids = {
             suggestion.target_id
             for suggestion in
             suggestion_services.get_suggestions_with_translatable_explorations(
                 in_review_suggestions)
-        ]
+        }
         exp_ids = [
-            node.exploration_id
-            for story in topic_stories
-            for node in story.story_contents.get_ordered_nodes()
-            if node.exploration_id in in_review_suggestion_target_ids
+            exp_id
+            for exp_id in topic_exp_ids
+            if exp_id in in_review_suggestion_target_ids
         ]
         return (
             opportunity_services.get_exploration_opportunity_summaries_by_ids(
diff --git a/core/domain/suggestion_services.py b/core/domain/suggestion_services.py
--- a/core/domain/suggestion_services.py
+++ b/core/domain/suggestion_services.py
@@ -602,7 +602,8 @@ def get_reviewable_translation_suggestions_by_offset(
             suggestions are fetched. If the list consists of some
             valid number of ids, suggestions corresponding to the
             IDs are fetched.
-        limit: int. The maximum number of results to return.
+        limit: int|None. The maximum number of results to return. If None,
+            all available results are returned.
         offset: int. The number of results to skip from the beginning of all
             results matching the query.
 
@@ -617,9 +618,12 @@ def get_reviewable_translation_suggestions_by_offset(
         user_id)
     language_codes = (
         contribution_rights.can_review_translation_for_language_codes)
+    # The user cannot review any translations, so return early.
+    if len(language_codes) == 0:
+        return [], offset
 
     in_review_translation_suggestions = []
-    next_offset = 0
+    next_offset = offset
     if opportunity_summary_exp_ids is None:
         in_review_translation_suggestions, next_offset = (
             suggestion_models.GeneralSuggestionModel
@@ -641,32 +645,6 @@ def get_reviewable_translation_suggestions_by_offset(
     return translation_suggestions, next_offset
 
 
-def get_reviewable_translation_suggestions(user_id):
-    """Returns the target IDs of translation suggestions for which the supplied
-    user can review.
-
-    Args:
-        user_id: str. The ID of the user.
-
-    Returns:
-        list(str). A set of target IDs of the translation suggestions that the
-        supplied user can review.
-    """
-    contribution_rights = user_services.get_user_contribution_rights(user_id)
-    language_codes = (
-        contribution_rights.can_review_translation_for_language_codes)
-    # The user cannot review any translations, so return early.
-    if len(language_codes) == 0:
-        return set()
-    in_review_translation_suggestions = (
-        suggestion_models.GeneralSuggestionModel
-        .get_in_review_translation_suggestions(user_id, language_codes))
-    return [
-        get_suggestion_from_model(s)
-        for s in in_review_translation_suggestions
-    ]
-
-
 def get_reviewable_question_suggestions_by_offset(
         user_id, limit, offset):
     """Returns a list of question suggestions which the user
diff --git a/core/storage/suggestion/gae_models.py b/core/storage/suggestion/gae_models.py
--- a/core/storage/suggestion/gae_models.py
+++ b/core/storage/suggestion/gae_models.py
@@ -315,44 +315,6 @@ def get_translation_suggestions_in_review_with_exp_id(
             cls.target_id == exp_id
         )).fetch(feconf.DEFAULT_SUGGESTION_QUERY_LIMIT)
 
-    @classmethod
-    def get_multiple_suggestions_from_suggestion_ids(
-        cls, suggestion_ids: List[str]
-    ) -> List[Optional[GeneralSuggestionModel]]:
-        """Returns suggestions matching the supplied suggestion IDs.
-
-        Args:
-            suggestion_ids: list(str). Suggestion IDs of suggestions that need
-                to be returned.
-
-        Returns:
-            list(SuggestionModel|None). A list of suggestions in matching the
-            supplied suggestion IDs.
-        """
-        return GeneralSuggestionModel.get_multi(suggestion_ids)
-
-    @classmethod
-    def get_translation_suggestions_in_review_ids_with_exp_id(
-            cls, target_exp_ids: List[str]
-    ) -> List[str]:
-        """Returns IDs of in review translation suggestions matching the
-        supplied target IDs.
-
-        Args:
-            target_exp_ids: list(str). Exploration IDs matching the target ID
-                of the translation suggestions.
-
-        Returns:
-            list(str). A list of IDs of translation suggestions in review
-            with given target_exp_ids.
-        """
-        suggestion_keys = GeneralSuggestionModel.query(
-            cls.status == STATUS_IN_REVIEW,
-            GeneralSuggestionModel.target_id.IN(target_exp_ids)
-            ).fetch(keys_only=True)
-
-        return [suggestion_key.id() for suggestion_key in suggestion_keys]
-
     @classmethod
     def get_translation_suggestion_ids_with_exp_ids(
             cls, exp_ids: List[str]
@@ -498,7 +460,7 @@ def get_in_review_translation_suggestions(
     @classmethod
     def get_in_review_translation_suggestions_by_offset(
         cls,
-        limit: int,
+        limit: Optional[int],
         offset: int,
         user_id: str,
         language_codes: List[str]
@@ -508,7 +470,8 @@ def get_in_review_translation_suggestions_by_offset(
         language_codes.
 
         Args:
-            limit: int. Maximum number of entities to be returned.
+            limit: int|None. Maximum number of entities to be returned. If None,
+                returns all matching entities.
             offset: int. Number of results to skip from the beginning of all
                 results matching the query.
             user_id: str. The id of the user trying to make this query. As a
@@ -534,6 +497,8 @@ def get_in_review_translation_suggestions_by_offset(
 
         results: Sequence[GeneralSuggestionModel] = (
             suggestion_query.fetch(limit, offset=offset)
+            if limit is not None
+            else suggestion_query.fetch(offset=offset)
         )
         next_offset = offset + len(results)
 


Test patch:
diff --git a/core/domain/suggestion_services_test.py b/core/domain/suggestion_services_test.py
--- a/core/domain/suggestion_services_test.py
+++ b/core/domain/suggestion_services_test.py
@@ -1404,67 +1404,6 @@ def test_get_translation_suggestions_in_review_by_exploration_returns_only_sugge
 
         self.assertEqual(len(suggestions), 1)
 
-    def test_get_reviewable_translation_suggestion_target_ids(self):
-        # Add a few translation suggestions in different languages.
-        self._create_translation_suggestion_with_language_code('hi')
-        self._create_translation_suggestion_with_language_code('hi')
-        self._create_translation_suggestion('pt', self.target_id_2)
-        self._create_translation_suggestion('bn', self.target_id_3)
-        self._create_translation_suggestion('bn', self.target_id_3)
-        # Provide the user permission to review suggestions in particular
-        # languages.
-        user_services.allow_user_to_review_translation_in_language(
-            self.reviewer_id_1, 'hi')
-        user_services.allow_user_to_review_translation_in_language(
-            self.reviewer_id_1, 'pt')
-
-        suggestions = (
-            suggestion_services.get_reviewable_translation_suggestions(
-                self.reviewer_id_1))
-
-        # Expect that the results correspond to translation suggestions that the
-        # user has rights to review.
-        self.assertEqual(len(suggestions), 3)
-        self.assertEqual(suggestions[0].target_id, self.target_id_1)
-        self.assertEqual(
-            suggestions[0].suggestion_type,
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT)
-        self.assertEqual(
-            suggestions[0].status,
-            suggestion_models.STATUS_IN_REVIEW)
-        self.assertEqual(suggestions[1].target_id, self.target_id_1)
-        self.assertEqual(
-            suggestions[1].suggestion_type,
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT)
-        self.assertEqual(
-            suggestions[1].status,
-            suggestion_models.STATUS_IN_REVIEW)
-        self.assertEqual(suggestions[2].target_id, self.target_id_2)
-        self.assertEqual(
-            suggestions[2].suggestion_type,
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT)
-        self.assertEqual(
-            suggestions[2].status,
-            suggestion_models.STATUS_IN_REVIEW)
-
-    def test_get_reviewable_translation_suggestion_target_ids_with_no_reviewable_languages( # pylint: disable=line-too-long
-        self
-    ):
-        # Add a few translation suggestions in different languages.
-        self._create_translation_suggestion_with_language_code('hi')
-        self._create_translation_suggestion_with_language_code('hi')
-        self._create_translation_suggestion('pt', self.target_id_2)
-        self._create_translation_suggestion('bn', self.target_id_3)
-        self._create_translation_suggestion('bn', self.target_id_3)
-
-        suggestions = (
-            suggestion_services.get_reviewable_translation_suggestions(
-                self.reviewer_id_1))
-
-        # The user does not have rights to review any languages, so expect an
-        # empty result.
-        self.assertEqual(len(suggestions), 0)
-
     def test_get_reviewable_translation_suggestions_with_valid_exp_ids(
             self):
         # Add a few translation suggestions in different languages.
@@ -1555,6 +1494,27 @@ def test_get_reviewable_translation_suggestions_with_none_exp_ids(
         expected_language_code_list = ['hi', 'hi', 'pt']
         self.assertEqual(actual_language_code_list, expected_language_code_list)
 
+    def test_get_reviewable_translation_suggestions_with_no_reviewable_languages( # pylint: disable=line-too-long
+            self):
+        # Add a few translation suggestions in different languages.
+        self._create_translation_suggestion_with_language_code('hi')
+        self._create_translation_suggestion_with_language_code('hi')
+        self._create_translation_suggestion_with_language_code('pt')
+        self._create_translation_suggestion_with_language_code('bn')
+        self._create_translation_suggestion_with_language_code('bn')
+
+        # Get all reviewable translation suggestions.
+        suggestions, offset = (
+            suggestion_services.
+            get_reviewable_translation_suggestions_by_offset(
+                self.reviewer_id_1, None,
+                constants.OPPORTUNITIES_PAGE_SIZE, 0))
+
+        # The user does not have rights to review any languages, so expect an
+        # empty result.
+        self.assertEqual(len(suggestions), 0)
+        self.assertEqual(offset, 0)
+
     def test_get_reviewable_question_suggestions(self):
         # Add a few translation suggestions in different languages.
         self._create_translation_suggestion_with_language_code('hi')
diff --git a/core/storage/suggestion/gae_models_test.py b/core/storage/suggestion/gae_models_test.py
--- a/core/storage/suggestion/gae_models_test.py
+++ b/core/storage/suggestion/gae_models_test.py
@@ -383,55 +383,6 @@ def test_get_in_review_translation_suggestions(self) -> None:
             suggestions[1].status,
             suggestion_models.STATUS_IN_REVIEW)
 
-    def test_get_translation_suggestions_in_review_ids_with_valid_exp(
-        self) -> None:
-        suggestion_models.GeneralSuggestionModel.create(
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
-            feconf.ENTITY_TYPE_EXPLORATION,
-            'exp1', self.target_version_at_submission,
-            suggestion_models.STATUS_IN_REVIEW, 'author_3',
-            'reviewer_2', self.change_cmd, self.score_category,
-            'exploration.exp1.thread_6', self.translation_language_code)
-        suggestion_models.GeneralSuggestionModel.create(
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
-            feconf.ENTITY_TYPE_EXPLORATION,
-            'exp1', self.target_version_at_submission,
-            suggestion_models.STATUS_IN_REVIEW, 'author_4',
-            'reviewer_2', self.change_cmd, self.score_category,
-            'exploration.exp1.thread_7', self.translation_language_code)
-
-        suggestion_ids = (
-            suggestion_models.GeneralSuggestionModel
-            .get_translation_suggestions_in_review_ids_with_exp_id(
-                ['exp1']))
-
-        self.assertEqual(len(suggestion_ids), 3)
-
-    def test_get_multiple_translation_suggestions_in_review(self) -> None:
-        suggestion_models.GeneralSuggestionModel.create(
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
-            feconf.ENTITY_TYPE_EXPLORATION,
-            'exp1', self.target_version_at_submission,
-            suggestion_models.STATUS_IN_REVIEW, 'author_3',
-            'reviewer_2', self.change_cmd, self.score_category,
-            'exploration.exp1.thread_6', self.translation_language_code)
-        suggestion_models.GeneralSuggestionModel.create(
-            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
-            feconf.ENTITY_TYPE_EXPLORATION,
-            'exp1', self.target_version_at_submission,
-            suggestion_models.STATUS_IN_REVIEW, 'author_4',
-            'reviewer_2', self.change_cmd, self.score_category,
-            'exploration.exp1.thread_7', self.translation_language_code)
-
-        suggestion_ids = (
-            suggestion_models.GeneralSuggestionModel
-            .get_translation_suggestions_in_review_ids_with_exp_id(
-                ['exp1']))
-        suggestions = (
-            suggestion_models.GeneralSuggestionModel
-            .get_multiple_suggestions_from_suggestion_ids(suggestion_ids))
-        self.assertEqual(len(suggestions), 3)
-
     def test_get_translation_suggestions_in_review_with_valid_exp(self) -> None:
         suggestion_models.GeneralSuggestionModel.create(
             feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
@@ -556,6 +507,41 @@ def test_get_in_review_translation_suggestions_by_offset(self) -> None:
         self.assertEqual(len(results), 0)
         self.assertEqual(offset_3, 2)
 
+    def test_get_in_review_translation_suggestions_by_offset_no_limit(
+        self) -> None:
+        suggestion_1_id = 'exploration.exp1.thread_6'
+        suggestion_2_id = 'exploration.exp1.thread_7'
+        user_id = 'author1'
+        suggestion_models.GeneralSuggestionModel.create(
+            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
+            feconf.ENTITY_TYPE_EXPLORATION,
+            'exp1', self.target_version_at_submission,
+            suggestion_models.STATUS_IN_REVIEW, 'author_3',
+            'reviewer_2', self.change_cmd, self.score_category,
+            suggestion_1_id, self.translation_language_code)
+        suggestion_models.GeneralSuggestionModel.create(
+            feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
+            feconf.ENTITY_TYPE_EXPLORATION,
+            'exp1', self.target_version_at_submission,
+            suggestion_models.STATUS_IN_REVIEW, 'author_4',
+            'reviewer_2', self.change_cmd, self.score_category,
+            suggestion_2_id, self.translation_language_code)
+
+        results, offset = (
+            suggestion_models.GeneralSuggestionModel
+            .get_in_review_translation_suggestions_by_offset(
+                limit=None,
+                offset=0,
+                user_id=user_id,
+                language_codes=[self.translation_language_code]))
+
+        # Ruling out the possibility of None for mypy type checking.
+        assert results is not None
+        self.assertEqual(len(results), 2)
+        self.assertEqual(results[0].id, suggestion_1_id)
+        self.assertEqual(results[1].id, suggestion_2_id)
+        self.assertEqual(offset, 2)
+
     def test_get_in_review_question_suggestions_by_offset(self) -> None:
         suggestion_1_id = 'skill1.thread1'
         suggestion_2_id = 'skill1.thread2'