Esempio n. 1
0
    def test_get_skill_descriptions_by_ids(self):
        self.save_new_skill(
            'skill_2',
            self.USER_ID,
            'Description 2',
            misconceptions=[],
            skill_contents=skill_domain.SkillContents(
                state_domain.SubtitledHtml('1', '<p>Explanation</p>'),
                [state_domain.SubtitledHtml('2', '<p>Example 1</p>')],
                state_domain.RecordedVoiceovers.from_dict(
                    {'voiceovers_mapping': {
                        '1': {},
                        '2': {}
                    }}),
                state_domain.WrittenTranslations.from_dict(
                    {'translations_mapping': {
                        '1': {},
                        '2': {}
                    }})))
        self.save_new_skill(
            'skill_3',
            self.USER_ID,
            'Description 3',
            misconceptions=[],
            skill_contents=skill_domain.SkillContents(
                state_domain.SubtitledHtml('1', '<p>Explanation</p>'),
                [state_domain.SubtitledHtml('2', '<p>Example 1</p>')],
                state_domain.RecordedVoiceovers.from_dict(
                    {'voiceovers_mapping': {
                        '1': {},
                        '2': {}
                    }}),
                state_domain.WrittenTranslations.from_dict(
                    {'translations_mapping': {
                        '1': {},
                        '2': {}
                    }})))

        with self.swap(feconf, 'CAN_SEND_EMAILS', True):
            skill_descriptions = skill_services.get_skill_descriptions_by_ids(
                'topic_id', [self.SKILL_ID, 'skill_2', 'skill_3'])
            messages = self.mail_stub.get_sent_messages(
                to=feconf.ADMIN_EMAIL_ADDRESS)
            self.assertEqual(len(messages), 0)

            skill_services.delete_skill(self.USER_ID, 'skill_2')
            skill_descriptions = skill_services.get_skill_descriptions_by_ids(
                'topic_id', [self.SKILL_ID, 'skill_2', 'skill_3'])
            messages = self.mail_stub.get_sent_messages(
                to=feconf.ADMIN_EMAIL_ADDRESS)
            expected_email_html_body = ('The deleted skills: skill_2 are still'
                                        ' present in topic with id topic_id')
            self.assertEqual(len(messages), 1)
            self.assertIn(expected_email_html_body, messages[0].html.decode())
            self.assertEqual(
                skill_descriptions, {
                    self.SKILL_ID: 'Description',
                    'skill_2': None,
                    'skill_3': 'Description 3'
                })
    def test_job_skips_deleted_skills(self):
        rubrics = [
            skill_domain.Rubric(
                constants.SKILL_DIFFICULTIES[0], ['<p>Explanation 1</p>']),
            skill_domain.Rubric(
                constants.SKILL_DIFFICULTIES[1], ['<p>Explanation 2</p>']),
            skill_domain.Rubric(
                constants.SKILL_DIFFICULTIES[2], ['<p>Explanation 3</p>'])]

        skill = skill_domain.Skill.create_default_skill(
            'valid_skill', 'A description', rubrics)
        skill_services.save_new_skill(self.albert_id, skill)

        skill_services.delete_skill(
            self.albert_id, skill.id)
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            skill_fetchers.get_skill_by_id(skill.id)

        job_id = (
            skill_jobs_one_off.SkillMathRteAuditOneOffJob.create_new())
        skill_jobs_one_off.SkillMathRteAuditOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        output = skill_jobs_one_off.SkillMathRteAuditOneOffJob.get_output(
            job_id)
        self.assertEqual(output, [])
    def test_get_descriptions_of_skills(self):
        self.save_new_skill(
            'skill_id_1', self.user_id_admin, description='Description 1',
            misconceptions=[],
            skill_contents=skill_domain.SkillContents(
                state_domain.SubtitledHtml('1', '<p>Explanation</p>'), [
                    state_domain.SubtitledHtml('2', '<p>Example 1</p>')],
                state_domain.RecordedVoiceovers.from_dict(
                    {'voiceovers_mapping': {'1': {}, '2': {}}}),
                state_domain.WrittenTranslations.from_dict(
                    {'translations_mapping': {'1': {}, '2': {}}})))
        self.save_new_skill(
            'skill_id_2', self.user_id_admin, description='Description 2',
            misconceptions=[],
            skill_contents=skill_domain.SkillContents(
                state_domain.SubtitledHtml('1', '<p>Explanation</p>'), [
                    state_domain.SubtitledHtml('2', '<p>Example 1</p>')],
                state_domain.RecordedVoiceovers.from_dict(
                    {'voiceovers_mapping': {'1': {}, '2': {}}}),
                state_domain.WrittenTranslations.from_dict(
                    {'translations_mapping': {'1': {}, '2': {}}})))

        skill_services.delete_skill(self.user_id_admin, 'skill_id_2')
        skill_descriptions, deleted_skill_ids = (
            skill_services.get_descriptions_of_skills(
                ['skill_id_1', 'skill_id_2']))
        self.assertEqual(deleted_skill_ids, ['skill_id_2'])
        self.assertEqual(
            skill_descriptions, {
                'skill_id_1': 'Description 1',
                'skill_id_2': None
            }
        )
