Repository: oppia/oppia

Issue:
Skill ids are not viewable in My Contributions > Skills
<!--
  - Thanks for taking the time to report a bug in the Oppia project.
  - Before filing a new issue, please do a quick search to check that it hasn't
  - already been filed on the [issue tracker](https://github.com/oppia/oppia/issues)._
  -->

**Describe the bug**
Skill id error and skills are not loaded

**To Reproduce**
Steps to reproduce the behavior:
 1. Go to [https://oppiatestserver.appspot.com/contributor-dashboard)](https://oppiatestserver.appspot.com/contributor-dashboard)
 2. Click on 'My Contributions.'
 3. Click on 'question' tab with dev console open
 4. See error

**Observed behavior**
No skill ID error raised on page and in console

**Expected behavior**
No errors and the skills to load

**Screenshots**
Console Error:
![Screen Shot 2020-09-15 at 7 27 11 PM](https://user-images.githubusercontent.com/35385366/93285385-91633680-f789-11ea-8dc0-37e0071d174a.png)

Page:
![Screen Shot 2020-09-15 at 7 27 16 PM](https://user-images.githubusercontent.com/35385366/93285390-93c59080-f789-11ea-92ca-d146d3ce3620.png)

**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: [macOS]
 - Browser [chrome]
 - Version [85]






Discussion:
Hi @jimbyo, changelog labels should not be used on issues. I’m removing the label. You can learn more about labels <a href="https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#labeling-issues-and-pull-requests">here</a>. Thanks!
Looks like this is occurring when fetching reviewable question suggestions in core/controllers/suggestion.py:73. The fix may be to pass in `strict=False` to the `skill_fetchers.get_multi_skills()` call in case the skill no longer exists (we delete skill opportunities when their corresponding skills get deleted, but we can add this as a defensive check). The skill state may have gotten corrupted somehow on the test server.

Resolving patch:
diff --git a/core/domain/opportunity_services.py b/core/domain/opportunity_services.py
--- a/core/domain/opportunity_services.py
+++ b/core/domain/opportunity_services.py
@@ -623,9 +623,10 @@ def get_skill_opportunities_by_ids(ids):
         opportunity_models.SkillOpportunityModel.get_multi(ids))
     opportunities = []
     for skill_opportunity_model in skill_opportunity_models:
-        skill_opportunity = (
-            get_skill_opportunity_from_model(skill_opportunity_model))
-        opportunities.append(skill_opportunity)
+        if skill_opportunity_model is not None:
+            skill_opportunity = (
+                get_skill_opportunity_from_model(skill_opportunity_model))
+            opportunities.append(skill_opportunity)
     return opportunities
 
 


Test patch:
diff --git a/core/domain/opportunity_services_test.py b/core/domain/opportunity_services_test.py
--- a/core/domain/opportunity_services_test.py
+++ b/core/domain/opportunity_services_test.py
@@ -41,8 +41,11 @@
 from core.tests import test_utils
 import python_utils
 
-(suggestion_models, feedback_models) = models.Registry.import_models([
-    models.NAMES.suggestion, models.NAMES.feedback])
+(
+    suggestion_models, feedback_models, opportunity_models
+) = models.Registry.import_models([
+    models.NAMES.suggestion, models.NAMES.feedback, models.NAMES.opportunity
+])
 
 
 class OpportunityServicesIntegrationTest(test_utils.GenericTestBase):
@@ -881,3 +884,20 @@ def _mock_logging_function(msg, *args):
                 observed_log_messages[0],
                 'Missing language codes [u\'new_lang\'] in exploration '
                 'opportunity model with id 0')
+
+    def _mock_get_multi_function(self, unused_ids):
+        """Mocks opportunity_models.SkillOpportunityModel.get_multi()"""
+        return [None]
+
+    def test_get_skill_opportunities_by_ids_if_returns_empty_list(
+            self):
+        get_multi_swap = self.swap(
+            opportunity_models.SkillOpportunityModel,
+            'get_multi', self._mock_get_multi_function)
+
+        with get_multi_swap:
+            opportunities = (
+                opportunity_services.get_skill_opportunities_by_ids(
+                    ['0']))
+
+        self.assertEqual(len(opportunities), 0)