def test_migration_job_skips_deleted_collection(self):
        """Tests that the collection migration job skips deleted collection
        and does not attempt to migrate.
        """
        collection = collection_domain.Collection.create_default_collection(
            self.COLLECTION_ID, 'A title', 'A Category', 'An Objective')
        collection_services.save_new_collection(self.albert_id, collection)

        # Note: This creates a summary based on the upgraded model (which is
        # fine). A summary is needed to delete the collection.
        collection_services.create_collection_summary(
            self.COLLECTION_ID, None)

        # Delete the exploration before migration occurs.
        collection_services.delete_collection(
            self.albert_id, self.COLLECTION_ID)

        # Ensure the exploration is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            collection_services.get_collection_by_id(self.COLLECTION_ID)

        # Start migration job on sample collection.
        job_id = (
            collection_jobs_one_off.CollectionMigrationJob.create_new())
        collection_jobs_one_off.CollectionMigrationJob.enqueue(job_id)

        # This running without errors indicates the deleted collection is
        # being ignored.
        self.process_and_flush_pending_tasks()

        # Ensure the exploration is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            collection_services.get_collection_by_id(self.COLLECTION_ID)
Beispiel #2
0
def _get_exploration_player_data(
        exploration_id, version, collection_id, can_edit):
    try:
        exploration = exp_services.get_exploration_by_id(
            exploration_id, version=version)
    except Exception:
        raise Exception

    collection_title = None
    if collection_id:
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
            collection_title = collection.title
        except Exception:
            raise Exception

    version = exploration.version

    # TODO(sll): Cache these computations.
    gadget_types = exploration.get_gadget_types()
    interaction_ids = exploration.get_interaction_ids()
    dependency_ids = (
        interaction_registry.Registry.get_deduplicated_dependency_ids(
            interaction_ids))
    dependencies_html, additional_angular_modules = (
        dependency_registry.Registry.get_deps_html_and_angular_modules(
            dependency_ids))

    gadget_templates = (
        gadget_registry.Registry.get_gadget_html(gadget_types))
    interaction_templates = (
        rte_component_registry.Registry.get_html_for_all_components() +
        interaction_registry.Registry.get_interaction_html(
            interaction_ids))

    return {
        'GADGET_SPECS': gadget_registry.Registry.get_all_specs(),
        'INTERACTION_SPECS': interaction_registry.Registry.get_all_specs(),
        'DEFAULT_TWITTER_SHARE_MESSAGE_PLAYER': (
            DEFAULT_TWITTER_SHARE_MESSAGE_PLAYER.value),
        'additional_angular_modules': additional_angular_modules,
        'can_edit': can_edit,
        'dependencies_html': jinja2.utils.Markup(
            dependencies_html),
        'exploration_title': exploration.title,
        'exploration_version': version,
        'collection_id': collection_id,
        'collection_title': collection_title,
        'gadget_templates': jinja2.utils.Markup(gadget_templates),
        'interaction_templates': jinja2.utils.Markup(
            interaction_templates),
        'is_private': rights_manager.is_exploration_private(
            exploration_id),
        # Note that this overwrites the value in base.py.
        'meta_name': exploration.title,
        # Note that this overwrites the value in base.py.
        'meta_description': utils.capitalize_string(exploration.objective),
        'nav_mode': feconf.NAV_MODE_EXPLORE,
    }
Beispiel #3
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)

        if (collection is None or
                not rights_manager.Actor(self.user_id).can_view(
                    rights_manager.ACTIVITY_TYPE_COLLECTION, collection_id)):
            self.redirect('/')
            return

        can_edit = (
            bool(self.user_id) and
            self.username not in config_domain.BANNED_USERNAMES.value and
            rights_manager.Actor(self.user_id).can_edit(
                rights_manager.ACTIVITY_TYPE_COLLECTION, collection_id))

        self.values.update({
            'is_public': rights_manager.is_collection_public(collection_id),
            'can_edit': can_edit,
            'collection_id': collection.id,
            'title': collection.title
        })

        self.render_template('collection_editor/collection_editor.html')
    def test_migration_job_does_not_convert_up_to_date_collection(self):
        """Tests that the collection migration job does not convert an
        collection that is already the latest collection content schema version.
        """
        # Create a new, collection that should not be affected by the
        # job.
        collection = collection_domain.Collection.create_default_collection(
            self.COLLECTION_ID, 'A title', 'A Category', 'An Objective')
        collection_services.save_new_collection(self.albert_id, collection)
        self.assertEqual(
            collection.schema_version,
            feconf.CURRENT_COLLECTION_SCHEMA_VERSION)
        yaml_before_migration = collection.to_yaml()

        # Start migration job.
        job_id = (
            collection_jobs_one_off.CollectionMigrationJob.create_new())
        collection_jobs_one_off.CollectionMigrationJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Verify the collection is exactly the same after migration.
        updated_collection = (
            collection_services.get_collection_by_id(self.COLLECTION_ID))
        self.assertEqual(
            updated_collection.schema_version,
            feconf.CURRENT_COLLECTION_SCHEMA_VERSION)
        after_converted_yaml = updated_collection.to_yaml()
        self.assertEqual(after_converted_yaml, yaml_before_migration)
    def map(item):
        if item.deleted:
            yield (CollectionMigrationJob._DELETED_KEY,
                   'Encountered deleted collection.')
            return

        # Note: the read will bring the collection up to the newest version.
        collection = collection_services.get_collection_by_id(item.id)
        try:
            collection.validate(strict=False)
        except Exception as e:
            logging.error(
                'Collection %s failed validation: %s' % (item.id, e))
            yield (CollectionMigrationJob._ERROR_KEY,
                   'Collection %s failed validation: %s' % (item.id, e))
            return

        # Write the new collection into the datastore if it's different from
        # the old version.
        if item.schema_version <= feconf.CURRENT_COLLECTION_SCHEMA_VERSION:
            commit_cmds = [{
                'cmd': collection_domain.CMD_MIGRATE_SCHEMA_TO_LATEST_VERSION,
                'from_version': item.schema_version,
                'to_version': str(
                    feconf.CURRENT_COLLECTION_SCHEMA_VERSION)
            }]
            collection_services.update_collection(
                feconf.MIGRATION_BOT_USERNAME, item.id, commit_cmds,
                'Update collection schema version to %d.' % (
                    feconf.CURRENT_COLLECTION_SCHEMA_VERSION))
            yield (CollectionMigrationJob._MIGRATED_KEY,
                   'Collection successfully migrated.')
Beispiel #6
0
    def get(self, collection_id):
        """Populates the data on the individual collection page."""
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
        except Exception as e:
            raise self.PageNotFoundException(e)

        exp_ids = collection.exploration_ids

        exp_summaries = (
            exp_services.get_exploration_summaries_matching_ids(exp_ids))

        exp_titles_dict = {}
        for (ind, exp_id) in enumerate(exp_ids):
            exp_summary = exp_summaries[ind]
            exp_titles_dict[exp_id] = exp_summary.title if exp_summary else ''

        # TODO(bhenning): Users should not be recommended explorations they
        # have completed outside the context of a collection.
        next_exploration_ids = None
        completed_exploration_ids = None
        if self.user_id:
            completed_exploration_ids = (
                collection_services.get_completed_exploration_ids(
                    self.user_id, collection_id))
            next_exploration_ids = collection.get_next_exploration_ids(
                completed_exploration_ids)
        else:
            # If the user is not logged in or they have not completed any of
            # the explorations yet within the context of this collection,
            # recommend the initial explorations.
            next_exploration_ids = collection.init_exploration_ids
            completed_exploration_ids = []

        collection_dict = collection.to_dict()
        collection_dict['next_exploration_ids'] = next_exploration_ids
        collection_dict['completed_exploration_ids'] = (
            completed_exploration_ids)

        # Insert an 'exploration' dict into each collection node, where the
        # dict includes meta information about the exploration (ID and title).
        for collection_node in collection_dict['nodes']:
            collection_node['exploration'] = {
                'id': collection_node['exploration_id'],
                'title': exp_titles_dict[collection_node['exploration_id']]
            }

        self.values.update({
            'can_edit': (
                self.user_id and rights_manager.Actor(self.user_id).can_edit(
                    rights_manager.ACTIVITY_TYPE_COLLECTION, collection_id)),
            'collection': collection_dict,
            'info_card_image_url': utils.get_info_card_url_for_category(
                collection.category),
            'is_logged_in': bool(self.user_id),
            'session_id': utils.generate_new_session_id(),
        })

        self.render_json(self.values)