Esempio n. 4
0
    def test_migration_job_skips_deleted_skill(self):
        """Tests that the skill migration job skips deleted skill
        and does not attempt to migrate.
        """
        skill = skill_domain.Skill.create_default_skill(
            self.SKILL_ID, 'A description', self.rubrics)
        skill_services.save_new_skill(self.albert_id, skill)

        # Delete the skill before migration occurs.
        skill_services.delete_skill(self.albert_id, self.SKILL_ID)

        # Ensure the skill is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            skill_fetchers.get_skill_by_id(self.SKILL_ID)

        # Start migration job on sample skill.
        job_id = (skill_jobs_one_off.SkillMigrationOneOffJob.create_new())
        skill_jobs_one_off.SkillMigrationOneOffJob.enqueue(job_id)

        # This running without errors indicates the deleted skill is
        # being ignored.
        self.process_and_flush_pending_mapreduce_tasks()

        # Ensure the skill is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            skill_fetchers.get_skill_by_id(self.SKILL_ID)

        output = skill_jobs_one_off.SkillMigrationOneOffJob.get_output(job_id)
        expected = [[u'skill_deleted', [u'Encountered 1 deleted skills.']]]
        self.assertEqual(expected, [ast.literal_eval(x) for x in output])
    def post(self):
        """Handles the POST request."""
        old_skill_id = self.payload.get('old_skill_id')
        new_skill_id = self.payload.get('new_skill_id')
        new_skill = skill_fetchers.get_skill_by_id(new_skill_id, strict=False)
        if new_skill is None:
            raise self.PageNotFoundException(
                Exception('The new skill with the given id doesn\'t exist.'))
        old_skill = skill_fetchers.get_skill_by_id(old_skill_id, strict=False)
        if old_skill is None:
            raise self.PageNotFoundException(
                Exception('The old skill with the given id doesn\'t exist.'))

        skill_services.replace_skill_id_in_all_topics(self.user_id,
                                                      old_skill_id,
                                                      new_skill_id)
        question_services.replace_skill_id_for_all_questions(
            old_skill_id, old_skill.description, new_skill_id)
        changelist = [
            skill_domain.SkillChange({
                'cmd':
                skill_domain.CMD_UPDATE_SKILL_PROPERTY,
                'property_name':
                (skill_domain.SKILL_PROPERTY_SUPERSEDING_SKILL_ID),
                'old_value':
                old_skill.superseding_skill_id,
                'new_value':
                new_skill_id
            })
        ]
        skill_services.update_skill(
            self.user_id, old_skill_id, changelist,
            'Marking the skill as having being merged successfully.')
        skill_services.delete_skill(self.user_id, old_skill_id)
        self.render_json({'merged_into_skill': new_skill_id})
Esempio n. 6
0
 def test_delete_skill(self):
     skill_services.delete_skill(self.USER_ID, self.SKILL_ID)
     self.assertEqual(
         skill_services.get_skill_by_id(self.SKILL_ID, strict=False), None)
     self.assertEqual(
         skill_services.get_skill_summary_by_id(self.SKILL_ID,
                                                strict=False), None)