Beispiel #7
0
    def get(self, collection_id):
        """Handles GET requests."""
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
        except Exception as e:
            raise self.PageNotFoundException(e)
        whitelisted_usernames = (
            config_domain.WHITELISTED_COLLECTION_EDITOR_USERNAMES.value)
        self.values.update({
            'nav_mode': feconf.NAV_MODE_COLLECTION,
            'can_edit': (
                bool(self.username) and
                self.username in whitelisted_usernames and
                self.username not in config_domain.BANNED_USERNAMES.value and
                rights_manager.Actor(self.user_id).can_edit(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id)
            ),
            'is_logged_in': bool(self.user_id),
            'collection_id': collection_id,
            'collection_title': collection.title,
            'collection_skills': collection.skills,
            'is_private': rights_manager.is_collection_private(collection_id),
            'meta_name': collection.title,
            'meta_description': utils.capitalize_string(collection.objective)
        })

        self.render_template('collection_player/collection_player.html')
Beispiel #8
0
    def put(self, collection_id):
        """Updates properties of the given collection."""

        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        self._require_valid_version(version, collection.version)

        commit_message = self.payload.get('commit_message')
        change_list = self.payload.get('change_list')

        try:
            collection_services.update_collection(
                self.user_id, collection_id, change_list, commit_message)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        collection_dict = (
            summary_services.get_learner_collection_dict_by_id(
                collection_id, self.user_id, allow_invalid_explorations=True))

        # Send the updated collection back to the frontend.
        self.values.update({
            'collection': collection_dict
        })

        self.render_json(self.values)
Beispiel #9
0
    def put(self, collection_id):
        """Updates the editing rights for the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        _require_valid_version(version, collection.version)

        # TODO(bhenning): Implement other rights changes here.
        is_public = self.payload.get('is_public')

        if is_public is not None:
            if is_public:
                try:
                    collection.validate(strict=True)
                    collection_services.validate_exps_in_collection_are_public(
                        collection)
                except utils.ValidationError as e:
                    raise self.InvalidInputException(e)

                collection_services.publish_collection_and_update_user_profiles(
                    self.user_id, collection_id)
                collection_services.index_collections_given_ids([
                    collection_id])
            elif not self.is_admin:
                raise self.InvalidInputException(
                    'Cannot unpublish a collection.')

        self.render_json({
            'rights': rights_manager.get_collection_rights(
                collection_id).to_dict()
        })
 def setUp(self):
     super(CollectionDomainUnitTests, self).setUp()
     self.save_new_valid_collection(
         self.COLLECTION_ID, '*****@*****.**', title='Title',
         category='Category', objective='Objective',
         exploration_id=self.EXPLORATION_ID)
     self.collection = collection_services.get_collection_by_id(
         self.COLLECTION_ID)
 def setUp(self):
     super(CollectionDomainUnitTests, self).setUp()
     self.save_new_valid_collection(self.COLLECTION_ID,
                                    '*****@*****.**',
                                    title='Title',
                                    category='Category',
                                    objective='Objective',
                                    exploration_id=self.EXPLORATION_ID)
     self.collection = collection_services.get_collection_by_id(
         self.COLLECTION_ID)
Beispiel #12
0
    def test_migration_job_skips_deleted_collection(self):
        """Tests that the collection migration job skips deleted collection
        and does not attempt to migrate.
        """
        collection = collection_domain.Collection.create_default_collection(
            self.COLLECTION_ID,
            title='A title',
            category='A Category',
            objective='An Objective')
        collection_services.save_new_collection(self.albert_id, collection)

        # Note: This creates a summary based on the upgraded model (which is
        # fine). A summary is needed to delete the collection.
        collection_services.regenerate_collection_and_contributors_summaries(
            self.COLLECTION_ID)

        # Delete the exploration before migration occurs.
        collection_services.delete_collection(self.albert_id,
                                              self.COLLECTION_ID)

        # Ensure the exploration is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            collection_services.get_collection_by_id(self.COLLECTION_ID)

        # Start migration job on sample collection.
        job_id = (
            collection_jobs_one_off.CollectionMigrationOneOffJob.create_new())
        collection_jobs_one_off.CollectionMigrationOneOffJob.enqueue(job_id)

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

        # Ensure the exploration is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            collection_services.get_collection_by_id(self.COLLECTION_ID)

        output = (collection_jobs_one_off.CollectionMigrationOneOffJob.
                  get_output(job_id))
        expected = [[
            u'collection_deleted', [u'Encountered 1 deleted collections.']
        ]]
        self.assertEqual(expected, [ast.literal_eval(x) for x in output])
Beispiel #13
0
 def setUp(self):
     super(CollectionDomainUnitTests, self).setUp()
     self.save_new_valid_collection(
         self.COLLECTION_ID,
         "*****@*****.**",
         title="Title",
         category="Category",
         objective="Objective",
         exploration_id=self.EXPLORATION_ID,
     )
     self.collection = collection_services.get_collection_by_id(self.COLLECTION_ID)
    def test_can_not_unpublish_collection_with_invalid_payload_version(self):
        self.set_collection_editors([self.OWNER_USERNAME])

        # Login as owner and publish a collection with a public exploration.
        self.login(self.OWNER_EMAIL)
        collection_id = collection_services.get_new_collection_id()
        exploration_id = exp_services.get_new_exploration_id()
        self.save_new_valid_exploration(exploration_id, self.owner_id)
        self.save_new_valid_collection(collection_id,
                                       self.owner_id,
                                       exploration_id=exploration_id)
        rights_manager.publish_exploration(self.owner, exploration_id)
        collection = collection_services.get_collection_by_id(collection_id)
        response = self.get_html_response(
            '%s/%s' % (feconf.COLLECTION_URL_PREFIX, self.COLLECTION_ID))
        csrf_token = self.get_csrf_token_from_response(response)
        response_dict = self.put_json('/collection_editor_handler/publish/%s' %
                                      collection_id,
                                      {'version': collection.version},
                                      csrf_token=csrf_token)
        self.assertFalse(response_dict['is_private'])
        self.logout()

        # Login as admin and try to unpublish the collection.
        self.login(self.ADMIN_EMAIL)
        response = self.get_html_response(
            '%s/%s' % (feconf.COLLECTION_URL_PREFIX, self.COLLECTION_ID))
        csrf_token = self.get_csrf_token_from_response(response)

        # Raises error as version is None.
        response_dict = self.put_json(
            '/collection_editor_handler/unpublish/%s' % collection_id,
            {'version': None},
            csrf_token=csrf_token,
            expected_status_int=400)

        self.assertEqual(response_dict['error'],
                         'Invalid POST request: a version must be specified.')

        # Raises error as version from payload does not match the collection
        # version.
        response_dict = self.put_json(
            '/collection_editor_handler/unpublish/%s' % collection_id,
            {'version': 2},
            csrf_token=csrf_token,
            expected_status_int=400)

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

        self.logout()
Beispiel #15
0
def _get_exploration_player_data(
        exploration_id, version, collection_id, can_edit):
    """Returns a dict of exploration player data.

    Args:
        exploration_id: str. The ID of the exploration.
        version: int or None. The version of the exploration.
        collection_id: str. ID of the collection.
        can_edit: bool. Whether the given user can edit this activity.

    Returns:
        dict. A dict of exploration player data.
        The keys and values of the dict are as follows:
        - 'can_edit': bool. Whether the given user can edit this activity.
        - 'exploration_title': str. Title of exploration.
        - 'exploration_version': int. The version of the exploration.
        - 'collection_id': str. ID of the collection.
        - 'collection_title': str. Title of collection.
            required by the given exploration ID.
        - 'is_private': bool. Whether the exploration is private or not.
        - 'meta_name': str. Title of exploration.
        - 'meta_description': str. Objective of exploration.
    """
    try:
        exploration = exp_fetchers.get_exploration_by_id(
            exploration_id, version=version)
    except Exception:
        raise Exception

    collection_title = None
    if collection_id:
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
            collection_title = collection.title
        except Exception:
            raise Exception

    version = exploration.version

    return {
        'can_edit': can_edit,
        'exploration_title': exploration.title,
        'exploration_version': version,
        'collection_id': collection_id,
        'collection_title': collection_title,
        'is_private': rights_manager.is_exploration_private(
            exploration_id),
        # Note that this overwrites the value in base.py.
        'meta_name': exploration.title,
        # Note that this overwrites the value in base.py.
        'meta_description': utils.capitalize_string(exploration.objective),
    }
Beispiel #16
0
    def test_collection_editor(self, collection_id, **kwargs):
        """Gets the user and collection id if the user can edit it.

        Args:
            self: the handler instance
            collection_id: the collection id
            **kwargs: any other arguments passed to the handler

        Returns:
            The relevant handler, if the user is authorized to edit this
            collection.

        Raises:
            self.PageNotFoundException: if no such collection exists.
            self.UnauthorizedUserException: if the user exists but does not
                have the right credentials.
        """
        if not self.user_id:
            self.redirect(current_user_services.create_login_url(
                self.request.uri))
            return

        if (self.username in config_domain.BANNED_USERNAMES.value
                or self.username not in
                config_domain.WHITELISTED_COLLECTION_EDITOR_USERNAMES.value):
            raise self.UnauthorizedUserException(
                'You do not have the credentials to access this page.')

        try:
            collection_services.get_collection_by_id(collection_id)
        except:
            raise self.PageNotFoundException

        if not rights_manager.Actor(self.user_id).can_edit(
                feconf.ACTIVITY_TYPE_COLLECTION, collection_id):
            raise self.UnauthorizedUserException(
                'You do not have the credentials to edit this collection.',
                self.user_id)

        return handler(self, collection_id, **kwargs)
Beispiel #17
0
    def test_collection_editor(self, collection_id, **kwargs):
        """Gets the user and collection id if the user can edit it.

        Args:
            self: the handler instance
            collection_id: the collection id
            **kwargs: any other arguments passed to the handler

        Returns:
            The relevant handler, if the user is authorized to edit this
            collection.

        Raises:
            self.PageNotFoundException: if no such collection exists.
            self.UnauthorizedUserException: if the user exists but does not
                have the right credentials.
        """
        if not self.user_id:
            self.redirect(
                current_user_services.create_login_url(self.request.uri))
            return

        if (self.username in config_domain.BANNED_USERNAMES.value
                or self.username not in
                config_domain.WHITELISTED_COLLECTION_EDITOR_USERNAMES.value):
            raise self.UnauthorizedUserException(
                'You do not have the credentials to access this page.')

        try:
            collection_services.get_collection_by_id(collection_id)
        except:
            raise self.PageNotFoundException

        if not rights_manager.Actor(self.user_id).can_edit(
                rights_manager.ACTIVITY_TYPE_COLLECTION, collection_id):
            raise self.UnauthorizedUserException(
                'You do not have the credentials to edit this collection.',
                self.user_id)

        return handler(self, collection_id, **kwargs)
Beispiel #18
0
    def test_add_question_id_to_skill(self):
        """Test to verify add_skill."""
        collection_id = 'col1'
        exp_id = '0_exploration_id'
        owner_id = self.get_user_id_from_email(self.OWNER_EMAIL)

        # Create a new collection and exploration.
        self.save_new_valid_collection(collection_id,
                                       owner_id,
                                       exploration_id=exp_id)

        # Add a skill.
        collection_services.update_collection(
            owner_id, collection_id, [{
                'cmd': collection_domain.CMD_ADD_COLLECTION_SKILL,
                'name': 'skill0'
            }], 'Add a new skill')

        state = exp_domain.State.create_default_state('ABC')
        question_data = state.to_dict()

        question_dict = {
            'question_id': 'col1.random',
            'title': 'abc',
            'question_data': question_data,
            'question_data_schema_version': 1,
            'collection_id': 'col1',
            'language_code': 'en'
        }

        collection = collection_services.get_collection_by_id(collection_id)
        skill_id = collection.get_skill_id_from_skill_name('skill0')
        question = question_domain.Question.from_dict(question_dict)
        question_services.add_question_id_to_skill(question.question_id,
                                                   collection_id, skill_id,
                                                   owner_id)
        collection = collection_services.get_collection_by_id(collection_id)
        self.assertIn(question.question_id,
                      collection.skills[skill_id].question_ids)
Beispiel #19
0
    def test_get_question_batch(self):
        coll_id_0 = '0_collection_id'
        exp_id_0 = '0_exploration_id'
        self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL)

        # Create a new collection and exploration.
        self.save_new_valid_collection(coll_id_0,
                                       self.owner_id,
                                       exploration_id=exp_id_0)

        # Add a skill.
        collection_services.update_collection(
            self.owner_id, coll_id_0, [{
                'cmd': collection_domain.CMD_ADD_COLLECTION_SKILL,
                'name': 'skill0'
            }], 'Add a new skill')
        collection = collection_services.get_collection_by_id(coll_id_0)
        skill_id = collection.get_skill_id_from_skill_name('skill0')
        collection_node = collection.get_node(exp_id_0)
        collection_node.update_acquired_skill_ids([skill_id])
        # Update the acquired skill IDs for the exploration.
        collection_services.update_collection(
            self.owner_id,
            coll_id_0,
            [{
                'cmd':
                collection_domain.CMD_EDIT_COLLECTION_NODE_PROPERTY,
                'property_name':
                (collection_domain.COLLECTION_NODE_PROPERTY_ACQUIRED_SKILL_IDS
                 ),  # pylint: disable=line-too-long
                'exploration_id':
                exp_id_0,
                'new_value': [skill_id]
            }],
            'Update skill')

        question = question_domain.Question(
            'dummy', 'A Question',
            exp_domain.State.create_default_state('ABC').to_dict(), 1,
            coll_id_0, 'en')

        question_id = question_services.add_question(self.owner_id, question)
        question = question_services.get_question_by_id(question_id)
        question_services.add_question_id_to_skill(question.question_id,
                                                   coll_id_0, skill_id,
                                                   self.owner_id)
        collection_services.record_played_exploration_in_collection_context(
            self.owner_id, coll_id_0, exp_id_0)
        question_batch = question_services.get_questions_batch(
            coll_id_0, [skill_id], self.owner_id, 1)
        self.assertEqual(question_batch[0].title, question.title)
Beispiel #20
0
    def get(self, exploration_id):
        """Handles GET requests."""
        collection_id = self.request.get('collection_id')
        include_system_recommendations = self.request.get(
            'include_system_recommendations')
        try:
            author_recommended_exp_ids = json.loads(
                self.request.get('stringified_author_recommended_ids'))
        except Exception:
            raise self.PageNotFoundException

        auto_recommended_exp_ids = []
        if self.user_id and collection_id:
            next_exp_ids_in_collection = (
                collection_services.
                get_next_exploration_ids_to_complete_by_user(  # pylint: disable=line-too-long
                    self.user_id, collection_id))
            auto_recommended_exp_ids = list(
                set(next_exp_ids_in_collection) -
                set(author_recommended_exp_ids))
        else:
            next_exp_ids_in_collection = []
            if collection_id:
                collection = collection_services.get_collection_by_id(
                    collection_id)
                next_exp_ids_in_collection = (
                    collection.get_next_exploration_ids_in_sequence(
                        exploration_id))
            if next_exp_ids_in_collection:
                auto_recommended_exp_ids = list(
                    set(next_exp_ids_in_collection) -
                    set(author_recommended_exp_ids))
            elif include_system_recommendations:
                system_chosen_exp_ids = (
                    recommendations_services.get_exploration_recommendations(
                        exploration_id))
                filtered_exp_ids = list(
                    set(system_chosen_exp_ids) -
                    set(author_recommended_exp_ids))
                auto_recommended_exp_ids = random.sample(
                    filtered_exp_ids,
                    min(MAX_SYSTEM_RECOMMENDATIONS, len(filtered_exp_ids)))

        self.values.update({
            'summaries':
            (summary_services.get_displayable_exp_summary_dicts_matching_ids(
                author_recommended_exp_ids + auto_recommended_exp_ids)),
        })
        self.render_json(self.values)
Beispiel #21
0
    def get(self, exploration_id):
        """Handles GET requests."""
        collection_id = self.request.get('collection_id')
        include_system_recommendations = self.request.get(
            'include_system_recommendations')
        try:
            author_recommended_exp_ids = json.loads(self.request.get(
                'stringified_author_recommended_ids'))
        except Exception:
            raise self.PageNotFoundException

        auto_recommended_exp_ids = []
        if self.user_id and collection_id:
            next_exp_ids_in_collection = (
                collection_services.get_next_exploration_ids_to_complete_by_user( # pylint: disable=line-too-long
                    self.user_id, collection_id))
            auto_recommended_exp_ids = list(
                set(next_exp_ids_in_collection) -
                set(author_recommended_exp_ids))
        else:
            next_exp_ids_in_collection = []
            if collection_id:
                collection = collection_services.get_collection_by_id(
                    collection_id)
                next_exp_ids_in_collection = (
                    collection.get_next_exploration_ids_in_sequence(
                        exploration_id))
            if next_exp_ids_in_collection:
                auto_recommended_exp_ids = list(
                    set(next_exp_ids_in_collection) -
                    set(author_recommended_exp_ids))
            elif include_system_recommendations:
                system_chosen_exp_ids = (
                    recommendations_services.get_exploration_recommendations(
                        exploration_id))
                filtered_exp_ids = list(
                    set(system_chosen_exp_ids) -
                    set(author_recommended_exp_ids))
                auto_recommended_exp_ids = random.sample(
                    filtered_exp_ids,
                    min(MAX_SYSTEM_RECOMMENDATIONS, len(filtered_exp_ids)))

        self.values.update({
            'summaries': (
                summary_services.get_displayable_exp_summary_dicts_matching_ids(
                    author_recommended_exp_ids + auto_recommended_exp_ids)),
        })
        self.render_json(self.values)
Beispiel #22
0
    def get(self, collection_id):
        """Gets the editing rights for the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)

        self.values.update({
            'can_edit': True,
            'can_unpublish': rights_manager.Actor(
                self.user_id).can_unpublish(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id': collection.id,
            'is_private': rights_manager.is_collection_private(collection_id),
            'owner_names': rights_manager.get_collection_owner_names(
                collection_id)
        })

        self.render_json(self.values)
Beispiel #23
0
 def test_can_create_collections(self):
     self.set_admins([self.OWNER_USERNAME])
     self.login(self.OWNER_EMAIL)
     csrf_token = self.get_new_csrf_token()
     collection_id = self.post_json(
         feconf.NEW_COLLECTION_URL, {}, csrf_token=csrf_token)[
             creator_dashboard.COLLECTION_ID_KEY]
     collection = collection_services.get_collection_by_id(collection_id)
     self.assertEqual(collection.id, collection_id)
     self.assertEqual(collection.title, feconf.DEFAULT_COLLECTION_TITLE)
     self.assertEqual(
         collection.objective, feconf.DEFAULT_COLLECTION_CATEGORY)
     self.assertEqual(
         collection.category, feconf.DEFAULT_COLLECTION_OBJECTIVE)
     self.assertEqual(
         collection.language_code, constants.DEFAULT_LANGUAGE_CODE)
     self.logout()
Beispiel #24
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)

        self.values.update({
            'collection_id': collection.id,
            'nav_mode': feconf.NAV_MODE_CREATE,
            'SHOW_COLLECTION_NAVIGATION_TAB_HISTORY': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_HISTORY),
            'SHOW_COLLECTION_NAVIGATION_TAB_STATS': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_STATS),
            'TAG_REGEX': feconf.TAG_REGEX,
        })

        self.render_template('pages/collection_editor/collection_editor.html')