Esempio n. 7
0
    def test_get_skill_descriptions_by_ids(self):
        self.save_new_skill('skill_2',
                            self.USER_ID,
                            'Description 2',
                            misconceptions=[],
                            skill_contents=skill_domain.SkillContents(
                                'Explanation', ['Example 1']))
        self.save_new_skill('skill_3',
                            self.USER_ID,
                            'Description 3',
                            misconceptions=[],
                            skill_contents=skill_domain.SkillContents(
                                'Explanation', ['Example 1']))
        with self.swap(feconf, 'CAN_SEND_EMAILS', True):
            skill_descriptions = skill_services.get_skill_descriptions_by_ids(
                'topic_id', [self.SKILL_ID, 'skill_2', 'skill_3'])
            messages = self.mail_stub.get_sent_messages(
                to=feconf.ADMIN_EMAIL_ADDRESS)
            self.assertEqual(len(messages), 0)

            skill_services.delete_skill(self.USER_ID, 'skill_2')
            skill_descriptions = skill_services.get_skill_descriptions_by_ids(
                'topic_id', [self.SKILL_ID, 'skill_2', 'skill_3'])
            messages = self.mail_stub.get_sent_messages(
                to=feconf.ADMIN_EMAIL_ADDRESS)
            expected_email_html_body = ('The deleted skills: skill_2 are still'
                                        ' present in topic with id topic_id')
            self.assertEqual(len(messages), 1)
            self.assertIn(expected_email_html_body, messages[0].html.decode())
            self.assertEqual(
                skill_descriptions, {
                    self.SKILL_ID: 'Description',
                    'skill_2': None,
                    'skill_3': 'Description 3'
                })
Esempio n. 8
0
    def delete(self, skill_id):
        """Handles Delete requests."""
        if not feconf.ENABLE_NEW_STRUCTURES:
            raise self.PageNotFoundException

        skill_domain.Skill.require_valid_skill_id(skill_id)
        if not skill_id:
            raise self.PageNotFoundException
        skill_services.delete_skill(self.user_id, skill_id)
Esempio n. 9
0
    def test_get_with_user_logged_in(self):
        skill_services.delete_skill(self.admin_id, self.skill_id_1)
        with self.swap(constants, 'ENABLE_NEW_STRUCTURE_PLAYERS', True):
            self.login(self.NEW_USER_EMAIL)
            with self.swap(feconf, 'CAN_SEND_EMAILS', True):
                messages = self.mail_stub.get_sent_messages(
                    to=feconf.ADMIN_EMAIL_ADDRESS)
                self.assertEqual(len(messages), 0)
                json_response = self.get_json(
                    '%s/%s' % (feconf.TOPIC_DATA_HANDLER, 'public_topic_name'))
                messages = self.mail_stub.get_sent_messages(
                    to=feconf.ADMIN_EMAIL_ADDRESS)
                expected_email_html_body = ('The deleted skills: %s are still'
                                            ' present in topic with id %s' %
                                            (self.skill_id_1, self.topic_id))
                self.assertEqual(len(messages), 1)
                self.assertIn(expected_email_html_body,
                              messages[0].html.decode())
                expected_dict = {
                    'topic_name':
                    'public_topic_name',
                    'topic_id':
                    self.topic_id,
                    'canonical_story_dicts': [{
                        'id': self.story_1.id,
                        'title': self.story_1.title,
                        'description': self.story_1.description,
                        'node_count': self.story_1.node_count,
                        'published': True
                    }],
                    'additional_story_dicts': [{
                        'id': self.story_2.id,
                        'title': self.story_2.title,
                        'description': self.story_2.description,
                        'node_count': self.story_2.node_count,
                        'published': True
                    }],
                    'uncategorized_skill_ids': [self.skill_id_1],
                    'subtopics': [{
                        u'skill_ids': [self.skill_id_2],
                        u'id': 1,
                        u'title': u'subtopic_name'
                    }],
                    'degrees_of_mastery': {
                        self.skill_id_1: 0.3,
                        self.skill_id_2: 0.5
                    },
                    'skill_descriptions': {
                        self.skill_id_1: None,
                        self.skill_id_2: 'Skill Description 2'
                    },
                    'train_tab_should_be_displayed':
                    False
                }
                self.assertDictContainsSubset(expected_dict, json_response)

            self.logout()
Esempio n. 10
0
    def test_get_rubrics_of_linked_skills(self):
        self.save_new_skill(
            'skill_id_1',
            self.user_id_admin,
            'Description 1',
            misconceptions=[],
            skill_contents=skill_domain.SkillContents(
                state_domain.SubtitledHtml('1', '<p>Explanation</p>'),
                [state_domain.SubtitledHtml('2', '<p>Example 1</p>')],
                state_domain.RecordedVoiceovers.from_dict(
                    {'voiceovers_mapping': {
                        '1': {},
                        '2': {}
                    }}),
                state_domain.WrittenTranslations.from_dict(
                    {'translations_mapping': {
                        '1': {},
                        '2': {}
                    }})))
        self.save_new_skill(
            'skill_id_2',
            self.user_id_admin,
            'Description 2',
            misconceptions=[],
            skill_contents=skill_domain.SkillContents(
                state_domain.SubtitledHtml('1', '<p>Explanation</p>'),
                [state_domain.SubtitledHtml('2', '<p>Example 1</p>')],
                state_domain.RecordedVoiceovers.from_dict(
                    {'voiceovers_mapping': {
                        '1': {},
                        '2': {}
                    }}),
                state_domain.WrittenTranslations.from_dict(
                    {'translations_mapping': {
                        '1': {},
                        '2': {}
                    }})))

        skill_services.delete_skill(self.user_id_admin, 'skill_id_2')
        skill_rubrics, deleted_skill_ids = (
            skill_services.get_rubrics_of_skills(['skill_id_1', 'skill_id_2']))
        self.assertEqual(deleted_skill_ids, ['skill_id_2'])

        self.assertEqual(
            skill_rubrics, {
                'skill_id_1': [
                    skill_domain.Rubric(constants.SKILL_DIFFICULTIES[0],
                                        'Explanation 1').to_dict(),
                    skill_domain.Rubric(constants.SKILL_DIFFICULTIES[1],
                                        'Explanation 2').to_dict(),
                    skill_domain.Rubric(constants.SKILL_DIFFICULTIES[2],
                                        'Explanation 3').to_dict()
                ],
                'skill_id_2':
                None
            })
Esempio n. 11
0
    def test_editable_topic_handler_get(self):
        skill_services.delete_skill(self.admin_id, self.skill_id_2)
        # Check that non-admins cannot access the editable topic data.
        self.login(self.NEW_USER_EMAIL)
        self.get_json(
            '%s/%s' % (
                feconf.TOPIC_EDITOR_DATA_URL_PREFIX, self.topic_id),
            expected_status_int=401)
        self.logout()

        # Check that admins can access the editable topic data.
        self.login(self.ADMIN_EMAIL)
        with self.swap(feconf, 'CAN_SEND_EMAILS', True):
            messages = self._get_sent_email_messages(
                feconf.ADMIN_EMAIL_ADDRESS)
            self.assertEqual(len(messages), 0)
            json_response = self.get_json(
                '%s/%s' % (
                    feconf.TOPIC_EDITOR_DATA_URL_PREFIX, self.topic_id))
            self.assertEqual(self.topic_id, json_response['topic_dict']['id'])
            self.assertTrue(
                self.skill_id in json_response['skill_question_count_dict'])
            self.assertEqual(
                json_response['skill_question_count_dict'][self.skill_id], 0)
            self.assertTrue(
                self.skill_id_2 in json_response['skill_question_count_dict'])
            self.assertEqual(
                json_response['skill_question_count_dict'][self.skill_id_2], 0)
            self.assertEqual(
                'Skill Description',
                json_response['skill_id_to_description_dict'][self.skill_id])

            messages = self._get_sent_email_messages(
                feconf.ADMIN_EMAIL_ADDRESS)
            expected_email_html_body = (
                'The deleted skills: %s are still'
                ' present in topic with id %s' % (
                    self.skill_id_2, self.topic_id))
            self.assertEqual(len(messages), 1)
            self.assertIn(
                expected_email_html_body,
                messages[0].html.decode())

        self.logout()

        # Check that editable topic handler is accessed only when a topic id
        # passed has an associated topic.
        self.login(self.ADMIN_EMAIL)

        self.get_json(
            '%s/%s' % (
                feconf.TOPIC_EDITOR_DATA_URL_PREFIX,
                topic_services.get_new_topic_id()), expected_status_int=404)

        self.logout()
Esempio n. 12
0
    def test_delete_skill_deletes_skill_opportunity(self):
        self.save_new_skill(self.SKILL_ID, self.USER_ID, 'skill_description')
        skill_opportunities, _, _ = (
            opportunity_services.get_skill_opportunities(None))
        self.assertEqual(len(skill_opportunities), 1)

        skill_services.delete_skill(self.USER_ID, self.SKILL_ID)

        skill_opportunities, _, _ = (
            opportunity_services.get_skill_opportunities(None))
        self.assertEqual(len(skill_opportunities), 0)