Beispiel #25
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)

        self.values.update({
            'collection_id': collection.id,
            'nav_mode': feconf.NAV_MODE_CREATE,
            'SHOW_COLLECTION_NAVIGATION_TAB_HISTORY': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_HISTORY),
            'SHOW_COLLECTION_NAVIGATION_TAB_STATS': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_STATS),
            'TAG_REGEX': feconf.TAG_REGEX,
        })

        self.render_template('pages/collection_editor/collection_editor.html')
Beispiel #26
0
    def put(self, collection_id):
        """Updates the editing rights for the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        _require_valid_version(version, collection.version)

        # TODO(bhenning): Implement other rights changes here.
        is_public = self.payload.get('is_public')

        if is_public is not None:
            if is_public:
                try:
                    collection.validate(strict=True)
                    collection_services.validate_exps_in_collection_are_public(
                        collection)
                except utils.ValidationError as e:
                    raise self.InvalidInputException(e)

                collection_services.publish_collection_and_update_user_profiles(
                    self.user_id, collection_id)
                collection_services.index_collections_given_ids(
                    [collection_id])
            elif rights_manager.Actor(self.user_id).can_unpublish(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id):
                rights_manager.unpublish_collection(self.user_id,
                                                    collection_id)
                collection_services.delete_documents_from_search_index(
                    [collection_id])
            else:
                raise self.InvalidInputException(
                    'Cannot unpublish a collection.')

        self.values.update({
            'can_edit':
            True,
            'can_unpublish':
            rights_manager.Actor(self.user_id).can_unpublish(
                feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id':
            collection.id,
            'is_private':
            rights_manager.is_collection_private(collection_id),
            'owner_names':
            rights_manager.get_collection_owner_names(collection_id)
        })
Beispiel #27
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)

        self.values.update({
            'collection_id': collection.id,
            'SHOW_COLLECTION_NAVIGATION_TAB_HISTORY': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_HISTORY),
            'SHOW_COLLECTION_NAVIGATION_TAB_STATS': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_STATS),
            'TAG_REGEX': feconf.TAG_REGEX,
            'title': collection.title,
            'INTERACTION_SPECS': interaction_registry.Registry.get_all_specs(),
        })

        self.render_template('dist/collection_editor.html')
Beispiel #28
0
    def get(self, collection_id):
        """Gets the editing rights for the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)

        self.values.update({
            'can_edit':
            True,
            'can_unpublish':
            rights_manager.Actor(self.user_id).can_unpublish(
                feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id':
            collection.id,
            'is_private':
            rights_manager.is_collection_private(collection_id),
            'owner_names':
            rights_manager.get_collection_owner_names(collection_id)
        })

        self.render_json(self.values)
Beispiel #29
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(collection_id,
                                                              strict=False)

        if (collection is None
                or not rights_manager.Actor(self.user_id).can_view(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id)):
            self.redirect('/')
            return

        can_edit = (bool(self.user_id) and self.username
                    not in config_domain.BANNED_USERNAMES.value
                    and rights_manager.Actor(self.user_id).can_edit(
                        feconf.ACTIVITY_TYPE_COLLECTION, collection_id))

        self.values.update({
            'can_edit':
            can_edit,
            'can_unpublish':
            rights_manager.Actor(self.user_id).can_unpublish(
                feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id':
            collection.id,
            'is_private':
            rights_manager.is_collection_private(collection_id),
            'nav_mode':
            feconf.NAV_MODE_CREATE,
            'title':
            collection.title,
            'SHOW_COLLECTION_NAVIGATION_TAB_HISTORY':
            (feconf.SHOW_COLLECTION_NAVIGATION_TAB_HISTORY),
            'SHOW_COLLECTION_NAVIGATION_TAB_FEEDBACK':
            (feconf.SHOW_COLLECTION_NAVIGATION_TAB_FEEDBACK),
            'SHOW_COLLECTION_NAVIGATION_TAB_STATS':
            (feconf.SHOW_COLLECTION_NAVIGATION_TAB_STATS),
            'TAG_REGEX':
            feconf.TAG_REGEX,
        })

        self.render_template('collection_editor/collection_editor.html')
Beispiel #30
0
    def put(self, collection_id):
        """Updates properties of the given collection."""

        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        _require_valid_version(version, collection.version)

        commit_message = self.payload.get('commit_message')
        change_list = self.payload.get('change_list')

        collection_services.update_collection(self.user_id, collection_id,
                                              change_list, commit_message)

        collection_dict = (summary_services.get_learner_collection_dict_by_id(
            collection_id, self.user, allow_invalid_explorations=True))

        # Send the updated collection back to the frontend.
        self.values.update({'collection': collection_dict})

        self.render_json(self.values)
Beispiel #31
0
    def put(self, collection_id):
        """Updates the editing rights for the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        _require_valid_version(version, collection.version)

        # TODO(bhenning): Implement other rights changes here.
        is_public = self.payload.get('is_public')

        if is_public is not None:
            if is_public:
                try:
                    collection.validate(strict=True)
                    collection_services.validate_exps_in_collection_are_public(
                        collection)
                except utils.ValidationError as e:
                    raise self.InvalidInputException(e)

                collection_services.publish_collection_and_update_user_profiles(
                    self.user_id, collection_id)
                collection_services.index_collections_given_ids([
                    collection_id])
            elif rights_manager.Actor(self.user_id).can_unpublish(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id):
                rights_manager.unpublish_collection(self.user_id, collection_id)
                collection_services.delete_documents_from_search_index([
                    collection_id])
            else:
                raise self.InvalidInputException(
                    'Cannot unpublish a collection.')

        self.values.update({
            'can_edit': True,
            'can_unpublish': rights_manager.Actor(
                self.user_id).can_unpublish(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id': collection.id,
            'is_private': rights_manager.is_collection_private(collection_id),
            'owner_names': rights_manager.get_collection_owner_names(
                collection_id)
        })
Beispiel #32
0
    def setUp(self):
        super(QuestionsHandlersTest, self).setUp()

        self.collection_id = 'coll_0'
        self.exp_id = 'exp_1'
        self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL)
        self.random_email = '*****@*****.**'
        self.signup(self.random_email, 'abc')
        self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME)
        self.new_user_id = self.get_user_id_from_email(self.NEW_USER_EMAIL)
        self.set_moderators([self.NEW_USER_USERNAME])
        # Create a new collection and exploration.
        self.save_new_valid_collection(
            self.collection_id, self.new_user_id, exploration_id=self.exp_id)

        # Add a skill.
        collection_services.update_collection(
            self.new_user_id, self.collection_id, [{
                'cmd': collection_domain.CMD_ADD_COLLECTION_SKILL,
                'name': 'test'
            }], 'Add a new skill')
        collection = collection_services.get_collection_by_id(
            self.collection_id)
        self.skill_id = collection.get_skill_id_from_skill_name('test')
        collection_node = collection.get_node(self.exp_id)
        collection_node.update_acquired_skill_ids([self.skill_id])

        # Update the acquired skill IDs for the exploration.
        collection_services.update_collection(
            self.new_user_id, self.collection_id, [{
                'cmd': collection_domain.CMD_EDIT_COLLECTION_NODE_PROPERTY,
                'property_name': (
                    collection_domain.COLLECTION_NODE_PROPERTY_ACQUIRED_SKILL_IDS), # pylint: disable=line-too-long
                'exploration_id': self.exp_id,
                'new_value': [self.skill_id]
            }], 'Update skill')

        self.question = question_domain.Question(
            'dummy', 'A Question',
            exp_domain.State.create_default_state('ABC').to_dict(), 1,
            self.collection_id, 'en')
Beispiel #33
0
    def get(self, collection_id):
        """Gets the editing rights for the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)
        collection_rights = rights_manager.get_collection_rights(collection_id,
                                                                 strict=False)

        self.values.update({
            'can_edit':
            True,
            'can_unpublish':
            rights_manager.check_can_unpublish_activity(
                self.user, collection_rights),
            'collection_id':
            collection.id,
            'is_private':
            rights_manager.is_collection_private(collection_id),
            'owner_names':
            rights_manager.get_collection_owner_names(collection_id)
        })

        self.render_json(self.values)
Beispiel #34
0
    def put(self, collection_id):
        """Updates properties of the given collection."""

        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        self._require_valid_version(version, collection.version)

        commit_message = self.payload.get('commit_message')
        change_list = self.payload.get('change_list')

        try:
            collection_services.update_collection(self.user_id, collection_id,
                                                  change_list, commit_message)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        # Retrieve the updated collection.
        collection_dict = (summary_services.get_learner_collection_dict_by_id(
            collection_id, self.user_id, allow_invalid_explorations=True))

        # Send the updated collection back to the frontend.
        self.values.update(collection_dict)
        self.render_json(self.values)
Beispiel #35
0
def get_question_summaries_for_collection(collection_id):
    """Gets a list of question summaries for a collection.

    Args:
        collection_id: str. ID of the collection.

    Returns:
        list(QuestionSummary). A list of Question Summary objects.
    """
    collection = collection_services.get_collection_by_id(collection_id)
    questions_to_skill_names = collections.defaultdict(list)
    for skill in collection.skills.values():
        for question_id in skill.question_ids:
            questions_to_skill_names[question_id].append(skill.name)
    questions = get_questions_by_ids(questions_to_skill_names.keys())

    question_summaries = []
    for question in questions:
        question_summaries.append(
            question_domain.QuestionSummary(
                question.question_id, question.title,
                (questions_to_skill_names[question.question_id])))
    return question_summaries
Beispiel #36
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)

        self.values.update({
            'can_edit': True,
            'can_unpublish': rights_manager.Actor(
                self.user_id).can_unpublish(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id': collection.id,
            'is_private': rights_manager.is_collection_private(collection_id),
            'nav_mode': feconf.NAV_MODE_CREATE,
            'title': collection.title,
            'SHOW_COLLECTION_NAVIGATION_TAB_HISTORY': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_HISTORY),
            'SHOW_COLLECTION_NAVIGATION_TAB_STATS': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_STATS),
            'TAG_REGEX': feconf.TAG_REGEX,
        })

        self.render_template('pages/collection_editor/collection_editor.html')