Esempio n. 13
0
    def delete(self, skill_id):
        """Handles Delete requests."""
        skill_domain.Skill.require_valid_skill_id(skill_id)
        if skill_services.skill_has_associated_questions(skill_id):
            raise Exception(
                'Please delete all questions associated with this skill '
                'first.')

        skill_services.delete_skill(self.user_id, skill_id)

        self.render_json(self.values)
Esempio n. 14
0
    def delete(self, skill_id):
        """Handles Delete requests."""

        skill_services.remove_skill_from_all_topics(self.user_id, skill_id)

        if skill_services.skill_has_associated_questions(skill_id):
            raise self.InvalidInputException(
                'Please delete all questions associated with this skill '
                'first.')

        skill_services.delete_skill(self.user_id, skill_id)

        self.render_json(self.values)
Esempio n. 15
0
    def delete(self, skill_id):
        """Handles Delete requests."""
        if not constants.ENABLE_NEW_STRUCTURE_EDITORS:
            raise self.PageNotFoundException

        skill_domain.Skill.require_valid_skill_id(skill_id)
        if skill_services.skill_has_associated_questions(skill_id):
            raise Exception(
                'Please delete all questions associated with this skill '
                'first.')

        skill_services.delete_skill(self.user_id, skill_id)

        self.render_json(self.values)
Esempio n. 16
0
    def delete(self, skill_id):
        """Handles Delete requests."""
        skill_domain.Skill.require_valid_skill_id(skill_id)
        skill_ids_assigned_to_some_topic = (
            topic_services.get_all_skill_ids_assigned_to_some_topic())
        if skill_id in skill_ids_assigned_to_some_topic:
            raise self.InvalidInputException(
                'Cannot delete skill that is assigned to a topic.')
        if skill_services.skill_has_associated_questions(skill_id):
            raise self.InvalidInputException(
                'Please delete all questions associated with this skill '
                'first.')

        skill_services.delete_skill(self.user_id, skill_id)

        self.render_json(self.values)
Esempio n. 17
0
    def test_model_with_last_updated_greater_than_current_time(self):
        skill_services.delete_skill(self.owner_id, '1')
        skill_services.delete_skill(self.owner_id, '2')
        expected_output = [
            (u'[u\'failed validation check for current time check of '
             'SkillSummaryModel\', '
             '[u\'Entity id %s: The last_updated field has a '
             'value %s which is greater than the time when the job was run\']]'
             ) % (self.model_instance_0.id, self.model_instance_0.last_updated)
        ]

        mocked_datetime = datetime.datetime.utcnow() - datetime.timedelta(
            hours=13)
        with datastore_services.mock_datetime_for_datastore(mocked_datetime):
            self.run_job_and_check_output(expected_output,
                                          sort=True,
                                          literal_eval=False)