Beispiel #37
0
    def put(self, collection_id):
        """Unpublishes the given collection."""
        collection = collection_services.get_collection_by_id(collection_id)
        version = self.payload.get('version')
        _require_valid_version(version, collection.version)

        rights_manager.unpublish_collection(self.user, collection_id)
        search_services.delete_collections_from_search_index([
            collection_id])

        collection_rights = rights_manager.get_collection_rights(
            collection_id, strict=False)

        self.values.update({
            'can_edit': True,
            'can_unpublish': rights_manager.check_can_unpublish_activity(
                self.user, collection_rights),
            'collection_id': collection.id,
            'is_private': rights_manager.is_collection_private(collection_id),
            'owner_names': rights_manager.get_collection_owner_names(
                collection_id)
        })
        self.render_json(self.values)
Beispiel #38
0
    def get(self, collection_id):
        """Handles GET requests."""

        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)

        if (collection is None or
                not rights_manager.Actor(self.user_id).can_view(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id)):
            self.redirect('/')
            return

        can_edit = (
            bool(self.user_id) and
            self.username not in config_domain.BANNED_USERNAMES.value and
            rights_manager.Actor(self.user_id).can_edit(
                feconf.ACTIVITY_TYPE_COLLECTION, collection_id))

        self.values.update({
            'can_edit': can_edit,
            'can_unpublish': rights_manager.Actor(
                self.user_id).can_unpublish(
                    feconf.ACTIVITY_TYPE_COLLECTION, collection_id),
            'collection_id': collection.id,
            'is_private': rights_manager.is_collection_private(collection_id),
            'nav_mode': feconf.NAV_MODE_CREATE,
            'title': collection.title,
            'SHOW_COLLECTION_NAVIGATION_TAB_HISTORY': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_HISTORY),
            'SHOW_COLLECTION_NAVIGATION_TAB_FEEDBACK': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_FEEDBACK),
            'SHOW_COLLECTION_NAVIGATION_TAB_STATS': (
                feconf.SHOW_COLLECTION_NAVIGATION_TAB_STATS),
            'TAG_REGEX': feconf.TAG_REGEX,
        })

        self.render_template('collection_editor/collection_editor.html')
Beispiel #39
0
    def get(self, collection_id):
        """Handles GET requests."""
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
        except Exception as e:
            raise self.PageNotFoundException(e)

        self.values.update({
            'can_edit': (
                bool(self.username) and
                self.username not in config_domain.BANNED_USERNAMES.value and
                rights_manager.Actor(self.user_id).can_edit(
                    rights_manager.ACTIVITY_TYPE_COLLECTION, collection_id)
            ),
            'is_logged_in': bool(self.user_id),
            'collection_id': collection_id,
            'collection_title': collection.title,
            'is_private': rights_manager.is_collection_private(collection_id),
            'meta_name': collection.title,
            'meta_description': utils.capitalize_string(collection.objective)
        })

        self.render_template('collection_player/collection_player.html')