Esempio n. 18
0
    def test_regeneration_job_for_deleted_skill_returns_empty_list_output(self):
        skill_opp_model_regen_job_class = (
            opportunity_jobs_one_off.SkillOpportunityModelRegenerationJob)

        skill_services.delete_skill(self.admin_id, self.skill_id_1)
        skill_services.delete_skill(self.admin_id, self.skill_id_2)

        job_id = skill_opp_model_regen_job_class.create_new()
        skill_opp_model_regen_job_class.enqueue(job_id)

        self.assertEqual(
            self.count_jobs_in_taskqueue(
                taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 1)
        self.process_and_flush_pending_tasks()

        output = skill_opp_model_regen_job_class.get_output(job_id)
        self.assertEqual(output, [])
Esempio n. 19
0
    def test_editable_topic_handler_put(self):
        # Check that admins can edit a topic.
        change_cmd = {
            'version':
            2,
            'commit_message':
            'Some changes and added a subtopic.',
            'topic_and_subtopic_page_change_dicts': [{
                'cmd': 'update_topic_property',
                'property_name': 'name',
                'old_value': '',
                'new_value': 'A new name'
            }, {
                'cmd': 'update_subtopic_page_property',
                'property_name': 'page_contents_html',
                'old_value': {
                    'html': '',
                    'content_id': 'content'
                },
                'subtopic_id': 1,
                'new_value': {
                    'html': '<p>New Data</p>',
                    'content_id': 'content'
                }
            }, {
                'cmd': 'update_subtopic_property',
                'property_name': 'url_fragment',
                'new_value': 'subtopic-one',
                'old_value': '',
                'subtopic_id': 1
            }, {
                'cmd': 'add_subtopic',
                'subtopic_id': 2,
                'title': 'Title2'
            }, {
                'cmd': 'update_subtopic_property',
                'property_name': 'url_fragment',
                'new_value': 'subtopic-two',
                'old_value': '',
                'subtopic_id': 2
            }, {
                'cmd': 'update_subtopic_page_property',
                'property_name': 'page_contents_html',
                'old_value': {
                    'html': '',
                    'content_id': 'content'
                },
                'new_value': {
                    'html': '<p>New Value</p>',
                    'content_id': 'content'
                },
                'subtopic_id': 2
            }, {
                'cmd': 'update_subtopic_page_property',
                'property_name': 'page_contents_audio',
                'old_value': {
                    'voiceovers_mapping': {
                        'content': {}
                    }
                },
                'new_value': {
                    'voiceovers_mapping': {
                        'content': {
                            'en': {
                                'filename': 'test.mp3',
                                'file_size_bytes': 100,
                                'needs_update': False,
                                'duration_secs': 0.34342
                            }
                        }
                    }
                },
                'subtopic_id': 2
            }]
        }
        self.login(self.CURRICULUM_ADMIN_EMAIL)
        csrf_token = self.get_new_csrf_token()
        skill_services.delete_skill(self.admin_id, self.skill_id_2)

        with self.swap(feconf, 'CAN_SEND_EMAILS', True):
            messages = self._get_sent_email_messages(
                feconf.ADMIN_EMAIL_ADDRESS)
            self.assertEqual(len(messages), 0)
            json_response = self.put_json(
                '%s/%s' % (feconf.TOPIC_EDITOR_DATA_URL_PREFIX, self.topic_id),
                change_cmd,
                csrf_token=csrf_token)
            self.assertEqual(self.topic_id, json_response['topic_dict']['id'])
            self.assertEqual('A new name', json_response['topic_dict']['name'])
            self.assertEqual(2, len(json_response['topic_dict']['subtopics']))
            self.assertEqual(
                'Skill Description',
                json_response['skill_id_to_description_dict'][self.skill_id])

            messages = self._get_sent_email_messages(
                feconf.ADMIN_EMAIL_ADDRESS)
            expected_email_html_body = ('The deleted skills: %s are still'
                                        ' present in topic with id %s' %
                                        (self.skill_id_2, self.topic_id))
            self.assertEqual(len(messages), 1)
            self.assertIn(expected_email_html_body, messages[0].html)

        # Test if the corresponding subtopic pages were created.
        json_response = self.get_json(
            '%s/%s/%s' %
            (feconf.SUBTOPIC_PAGE_EDITOR_DATA_URL_PREFIX, self.topic_id, 1))
        self.assertEqual(
            {
                'subtitled_html': {
                    'html': '<p>New Data</p>',
                    'content_id': 'content'
                },
                'recorded_voiceovers': {
                    'voiceovers_mapping': {
                        'content': {}
                    }
                },
                'written_translations': {
                    'translations_mapping': {
                        'content': {}
                    }
                }
            }, json_response['subtopic_page']['page_contents'])
        json_response = self.get_json(
            '%s/%s/%s' %
            (feconf.SUBTOPIC_PAGE_EDITOR_DATA_URL_PREFIX, self.topic_id, 2))
        self.assertEqual(
            {
                'subtitled_html': {
                    'html': '<p>New Value</p>',
                    'content_id': 'content'
                },
                'recorded_voiceovers': {
                    'voiceovers_mapping': {
                        'content': {
                            'en': {
                                'file_size_bytes': 100,
                                'filename': 'test.mp3',
                                'needs_update': False,
                                'duration_secs': 0.34342
                            }
                        }
                    }
                },
                'written_translations': {
                    'translations_mapping': {
                        'content': {}
                    }
                }
            }, json_response['subtopic_page']['page_contents'])
        self.logout()

        # Test that any topic manager cannot edit the topic.
        self.login(self.TOPIC_MANAGER_EMAIL)
        self.put_json('%s/%s' %
                      (feconf.TOPIC_EDITOR_DATA_URL_PREFIX, self.topic_id),
                      change_cmd,
                      csrf_token=csrf_token,
                      expected_status_int=401)
        self.logout()

        # Check that non-admins and non-topic managers cannot edit a topic.
        self.put_json('%s/%s' %
                      (feconf.TOPIC_EDITOR_DATA_URL_PREFIX, self.topic_id),
                      change_cmd,
                      csrf_token=csrf_token,
                      expected_status_int=401)

        # Check that topic can not be edited when version is None.
        self.login(self.CURRICULUM_ADMIN_EMAIL)

        json_response = self.put_json(
            '%s/%s' % (feconf.TOPIC_EDITOR_DATA_URL_PREFIX, self.topic_id),
            {'version': None},
            csrf_token=csrf_token,
            expected_status_int=400)
        self.assertEqual(json_response['error'],
                         'Invalid POST request: a version must be specified.')

        self.logout()

        # Check topic can not be edited when payload version differs from
        # topic version.
        self.login(self.CURRICULUM_ADMIN_EMAIL)

        topic_id_1 = topic_fetchers.get_new_topic_id()
        self.save_new_topic(topic_id_1,
                            self.admin_id,
                            name='Name 1',
                            abbreviated_name='topic-three',
                            url_fragment='topic-three',
                            description='Description 1',
                            canonical_story_ids=[],
                            additional_story_ids=[],
                            uncategorized_skill_ids=[self.skill_id],
                            subtopics=[],
                            next_subtopic_id=1)

        json_response = self.put_json(
            '%s/%s' % (feconf.TOPIC_EDITOR_DATA_URL_PREFIX, topic_id_1),
            {'version': '3'},
            csrf_token=csrf_token,
            expected_status_int=400)

        self.assertEqual(
            json_response['error'],
            'Trying to update version 1 of topic from version 3, '
            'which is too old. Please reload the page and try again.')

        self.logout()
Esempio n. 20
0
    def test_get_with_user_logged_in(self):
        skill_services.delete_skill(self.admin_id, self.skill_id_1)
        self.login(self.NEW_USER_EMAIL)
        with self.swap(feconf, 'CAN_SEND_EMAILS', True):
            messages = self._get_sent_email_messages(
                feconf.ADMIN_EMAIL_ADDRESS)
            self.assertEqual(len(messages), 0)
            json_response = self.get_json(
                '%s/staging/%s' % (feconf.TOPIC_DATA_HANDLER, 'public'))
            messages = self._get_sent_email_messages(
                feconf.ADMIN_EMAIL_ADDRESS)
            expected_email_html_body = (
                'The deleted skills: %s are still'
                ' present in topic with id %s' % (
                    self.skill_id_1, self.topic_id))
            self.assertEqual(len(messages), 1)
            self.assertIn(expected_email_html_body, messages[0].html)
            expected_dict = {
                'topic_name': 'public_topic_name',
                'topic_id': self.topic_id,
                'canonical_story_dicts': [{
                    'id': self.story_1.id,
                    'title': self.story_1.title,
                    'description': self.story_1.description,
                    'node_titles': self.story_1.node_titles,
                    'thumbnail_filename': None,
                    'thumbnail_bg_color': None,
                    'story_is_published': True,
                    'completed_node_titles': [],
                    'url_fragment': 'story-frag-one',
                    'all_node_dicts': []
                }],
                'additional_story_dicts': [{
                    'id': self.story_2.id,
                    'title': self.story_2.title,
                    'description': self.story_2.description,
                    'node_titles': self.story_2.node_titles,
                    'thumbnail_filename': None,
                    'thumbnail_bg_color': None,
                    'story_is_published': True,
                    'completed_node_titles': [],
                    'url_fragment': 'story-frag-two',
                    'all_node_dicts': []
                }],
                'uncategorized_skill_ids': [self.skill_id_1],
                'subtopics': [{
                    u'thumbnail_filename': u'image.svg',
                    u'thumbnail_bg_color': u'#FFFFFF',
                    u'thumbnail_size_in_bytes': 21131,
                    u'skill_ids': [self.skill_id_2],
                    u'id': 1,
                    u'title': u'subtopic_name',
                    u'url_fragment': u'subtopic-name'}],
                'degrees_of_mastery': {
                    self.skill_id_1: 0.3,
                    self.skill_id_2: 0.5
                },
                'skill_descriptions': {
                    self.skill_id_1: None,
                    self.skill_id_2: 'Skill Description 2'
                },
                'practice_tab_is_displayed': False
            }
            self.assertDictContainsSubset(expected_dict, json_response)

        self.logout()