Beispiel #40
0
    def test_migration_job_does_not_convert_up_to_date_collection(self):
        """Tests that the collection migration job does not convert an
        collection that is already the latest collection content schema version.
        """
        # Create a new collection that should not be affected by the
        # job.
        collection = collection_domain.Collection.create_default_collection(
            self.COLLECTION_ID,
            title='A title',
            category='A Category',
            objective='An Objective')
        collection_services.save_new_collection(self.albert_id, collection)
        self.assertEqual(collection.schema_version,
                         feconf.CURRENT_COLLECTION_SCHEMA_VERSION)
        yaml_before_migration = collection.to_yaml()

        # Start migration job.
        job_id = (
            collection_jobs_one_off.CollectionMigrationOneOffJob.create_new())
        collection_jobs_one_off.CollectionMigrationOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Verify the collection is exactly the same after migration.
        updated_collection = (collection_services.get_collection_by_id(
            self.COLLECTION_ID))
        self.assertEqual(updated_collection.schema_version,
                         feconf.CURRENT_COLLECTION_SCHEMA_VERSION)
        after_converted_yaml = updated_collection.to_yaml()
        self.assertEqual(after_converted_yaml, yaml_before_migration)

        output = (collection_jobs_one_off.CollectionMigrationOneOffJob.
                  get_output(job_id))
        expected = [[
            u'collection_migrated', [u'1 collections successfully migrated.']
        ]]
        self.assertEqual(expected, [ast.literal_eval(x) for x in output])
Beispiel #41
0
def _does_exploration_exist(exploration_id, version, collection_id):
    """Returns if an exploration exists.

    Args:
        exploration_id: str. The ID of the exploration.
        version: int or None. The version of the exploration.
        collection_id: str. ID of the collection.

    Returns:
        bool. True if the exploration exists False otherwise.
    """
    exploration = exp_fetchers.get_exploration_by_id(
        exploration_id, strict=False, version=version)

    if exploration is None:
        return False

    if collection_id:
        collection = collection_services.get_collection_by_id(
            collection_id, strict=False)
        if collection is None:
            return False

    return True
Beispiel #42
0
def _get_exploration_player_data(
        exploration_id, version, collection_id, can_edit):
    """Returns a dict of exploration player data.

    Args:
        exploration_id: str. The ID of the exploration.
        version: int or None. The version of the exploration.
        collection_id: str. ID of the collection.
        can_edit: bool. Whether the given user can edit this activity.

    Returns:
        dict. A dict of exploration player data.
        The keys and values of the dict are as follows:
        - 'INTERACTION_SPECS': dict. A dict containing the full specs of each
            interaction. Contains interaction ID and a list of instances of
            all interactions.
        - 'additional_angular_modules': list. A de-duplicated list of strings,
            each representing an additional angular module that should be
            loaded.
        - 'can_edit': bool. Whether the given user can edit this activity.
        - 'dependencies_html': str. The additional HTML to insert on the page.
        - 'exploration_title': str. Title of exploration.
        - 'exploration_version': int. The version of the exploration.
        - 'collection_id': str. ID of the collection.
        - 'collection_title': str. Title of collection.
        - 'interaction_templates': str. The HTML bodies of the interactions
            required by the given exploration ID.
        - 'is_private': bool. Whether the exploration is private or not.
        - 'meta_name': str. Title of exploration.
        - 'meta_description': str. Objective of exploration.
    """
    try:
        exploration = exp_fetchers.get_exploration_by_id(
            exploration_id, version=version)
    except Exception:
        raise Exception

    collection_title = None
    if collection_id:
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
            collection_title = collection.title
        except Exception:
            raise Exception

    version = exploration.version

    # TODO(sll): Cache these computations.
    interaction_ids = exploration.get_interaction_ids()
    for interaction_id in feconf.ALLOWED_QUESTION_INTERACTION_IDS:
        if interaction_id not in interaction_ids:
            interaction_ids.append(interaction_id)

    dependency_ids = (
        interaction_registry.Registry.get_deduplicated_dependency_ids(
            interaction_ids))
    dependencies_html, additional_angular_modules = (
        dependency_registry.Registry.get_deps_html_and_angular_modules(
            dependency_ids))

    interaction_templates = (
        interaction_registry.Registry.get_interaction_html(
            interaction_ids))

    return {
        'INTERACTION_SPECS': interaction_registry.Registry.get_all_specs(),
        'additional_angular_modules': additional_angular_modules,
        'can_edit': can_edit,
        'dependencies_html': jinja2.utils.Markup(
            dependencies_html),
        'exploration_title': exploration.title,
        'exploration_version': version,
        'collection_id': collection_id,
        'collection_title': collection_title,
        'interaction_templates': jinja2.utils.Markup(
            interaction_templates),
        'is_private': rights_manager.is_exploration_private(
            exploration_id),
        # Note that this overwrites the value in base.py.
        'meta_name': exploration.title,
        # Note that this overwrites the value in base.py.
        'meta_description': utils.capitalize_string(exploration.objective),
    }
Beispiel #43
0
def _get_exploration_player_data(exploration_id, version, collection_id,
                                 can_edit):
    try:
        exploration = exp_services.get_exploration_by_id(exploration_id,
                                                         version=version)
    except Exception:
        raise Exception

    collection_title = None
    if collection_id:
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
            collection_title = collection.title
        except Exception:
            raise Exception

    version = exploration.version

    # TODO(sll): Cache these computations.
    gadget_types = exploration.get_gadget_types()
    interaction_ids = exploration.get_interaction_ids()
    dependency_ids = (interaction_registry.Registry.
                      get_deduplicated_dependency_ids(interaction_ids))
    dependencies_html, additional_angular_modules = (
        dependency_registry.Registry.get_deps_html_and_angular_modules(
            dependency_ids))

    gadget_templates = (gadget_registry.Registry.get_gadget_html(gadget_types))
    interaction_templates = (
        rte_component_registry.Registry.get_html_for_all_components() +
        interaction_registry.Registry.get_interaction_html(interaction_ids))

    return {
        'GADGET_SPECS':
        gadget_registry.Registry.get_all_specs(),
        'INTERACTION_SPECS':
        interaction_registry.Registry.get_all_specs(),
        'DEFAULT_TWITTER_SHARE_MESSAGE_PLAYER':
        (DEFAULT_TWITTER_SHARE_MESSAGE_PLAYER.value),
        'additional_angular_modules':
        additional_angular_modules,
        'can_edit':
        can_edit,
        'dependencies_html':
        jinja2.utils.Markup(dependencies_html),
        'exploration_title':
        exploration.title,
        'exploration_version':
        version,
        'collection_id':
        collection_id,
        'collection_title':
        collection_title,
        'gadget_templates':
        jinja2.utils.Markup(gadget_templates),
        'interaction_templates':
        jinja2.utils.Markup(interaction_templates),
        'is_private':
        rights_manager.is_exploration_private(exploration_id),
        # Note that this overwrites the value in base.py.
        'meta_name':
        exploration.title,
        # Note that this overwrites the value in base.py.
        'meta_description':
        utils.capitalize_string(exploration.objective),
        'nav_mode':
        feconf.NAV_MODE_EXPLORE,
    }
Beispiel #44
0
def get_learner_collection_dict_by_id(collection_id,
                                      user,
                                      strict=True,
                                      allow_invalid_explorations=False,
                                      version=None):
    """Gets a dictionary representation of a collection given by the provided
    collection ID. This dict includes user-specific playthrough information.

    Args:
        collection_id: str. The id of the collection.
        user: UserActionsInfo. Object having user_id, role and actions for
            given user.
        strict: bool. Whether to fail noisily if no collection with the given
            id exists in the datastore.
        allow_invalid_explorations: bool. Whether to also return explorations
            that are invalid, such as deleted/private explorations.
        version: str or None. The version number of the collection to be
            retrieved. If it is None, the latest version will be retrieved.

    Returns:
        dict. A dictionary that contains extra information along with the dict
        returned by collection_domain.Collection.to_dict() which includes useful
        data for the collection learner view. The information includes progress
        in the collection, information about explorations referenced within the
        collection, and a slightly nicer data structure for frontend work.

    Raises:
        ValidationError. If the collection retrieved using the given
            ID references non-existent explorations.
    """
    collection = collection_services.get_collection_by_id(collection_id,
                                                          strict=strict,
                                                          version=version)

    exp_ids = collection.exploration_ids
    exp_summary_dicts = get_displayable_exp_summary_dicts_matching_ids(
        exp_ids, user=user)
    exp_summaries_dict_map = {
        exp_summary_dict['id']: exp_summary_dict
        for exp_summary_dict in exp_summary_dicts
    }

    # TODO(bhenning): Users should not be recommended explorations they have
    # completed outside the context of a collection (see #1461).
    next_exploration_id = None
    completed_exp_ids = None
    if user.user_id:
        completed_exp_ids = (
            collection_services.get_valid_completed_exploration_ids(
                user.user_id, collection))
        next_exploration_id = collection.get_next_exploration_id(
            completed_exp_ids)
    else:
        # If the user is not logged in or they have not completed any of
        # the explorations yet within the context of this collection,
        # recommend the initial exploration.
        next_exploration_id = collection.first_exploration_id
        completed_exp_ids = []

    collection_dict = collection.to_dict()
    collection_dict['nodes'] = [node.to_dict() for node in collection.nodes]

    collection_dict['playthrough_dict'] = {
        'next_exploration_id': next_exploration_id,
        'completed_exploration_ids': completed_exp_ids
    }
    collection_dict['version'] = collection.version
    collection_is_public = rights_manager.is_collection_public(collection_id)

    # Insert an 'exploration' dict into each collection node, where the
    # dict includes meta information about the exploration (ID and title).
    for collection_node in collection_dict['nodes']:
        exploration_id = collection_node['exploration_id']
        summary_dict = exp_summaries_dict_map.get(exploration_id)
        if not allow_invalid_explorations:
            if not summary_dict:
                raise utils.ValidationError(
                    'Expected collection to only reference valid '
                    'explorations, but found an exploration with ID: %s (was '
                    'the exploration deleted or is it a private exploration '
                    'that you do not have edit access to?)' % exploration_id)
            if collection_is_public and rights_manager.is_exploration_private(
                    exploration_id):
                raise utils.ValidationError(
                    'Cannot reference a private exploration within a public '
                    'collection, exploration ID: %s' % exploration_id)

        if summary_dict:
            collection_node['exploration_summary'] = summary_dict
        else:
            collection_node['exploration_summary'] = None

    return collection_dict
Beispiel #45
0
    def get(self, exploration_id):
        """Handles GET requests."""
        version_str = self.request.get('v')
        version = int(version_str) if version_str else None

        # Note: this is an optional argument and will be None when the
        # exploration is being played outside the context of a collection.
        collection_id = self.request.get('collection_id')

        try:
            exploration = exp_services.get_exploration_by_id(
                exploration_id, version=version)
        except Exception as e:
            raise self.PageNotFoundException(e)

        collection_title = None
        if collection_id:
            try:
                collection = collection_services.get_collection_by_id(
                    collection_id)
                collection_title = collection.title
            except Exception as e:
                raise self.PageNotFoundException(e)

        version = exploration.version

        if not rights_manager.Actor(self.user_id).can_view(
                rights_manager.ACTIVITY_TYPE_EXPLORATION, exploration_id):
            raise self.PageNotFoundException

        is_iframed = (self.request.get('iframed') == 'true')

        # TODO(sll): Cache these computations.
        gadget_types = exploration.get_gadget_types()
        interaction_ids = exploration.get_interaction_ids()
        dependency_ids = (
            interaction_registry.Registry.get_deduplicated_dependency_ids(
                interaction_ids))
        dependencies_html, additional_angular_modules = (
            dependency_registry.Registry.get_deps_html_and_angular_modules(
                dependency_ids))

        gadget_templates = (
            gadget_registry.Registry.get_gadget_html(gadget_types))

        interaction_templates = (
            rte_component_registry.Registry.get_html_for_all_components() +
            interaction_registry.Registry.get_interaction_html(
                interaction_ids))

        self.values.update({
            'GADGET_SPECS': gadget_registry.Registry.get_all_specs(),
            'INTERACTION_SPECS': interaction_registry.Registry.get_all_specs(),
            'SHARING_OPTIONS': SHARING_OPTIONS.value,
            'SHARING_OPTIONS_TWITTER_TEXT': SHARING_OPTIONS_TWITTER_TEXT.value,
            'additional_angular_modules': additional_angular_modules,
            'can_edit': (
                bool(self.username) and
                self.username not in config_domain.BANNED_USERNAMES.value and
                rights_manager.Actor(self.user_id).can_edit(
                    rights_manager.ACTIVITY_TYPE_EXPLORATION, exploration_id)
            ),
            'dependencies_html': jinja2.utils.Markup(
                dependencies_html),
            'exploration_title': exploration.title,
            'exploration_version': version,
            'collection_id': collection_id,
            'collection_title': collection_title,
            'gadget_templates': jinja2.utils.Markup(gadget_templates),
            'iframed': is_iframed,
            'interaction_templates': jinja2.utils.Markup(
                interaction_templates),
            'is_private': rights_manager.is_exploration_private(
                exploration_id),
            # Note that this overwrites the value in base.py.
            'meta_name': exploration.title,
            # Note that this overwrites the value in base.py.
            'meta_description': utils.capitalize_string(exploration.objective),
            'nav_mode': feconf.NAV_MODE_EXPLORE,
        })

        if is_iframed:
            self.render_template(
                'player/exploration_player.html', iframe_restriction=None)
        else:
            self.render_template('player/exploration_player.html')
Beispiel #46
0
    def get(self, exploration_id):
        """Handles GET requests."""
        version_str = self.request.get('v')
        version = int(version_str) if version_str else None

        # Note: this is an optional argument and will be None when the
        # exploration is being played outside the context of a collection.
        collection_id = self.request.get('collection_id')

        try:
            exploration = exp_services.get_exploration_by_id(exploration_id,
                                                             version=version)
        except Exception as e:
            raise self.PageNotFoundException(e)

        collection_title = None
        if collection_id:
            try:
                collection = collection_services.get_collection_by_id(
                    collection_id)
                collection_title = collection.title
            except Exception as e:
                raise self.PageNotFoundException(e)

        version = exploration.version

        if not rights_manager.Actor(self.user_id).can_view(
                feconf.ACTIVITY_TYPE_EXPLORATION, exploration_id):
            raise self.PageNotFoundException

        is_iframed = (self.request.get('iframed') == 'true')

        # TODO(sll): Cache these computations.
        gadget_types = exploration.get_gadget_types()
        interaction_ids = exploration.get_interaction_ids()
        dependency_ids = (interaction_registry.Registry.
                          get_deduplicated_dependency_ids(interaction_ids))
        dependencies_html, additional_angular_modules = (
            dependency_registry.Registry.get_deps_html_and_angular_modules(
                dependency_ids))

        gadget_templates = (
            gadget_registry.Registry.get_gadget_html(gadget_types))

        interaction_templates = (
            rte_component_registry.Registry.get_html_for_all_components() +
            interaction_registry.Registry.get_interaction_html(interaction_ids)
        )

        self.values.update({
            'GADGET_SPECS':
            gadget_registry.Registry.get_all_specs(),
            'INTERACTION_SPECS':
            interaction_registry.Registry.get_all_specs(),
            'DEFAULT_TWITTER_SHARE_MESSAGE_PLAYER':
            (DEFAULT_TWITTER_SHARE_MESSAGE_PLAYER.value),
            'additional_angular_modules':
            additional_angular_modules,
            'can_edit':
            (bool(self.username)
             and self.username not in config_domain.BANNED_USERNAMES.value
             and rights_manager.Actor(self.user_id).can_edit(
                 feconf.ACTIVITY_TYPE_EXPLORATION, exploration_id)),
            'dependencies_html':
            jinja2.utils.Markup(dependencies_html),
            'exploration_title':
            exploration.title,
            'exploration_version':
            version,
            'collection_id':
            collection_id,
            'collection_title':
            collection_title,
            'gadget_templates':
            jinja2.utils.Markup(gadget_templates),
            'iframed':
            is_iframed,
            'interaction_templates':
            jinja2.utils.Markup(interaction_templates),
            'is_private':
            rights_manager.is_exploration_private(exploration_id),
            # Note that this overwrites the value in base.py.
            'meta_name':
            exploration.title,
            # Note that this overwrites the value in base.py.
            'meta_description':
            utils.capitalize_string(exploration.objective),
            'nav_mode':
            feconf.NAV_MODE_EXPLORE,
        })

        if is_iframed:
            self.render_template('player/exploration_player.html',
                                 iframe_restriction=None)
        else:
            self.render_template('player/exploration_player.html')
Beispiel #47
0
    def get(self, collection_id):
        """Populates the data on the individual collection page."""
        try:
            collection = collection_services.get_collection_by_id(
                collection_id)
        except Exception as e:
            raise self.PageNotFoundException(e)

        exp_ids = collection.exploration_ids
        exp_summaries = (
            exp_services.get_exploration_summaries_matching_ids(exp_ids))
        exp_summaries_dict = {
            exp_id: exp_summaries[ind]
            for (ind, exp_id) in enumerate(exp_ids)
        }

        # TODO(bhenning): Users should not be recommended explorations they
        # have completed outside the context of a collection.
        next_exploration_ids = None
        completed_exploration_ids = None
        if self.user_id:
            completed_exploration_ids = (
                collection_services.get_completed_exploration_ids(
                    self.user_id, collection_id))
            next_exploration_ids = collection.get_next_exploration_ids(
                completed_exploration_ids)
        else:
            # If the user is not logged in or they have not completed any of
            # the explorations yet within the context of this collection,
            # recommend the initial explorations.
            next_exploration_ids = collection.init_exploration_ids
            completed_exploration_ids = []

        collection_dict = collection.to_dict()
        collection_dict['next_exploration_ids'] = next_exploration_ids
        collection_dict['completed_exploration_ids'] = (
            completed_exploration_ids)

        # Insert an 'exploration' dict into each collection node, where the
        # dict includes meta information about the exploration (ID and title).
        for collection_node in collection_dict['nodes']:
            summary = exp_summaries_dict.get(collection_node['exploration_id'])
            collection_node['exploration'] = {
                'id':
                collection_node['exploration_id'],
                'title':
                summary.title if summary else None,
                'category':
                summary.category if summary else None,
                'objective':
                summary.objective if summary else None,
                'ratings':
                summary.ratings if summary else None,
                'last_updated_msec':
                utils.get_time_in_millisecs(
                    summary.exploration_model_last_updated)
                if summary else None,
                'thumbnail_icon_url':
                utils.get_thumbnail_icon_url_for_category(summary.category),
                'thumbnail_bg_color':
                utils.get_hex_color_for_category(summary.category),
            }

        self.values.update({
            'can_edit':
            (self.user_id and rights_manager.Actor(self.user_id).can_edit(
                rights_manager.ACTIVITY_TYPE_COLLECTION, collection_id)),
            'collection':
            collection_dict,
            'info_card_image_url':
            utils.get_info_card_url_for_category(collection.category),
            'is_logged_in':
            bool(self.user_id),
            'session_id':
            utils.generate_new_session_id(),
        })

        self.render_json(self.values)
Beispiel #48
0
def get_learner_collection_dict_by_id(collection_id,
                                      user_id,
                                      strict=True,
                                      allow_invalid_explorations=False,
                                      version=None):
    """Creates and returns a dictionary representation of a collection given by
    the provided collection ID. This dictionary contains extra information
    along with the dict returned by collection_domain.Collection.to_dict()
    which includes useful data for the collection learner view. The information
    includes progress in the collection, information about explorations
    referenced within the collection, and a slightly nicer data structure for
    frontend work.
    This raises a ValidationError if the collection retrieved using the given
    ID references non-existent explorations.
    which includes useful data for the collection learner view.
    """
    collection = collection_services.get_collection_by_id(collection_id,
                                                          strict=strict,
                                                          version=version)

    exp_ids = collection.exploration_ids
    exp_summary_dicts = get_displayable_exp_summary_dicts_matching_ids(
        exp_ids, editor_user_id=user_id)
    exp_summaries_dict_map = {
        exp_summary_dict['id']: exp_summary_dict
        for exp_summary_dict in exp_summary_dicts
    }

    # TODO(bhenning): Users should not be recommended explorations they have
    # completed outside the context of a collection (see #1461).
    next_exploration_ids = None
    completed_exp_ids = None
    if user_id:
        completed_exp_ids = (
            collection_services.get_valid_completed_exploration_ids(
                user_id, collection_id, collection))
        next_exploration_ids = collection.get_next_exploration_ids(
            completed_exp_ids)
    else:
        # If the user is not logged in or they have not completed any of
        # the explorations yet within the context of this collection,
        # recommend the initial explorations.
        next_exploration_ids = collection.init_exploration_ids
        completed_exp_ids = []

    collection_dict = collection.to_dict()
    collection_dict['skills'] = collection.skills
    collection_dict['playthrough_dict'] = {
        'next_exploration_ids': next_exploration_ids,
        'completed_exploration_ids': completed_exp_ids
    }
    collection_dict['version'] = collection.version
    collection_is_public = rights_manager.is_collection_public(collection_id)

    # Insert an 'exploration' dict into each collection node, where the
    # dict includes meta information about the exploration (ID and title).
    for collection_node in collection_dict['nodes']:
        exploration_id = collection_node['exploration_id']
        summary_dict = exp_summaries_dict_map.get(exploration_id)
        if not allow_invalid_explorations:
            if not summary_dict:
                raise utils.ValidationError(
                    'Expected collection to only reference valid '
                    'explorations, but found an exploration with ID: %s (was '
                    'the exploration deleted or is it a private exploration '
                    'that you do not have edit access to?)' % exploration_id)
            if collection_is_public and rights_manager.is_exploration_private(
                    exploration_id):
                raise utils.ValidationError(
                    'Cannot reference a private exploration within a public '
                    'collection, exploration ID: %s' % exploration_id)

        if summary_dict:
            collection_node['exploration_summary'] = summary_dict
        else:
            collection_node['exploration_summary'] = None

    return collection_dict
Beispiel #49
0
def get_learner_collection_dict_by_id(
        collection_id, user_id, strict=True, allow_invalid_explorations=False,
        version=None):
    """Creates and returns a dictionary representation of a collection given by
    the provided collection ID. This dictionary contains extra information
    along with the dict returned by collection_domain.Collection.to_dict()
    which includes useful data for the collection learner view. The information
    includes progress in the collection, information about explorations
    referenced within the collection, and a slightly nicer data structure for
    frontend work.
    This raises a ValidationError if the collection retrieved using the given
    ID references non-existent explorations.
    which includes useful data for the collection learner view.
    """
    collection = collection_services.get_collection_by_id(
        collection_id, strict=strict, version=version)

    exp_ids = collection.exploration_ids
    exp_summary_dicts = get_displayable_exp_summary_dicts_matching_ids(
        exp_ids, editor_user_id=user_id)
    exp_summaries_dict_map = {
        exp_summary_dict['id']: exp_summary_dict
        for exp_summary_dict in exp_summary_dicts
    }

    # TODO(bhenning): Users should not be recommended explorations they have
    # completed outside the context of a collection (see #1461).
    next_exploration_ids = None
    completed_exp_ids = None
    if user_id:
        completed_exp_ids = (
            collection_services.get_valid_completed_exploration_ids(
                user_id, collection_id, collection))
        next_exploration_ids = collection.get_next_exploration_ids(
            completed_exp_ids)
    else:
        # If the user is not logged in or they have not completed any of
        # the explorations yet within the context of this collection,
        # recommend the initial explorations.
        next_exploration_ids = collection.init_exploration_ids
        completed_exp_ids = []

    collection_dict = collection.to_dict()
    collection_dict['skills'] = collection.skills
    collection_dict['playthrough_dict'] = {
        'next_exploration_ids': next_exploration_ids,
        'completed_exploration_ids': completed_exp_ids
    }
    collection_dict['version'] = collection.version
    collection_is_public = rights_manager.is_collection_public(collection_id)

    # Insert an 'exploration' dict into each collection node, where the
    # dict includes meta information about the exploration (ID and title).
    for collection_node in collection_dict['nodes']:
        exploration_id = collection_node['exploration_id']
        summary_dict = exp_summaries_dict_map.get(exploration_id)
        if not allow_invalid_explorations:
            if not summary_dict:
                raise utils.ValidationError(
                    'Expected collection to only reference valid '
                    'explorations, but found an exploration with ID: %s (was '
                    'the exploration deleted or is it a private exploration '
                    'that you do not have edit access to?)'
                    % exploration_id)
            if collection_is_public and rights_manager.is_exploration_private(
                    exploration_id):
                raise utils.ValidationError(
                    'Cannot reference a private exploration within a public '
                    'collection, exploration ID: %s' % exploration_id)

        if summary_dict:
            collection_node['exploration_summary'] = summary_dict
        else:
            collection_node['exploration_summary'] = None

    return collection_dict