Пример #1
0
    def post(self, user):
        """Handles POST requests."""

        payload = json.loads(self.request.get('payload'))

        title = payload.get('title')
        category = payload.get('category')

        if not title:
            raise self.InvalidInputException('No title supplied.')
        if not category:
            raise self.InvalidInputException('No category chosen.')

        yaml = self.request.get('yaml')

        if yaml and feconf.ALLOW_YAML_FILE_UPLOAD:
            exploration = Exploration.create_from_yaml(
                yaml_file=yaml, user=user, title=title, category=category)
        else:
            exploration = Exploration.create(
                user, title=title, category=category)

        self.response.write(json.dumps({
            'explorationId': exploration.id,
        }))
Пример #2
0
    def test_yaml_methods(self):
        """Test the as_yaml() and create_from_yaml() methods."""
        exploration = Exploration.create(
            self.user, 'A title', 'A category', 'A different exploration_id')
        exploration.add_state('New state')
        yaml_file = exploration.as_yaml()
        self.assertEqual(yaml_file, """Activity 1:
  content: []
  param_changes: []
  widget:
    handlers:
    - name: submit
      rules:
      - dest: Activity 1
        feedback: []
        inputs: {}
        name: Default
        param_changes: []
    params: {}
    sticky: false
    widget_id: Continue
New state:
  content: []
  param_changes: []
  widget:
    handlers:
    - name: submit
      rules:
      - dest: New state
        feedback: []
        inputs: {}
        name: Default
        param_changes: []
    params: {}
    sticky: false
    widget_id: Continue
""")

        exploration2 = Exploration.create_from_yaml(
            yaml_file, self.user, 'Title', 'Category')
        self.assertEqual(len(exploration2.states), 2)
        self.assertEqual(exploration2.as_yaml(), yaml_file)

        self.assertEqual(Exploration.query().count(), 2)

        with self.assertRaises(Exception):
            Exploration.create_from_yaml(
                'No_initial_state_name', self.user, 'Title', 'category')

        with self.assertRaises(Exception):
            Exploration.create_from_yaml(
                'Invalid\ninit_state_name:\nMore stuff',
                self.user, 'Title', 'category')

        with self.assertRaises(Exception):
            Exploration.create_from_yaml(
                'State1:\n(\nInvalid yaml', self.user, 'Title', 'category')

        # Check that no new exploration was created.
        self.assertEqual(Exploration.query().count(), 2)
Пример #3
0
    def test_get_exploration_error_cases(self):
        """Test the error cases for the get() method."""
        with self.assertRaises(Exception):
            Exploration.get('Invalid id')
        with self.assertRaises(Exception):
            Exploration.get('Invalid id', strict=True)

        # The get() should fail silently when strict == False.
        self.assertIsNone(Exploration.get('Invalid id', strict=False))
Пример #4
0
    def get(self):
        """Handles GET requests."""
        exp_ids = Exploration.get_explorations_user_can_edit(self.user)
        improvable = Statistics.get_top_ten_improvable_states(exp_ids)
        explorations = []
        for exp_id in exp_ids:
            exp = Exploration.get(exp_id)
            explorations.append({"name": exp.title, "id": exp.id, "image_id": exp.image_id})

        self.values.update({"improvable": improvable, "explorations": explorations})
        self.render_json(self.values)
Пример #5
0
def reload_demos():
    """Reload default classifiers, widgets, and explorations (in that order)."""
    Classifier.delete_all_classifiers()
    Classifier.load_default_classifiers()

    Widget.delete_all_widgets()
    InteractiveWidget.load_default_widgets()
    NonInteractiveWidget.load_default_widgets()

    Exploration.delete_demo_explorations()
    Exploration.load_demo_explorations()
Пример #6
0
    def test_editor_permissions(self):
        """Test permissions."""
        exploration = Exploration.create(self.user, 'Title', 'Category', 'eid')
        exploration.put()
        self.assertTrue(exploration.is_editable_by(self.user))
        self.assertFalse(exploration.is_editable_by(self.another_user))

        self.assertItemsEqual(
            Exploration.get_viewable_explorations(self.user), [exploration])
        self.assertItemsEqual(
            Exploration.get_viewable_explorations(self.another_user), [])
Пример #7
0
    def test_create_get_and_delete_exploration(self):
        """Test the create(), get() and delete() methods."""
        exploration = Exploration.create(
            self.user, 'A title', 'A category', 'A exploration_id')
        exploration.put()

        retrieved_exploration = Exploration.get('A exploration_id')
        self.assertEqual(exploration, retrieved_exploration)

        exploration.delete()
        with self.assertRaises(Exception):
            retrieved_exploration = Exploration.get('A exploration_id')
Пример #8
0
    def get(self, exploration_id):
        """Handles GET requests."""
        self.values.update({
            'js': utils.get_js_controllers(['readerExploration']),
            'nav_mode': READER_MODE,
        })

        Exploration.get(exploration_id)

        # The following allows embedding of Oppia explorations in other pages.
        if self.request.get('iframed') == 'true':
            self.values['iframed'] = True

        self.render_template('reader/reader_exploration.html')
Пример #9
0
    def get(self, exploration_id):
        """Populates the data on the individual exploration page."""
        # TODO(sll): Maybe this should send a complete state machine to the
        # frontend, and all interaction would happen client-side?
        exploration = Exploration.get(exploration_id)
        init_state = exploration.init_state.get()
        params = self.get_params(init_state)
        init_html, init_widgets = parse_content_into_html(
            init_state.content, 0, params)
        interactive_widget_html = InteractiveWidget.get_raw_code(
            init_state.widget.widget_id,
            params=utils.parse_dict_with_params(
                init_state.widget.params, params)
        )

        self.values.update({
            'block_number': 0,
            'interactive_widget_html': interactive_widget_html,
            'interactive_params': init_state.widget.params,
            'oppia_html': init_html,
            'params': params,
            'state_id': init_state.id,
            'title': exploration.title,
            'widgets': init_widgets,
        })
        if init_state.widget.widget_id in DEFAULT_ANSWERS:
            self.values['default_answer'] = (
                DEFAULT_ANSWERS[init_state.widget.widget_id])
        self.response.write(json.dumps(self.values))

        EventHandler.record_exploration_visited(exploration_id)
        EventHandler.record_state_hit(exploration_id, init_state.id)
Пример #10
0
    def test_incomplete_and_default_flags(self):
        InteractiveWidget.load_default_widgets()
        exp = Exploration.create(
            User(email='*****@*****.**'), 'exploration', 'category', 'eid')

        state_id = exp.init_state.get().id

        # Hit the default once, and do an incomplete twice. The result should
        # be classified as incomplete.

        for i in range(3):
            EventHandler.record_state_hit('eid', state_id)

        EventHandler.record_rule_hit(
            'eid', state_id, Rule(name='Default', dest=state_id),
            extra_info='1')

        states = Statistics.get_top_ten_improvable_states(['eid'])
        self.assertEquals(len(states), 1)
        self.assertEquals(states[0]['rank'], 2)
        self.assertEquals(states[0]['type'], 'incomplete')

        # Now hit the default two more times. The result should be classified
        # as default.

        for i in range(2):
            EventHandler.record_state_hit('eid', state_id)
            EventHandler.record_rule_hit(
                'eid', state_id, Rule(name='Default', dest=state_id),
                extra_info='1')

        states = Statistics.get_top_ten_improvable_states(['eid'])
        self.assertEquals(len(states), 1)
        self.assertEquals(states[0]['rank'], 3)
        self.assertEquals(states[0]['type'], 'default')
Пример #11
0
    def test_editor(self, exploration_id, state_id=None, **kwargs):
        """Gets the user and exploration id if the user can edit it.

        Args:
            self: the handler instance
            exploration_id: the exploration id
            state_id: the state id, if it exists
            **kwargs: any other arguments passed to the handler

        Returns:
            The user and exploration instance, if the user is authorized to edit
            this exploration. Also, the state instance, if one is supplied.

        Raises:
            self.NotLoggedInException: if there is no current user.
            self.UnauthorizedUserException: if the user exists but does not have
                the right credentials.
        """
        user = users.get_current_user()
        if not user:
            self.redirect(users.create_login_url(self.request.uri))
            return

        exploration = Exploration.get(exploration_id)

        if not exploration.is_editable_by(user):
            raise self.UnauthorizedUserException(
                '%s does not have the credentials to edit this exploration.',
                user)

        if not state_id:
            return handler(self, user, exploration, **kwargs)
        state = State.get(state_id, exploration)
        return handler(self, user, exploration, state, **kwargs)
Пример #12
0
    def test_state_operations(self):
        """Test adding, renaming and checking existence of states."""
        exploration = Exploration.create(self.user, 'Title', 'Category', 'eid')
        exploration.put()

        self.assertEqual(len(exploration.states), 1)

        default_state = exploration.states[0].get()
        default_state_name = default_state.name
        exploration.rename_state(default_state, 'Renamed state')

        self.assertEqual(len(exploration.states), 1)
        self.assertEqual(default_state.name, 'Renamed state')

        # Add a new state.
        second_state = exploration.add_state('State 2')
        self.assertEqual(len(exploration.states), 2)

        # It is OK to rename a state to itself.
        exploration.rename_state(second_state, second_state.name)
        self.assertEqual(second_state.name, 'State 2')

        # But it is not OK to add or rename a state using a name that already
        # exists.
        with self.assertRaises(Exception):
            exploration.add_state('State 2')
        with self.assertRaises(Exception):
            exploration.rename_state(second_state, 'Renamed state')

        # The exploration now has exactly two states.
        self.assertFalse(exploration._has_state_named(default_state_name))
        self.assertTrue(exploration._has_state_named('Renamed state'))
        self.assertTrue(exploration._has_state_named('State 2'))
Пример #13
0
    def test_get_top_ten_improvable_states(self):
        InteractiveWidget.load_default_widgets()
        exp = Exploration.create(
            User(email='*****@*****.**'), 'exploration', 'category', 'eid')

        state_id = exp.init_state.get().id

        EventHandler.record_rule_hit(
            'eid', state_id, Rule(name='Default', dest=state_id),
            extra_info='1')
        EventHandler.record_rule_hit(
            'eid', state_id, Rule(name='Default', dest=state_id),
            extra_info='2')
        EventHandler.record_rule_hit(
            'eid', state_id, Rule(name='Default', dest=state_id),
            extra_info='1')

        EventHandler.record_state_hit('eid', state_id)
        EventHandler.record_state_hit('eid', state_id)
        EventHandler.record_state_hit('eid', state_id)
        EventHandler.record_state_hit('eid', state_id)
        EventHandler.record_state_hit('eid', state_id)

        states = Statistics.get_top_ten_improvable_states(['eid'])
        self.assertEquals(len(states), 1)
        self.assertEquals(states[0]['exp_id'], 'eid')
        self.assertEquals(states[0]['type'], 'default')
        self.assertEquals(states[0]['rank'], 3)
        self.assertEquals(states[0]['state_id'], exp.init_state.get().id)
Пример #14
0
    def get(self):
        """Handles GET requests."""
        if not Exploration.get('0', strict=False):
            admin.reload_demos()

        self.values.update({
            'gallery_login_url': users.create_login_url('/gallery'),
        })
        self.render_template('index.html')
Пример #15
0
    def test_dataset_operations(self):
        """Test adding, deleting and adding values to a dataset."""
        exploration = Exploration.create(self.user, 'Title', 'Category', 'eid')
        exploration.put()

        self.assertEqual(len(exploration.datasets), 0)

        exploration.add_dataset('Dataset 1', ['Name', 'Number'])
        self.assertEqual(len(exploration.datasets), 1)
        self.assertEqual(exploration.datasets[0].name, 'Dataset 1')
        self.assertIsNone(exploration.datasets[0].get_random_param_set())

        # The same dataset name cannot be used more than once.
        with self.assertRaises(Exception):
            exploration.add_dataset('Dataset 1', ['Name', 'Number'])

        # Parameter names cannot be repeated across datasets.
        with self.assertRaises(Exception):
            exploration.add_dataset('Dataset 2', ['Name'])

        # It is not possible to add data to a non-existent dataset.
        with self.assertRaises(Exception):
            exploration.add_data_to_dataset('Fake dataset', [])

        exploration.add_data_to_dataset('Dataset 1', [])
        self.assertIsNone(exploration.datasets[0].get_random_param_set())

        # Parameter set keys must match the dataset schema.
        with self.assertRaises(Exception):
            exploration.add_data_to_dataset('Dataset 1', [{'Fake Key': 'John'}])
        with self.assertRaises(KeyError):
            exploration.add_data_to_dataset('Dataset 1', [{'Name': 'John'}])

        exploration.add_data_to_dataset(
            'Dataset 1', [{'Name': 'John', 'Number': '123'}])
        param_set = exploration.datasets[0].get_random_param_set()
        self.assertEqual(len(param_set.params), 2)
        self.assertEqual(param_set.params[0].name, 'Name')
        self.assertEqual(param_set.params[0].obj_type, 'UnicodeString')
        self.assertEqual(param_set.params[0].values[0], 'John')
        self.assertEqual(param_set.params[1].name, 'Number')
        self.assertEqual(param_set.params[1].obj_type, 'UnicodeString')
        self.assertEqual(param_set.params[1].values[0], '123')

        param_set_key = param_set.key
        self.assertTrue(ParamSet.get_by_id(param_set_key.id()))

        with self.assertRaises(Exception):
            exploration.delete_dataset('Fake dataset')

        exploration.add_dataset('Dataset 2', ['Address', 'Zip code'])
        self.assertEqual(len(exploration.datasets), 2)

        exploration.delete_dataset('Dataset 1')
        self.assertEqual(len(exploration.datasets), 1)
        self.assertIsNone(ParamSet.get_by_id(param_set_key.id()))
Пример #16
0
    def get(self):
        """Handles GET requests."""
        explorations = Exploration.query().filter(
            Exploration.is_public == True).fetch(100)

        # Don't use the first exploration; users will have seen that already
        # on the main page.
        selected_exploration = utils.get_random_choice(explorations[1:])

        self.redirect('/learn/%s' % selected_exploration.id)
Пример #17
0
    def setUp(self):
        """Loads the default widgets and create sample users and explorations."""
        super(StateModelUnitTests, self).setUp()
        InteractiveWidget.load_default_widgets()

        self.user = User(email="*****@*****.**")
        self.another_user = User(email="*****@*****.**")

        self.exploration = Exploration.create(self.user, "A title", "A category", "A exploration_id")
        self.exploration.put()
Пример #18
0
    def test_loading_and_deletion_of_demo_explorations(self):
        """Test loading and deletion of the demo explorations."""
        self.assertEqual(Exploration.query().count(), 0)

        Exploration.load_demo_explorations()
        self.assertEqual(Exploration.query().count(), 6)

        Exploration.delete_demo_explorations()
        self.assertEqual(Exploration.query().count(), 0)
Пример #19
0
    def post(self):
        """Handles POST requests."""

        payload = json.loads(self.request.get('payload'))

        exploration_id = payload.get('exploration_id')

        forked_exploration = Exploration.get(exploration_id)
        if not forked_exploration.is_demo_exploration():
            raise self.InvalidInputException('Exploration cannot be forked.')

        # Get the demo exploration as a YAML file, so that new states can be
        # created.
        yaml = forked_exploration.as_yaml()
        title = 'Copy of %s' % forked_exploration.title
        category = forked_exploration.category

        exploration = Exploration.create_from_yaml(
            yaml_file=yaml, user=self.user, title=title, category=category)

        self.render_json({'explorationId': exploration.id})
Пример #20
0
    def setUp(self):
        """Loads the default widgets."""
        super(ExplorationServicesUnitTests, self).setUp()
        InteractiveWidget.load_default_widgets()

        self.owner = User(email='*****@*****.**')
        self.editor = User(email='*****@*****.**')
        self.viewer = User(email='*****@*****.**')

        self.exploration = Exploration.create(
            self.owner, 'A title', 'A category', 'A exploration_id')
        self.exploration.editors.append(self.editor)
        self.exploration.put()
Пример #21
0
 def test_is_demo_exploration_method(self):
     """Test the is_demo_exploration() method."""
     demo = Exploration(id='0')
     self.assertEqual(demo.is_demo_exploration(), True)
     notdemo1 = Exploration(id='a')
     self.assertEqual(notdemo1.is_demo_exploration(), False)
     notdemo2 = Exploration(id='abcd')
     self.assertEqual(notdemo2.is_demo_exploration(), False)
Пример #22
0
    def test_two_state_default_hit(self):
        SECOND_STATE = 'State 2'

        InteractiveWidget.load_default_widgets()
        exp = Exploration.create(
            User(email='*****@*****.**'), 'exploration', 'category', 'eid')
        second_state = exp.add_state(SECOND_STATE)

        state_1_id = exp.init_state.get().id
        state_2_id = second_state.id

        # Hit the default rule of state 1 once, and the default rule of state 2
        # twice.
        EventHandler.record_state_hit('eid', state_1_id)
        EventHandler.record_rule_hit(
            'eid', state_1_id, Rule(name='Default', dest=state_1_id),
            extra_info='1')

        for i in range(2):
            EventHandler.record_state_hit('eid', state_2_id)
            EventHandler.record_rule_hit(
                'eid', state_2_id, Rule(name='Default', dest=state_2_id),
                extra_info='1')

        states = Statistics.get_top_ten_improvable_states(['eid'])
        self.assertEquals(len(states), 2)
        self.assertEquals(states[0]['rank'], 2)
        self.assertEquals(states[0]['type'], 'default')
        self.assertEquals(states[0]['state_id'], state_2_id)
        self.assertEquals(states[1]['rank'], 1)
        self.assertEquals(states[1]['type'], 'default')
        self.assertEquals(states[1]['state_id'], state_1_id)

        # Hit the default rule of state 1 two more times.

        for i in range(2):
            EventHandler.record_state_hit('eid', state_1_id)
            EventHandler.record_rule_hit(
                'eid', state_1_id, Rule(name='Default', dest=state_1_id),
                extra_info='1')

        states = Statistics.get_top_ten_improvable_states(['eid'])
        self.assertEquals(len(states), 2)
        self.assertEquals(states[0]['rank'], 3)
        self.assertEquals(states[0]['type'], 'default')
        self.assertEquals(states[0]['state_id'], state_1_id)
        self.assertEquals(states[1]['rank'], 2)
        self.assertEquals(states[1]['type'], 'default')
        self.assertEquals(states[1]['state_id'], state_2_id)
Пример #23
0
    def test_no_improvement_flag_hit(self):
        InteractiveWidget.load_default_widgets()
        exp = Exploration.create(
            User(email='*****@*****.**'), 'exploration', 'category', 'eid')

        init_state = exp.init_state.get()
        state_id = init_state.id
        init_state.widget.handlers[0].rules = [
            Rule(name='NotDefault', dest=state_id),
            Rule(name='Default', dest=state_id),
        ]
        init_state.put()

        EventHandler.record_rule_hit(
            'eid', state_id, Rule(name='NotDefault', dest=state_id),
            extra_info='1')
        EventHandler.record_state_hit('eid', state_id)

        states = Statistics.get_top_ten_improvable_states(['eid'])
        self.assertEquals(len(states), 0)
Пример #24
0
    def get(self, exploration_id):
        """Populates the data on the individual exploration page."""
        # TODO(sll): Maybe this should send a complete state machine to the
        # frontend, and all interaction would happen client-side?
        try:
            exploration = Exploration.get(exploration_id)
        except Exception as e:
            raise self.PageNotFoundException(e)

        init_state = exploration.init_state.get()
        # TODO: get params from exploration specification instead
        params = self._get_exploration_params(exploration)
        params = get_params(init_state, params)
        init_html, init_widgets = parse_content_into_html(
            init_state.content, 0, params)
        interactive_widget_html = InteractiveWidget.get_raw_code(
            init_state.widget.widget_id,
            params=utils.parse_dict_with_params(
                init_state.widget.params, params)
        )

        self.values.update({
            'block_number': 0,
            'interactive_widget_html': interactive_widget_html,
            'interactive_params': init_state.widget.params,
            'oppia_html': init_html,
            'params': params,
            'state_id': init_state.id,
            'title': exploration.title,
            'widgets': init_widgets,
        })
        if init_state.widget.widget_id in DEFAULT_ANSWERS:
            self.values['default_answer'] = (
                DEFAULT_ANSWERS[init_state.widget.widget_id])
        self.render_json(self.values)

        EventHandler.record_exploration_visited(exploration_id)
        EventHandler.record_state_hit(exploration_id, init_state.id)
Пример #25
0
def get_viewable_explorations(user):
    """Returns a list of explorations viewable by the given user."""
    return Exploration.query().filter(
        ndb.OR(Exploration.is_public == True, Exploration.editors == user)
    )
Пример #26
0
    def get_top_ten_improvable_states(cls, exploration_ids):
        ranked_states = []
        for exp in exploration_ids:
            exploration = Exploration.get(exp)
            for state_db_key in exploration.states:
                state = state_db_key.get()
                state_key = '%s.%s' % (exp, state.id)

                # Get count of how many times the state was hit
                event_key = get_event_key(STATS_ENUMS.state_hit, state_key)
                all_count = Counter.get_value_by_id(event_key)
                if all_count == 0:
                    continue

                # Count the number of times the default rule was hit.
                event_key = get_event_key(
                    STATS_ENUMS.rule_hit, '%s.Default' % state_key)
                default_count = Journal.get_value_count_by_id(event_key)
                journal = Journal.get_by_id(event_key)
                if journal:
                    top_default_answers = collections.Counter(journal.values).most_common(5)
                else:
                    top_default_answers = []

                # Count the number of times an answer was submitted, regardless
                # of which rule it hits.
                completed_count = 0
                for handler in state.widget.handlers:
                    for rule in handler.rules:
                        rule_name = create_rule_name(rule)
                        event_key = get_event_key(
                            STATS_ENUMS.rule_hit, '%s.%s' %
                            (state_key, rule_name))
                        completed_count += Journal.get_value_count_by_id(
                            event_key)

                incomplete_count = all_count - completed_count

                state_rank, improve_type = 0, ''

                eligible_flags = []
                default_rule = filter(lambda rule: rule.name == 'Default', state.widget.handlers[0].rules)[0]
                default_self_loop = default_rule.dest == state.id
                if float(default_count) / all_count > .2 and default_self_loop:
                    eligible_flags.append({
                        'rank': default_count,
                        'improve_type': IMPROVE_TYPE_DEFAULT})
                if float(incomplete_count) / all_count > .2:
                    eligible_flags.append({
                        'rank': incomplete_count,
                        'improve_type': IMPROVE_TYPE_INCOMPLETE})

                eligible_flags = sorted(
                    eligible_flags, key=lambda flag: flag['rank'], reverse=True)
                if eligible_flags:
                    state_rank = eligible_flags[0]['rank']
                    improve_type = eligible_flags[0]['improve_type']

                ranked_states.append({'exp_id': exp, 'exp_name': exploration.title,
                                      'state_id': state.id, 'state_name': state.name,
                                      'rank': state_rank, 'type': improve_type,
                                      'top_default_answers': top_default_answers})

        problem_states = sorted(
            [state for state in ranked_states if state['rank'] != 0],
            key=lambda state: state['rank'],
            reverse=True)
        return problem_states[:10]
Пример #27
0
    def get_exploration_stats(cls, event_name, exploration_id):
        """Retrieves statistics for the given event name and exploration id."""

        if event_name == STATS_ENUMS.exploration_visited:
            event_key = get_event_key(event_name, exploration_id)
            counter = Counter.get_by_id(event_key)
            if not counter:
                return 0
            return counter.value

        if event_name == STATS_ENUMS.exploration_completed:
            event_key = get_event_key(event_name, exploration_id)
            counter = Counter.get_by_id(event_key)
            if not counter:
                return 0
            return counter.value

        if event_name == STATS_ENUMS.rule_hit:
            result = {}

            exploration = Exploration.get(exploration_id)
            for state_key in exploration.states:
                state = state_key.get()
                result[state.id] = {
                    'name': state.name,
                    'rules': {}
                }
                for handler in state.widget.handlers:
                    for rule in handler.rules:
                        rule_name = create_rule_name(rule)
                        event_key = get_event_key(
                            event_name, '.'.join([exploration_id, state.id, rule_name]))

                        journal = Journal.get_by_id(event_key)

                        if journal:
                            top_ten = collections.Counter(
                                journal.values).most_common(10)
                        else:
                            top_ten = []

                        result[state.id]['rules'][rule_name] = {
                            'answers': top_ten,
                        }

            return result

        if event_name == STATS_ENUMS.state_hit:
            result = {}

            exploration = Exploration.get(exploration_id)
            for state_key in exploration.states:
                state = state_key.get()
                event_key = get_event_key(
                    event_name, '.'.join([exploration_id, state.id]))

                counter = Counter.get_by_id(event_key)
                if not counter:
                    count = 0
                else:
                    count = counter.value

                result[state.id] = {
                    'name': state.name,
                    'count': count,
                }
            return result
Пример #28
0
    def test_exploration_class(self):
        """Test the Exploration class."""
        exploration = Exploration(id='The exploration hash id')

        # A new exploration should have a default title property.
        self.assertEqual(exploration.title, 'New exploration')

        # A new exploration should have a default is_public property.
        self.assertEqual(exploration.is_public, False)

        # An Exploration must have properties 'category' and 'init_state' set.
        with self.assertRaises(BadValueError):
            exploration.put()
        exploration.category = 'The category'
        with self.assertRaises(BadValueError):
            exploration.put()

        # The 'init_state' property must be a valid State.
        with self.assertRaises(BadValueError):
            exploration.init_state = 'The State'
        state = State(id='The state hash id')
        state.put()
        exploration.init_state = state.key

        # The 'states' property must be a list.
        with self.assertRaises(BadValueError):
            exploration.states = 'A string'
        # TODO(emersoj): We should put the initial state in the states list. It
        # should not be empty
        exploration.states = []

        # The 'states property must be a list of State keys.
        with self.assertRaises(BadValueError):
            exploration.states = ['A string']
        with self.assertRaises(BadValueError):
            exploration.states = [state]
        exploration.states = [state.key]

        # The 'parameters' property must be a list of Parameter objects.
        with self.assertRaises(BadValueError):
            exploration.parameters = 'A string'
        exploration.parameters = []
        parameter = Parameter(name='theParameter', obj_type='Int')
        with self.assertRaises(BadValueError):
            exploration.parameters = [parameter.key]
        exploration.parameters = [parameter]

        # The 'is_public' property must be a boolean.
        with self.assertRaises(BadValueError):
            exploration.is_public = 'true'
        exploration.is_public = True

        # The 'image_id' property must be a string.
        image = Image(id='The image')
        with self.assertRaises(BadValueError):
            exploration.image_id = image
        with self.assertRaises(BadValueError):
            exploration.image_id = image.key
        exploration.image_id = 'A string'

        # The 'editors' property must be a list of User objects.
        with self.assertRaises(BadValueError):
            exploration.editors = 'A string'
        with self.assertRaises(BadValueError):
            exploration.editors = ['A string']
        exploration.editors = []
        # There must be at least one editor.
        with self.assertRaises(BadValueError):
            exploration.put()
        exploration.editors = [self.user]

        # Put and Retrieve the exploration.
        exploration.put()
        retrieved_exploration = Exploration.get_by_id('The exploration hash id')
        self.assertEqual(retrieved_exploration.category, 'The category')
        self.assertEqual(retrieved_exploration.init_state, state.key)
        self.assertEqual(retrieved_exploration.title, 'New exploration')
        self.assertEqual(retrieved_exploration.states, [state.key])
        self.assertEqual(retrieved_exploration.parameters, [parameter])
        self.assertEqual(retrieved_exploration.is_public, True)
        self.assertEqual(retrieved_exploration.image_id, 'A string')
        self.assertEqual(retrieved_exploration.editors, [self.user])
Пример #29
0
    def post(self, exploration_id, state_id):
        """Handles feedback interactions with readers."""
        values = {'error': []}

        exploration = Exploration.get(exploration_id)
        state = State.get(state_id, parent=exploration.key)
        old_state = state

        payload = json.loads(self.request.get('payload'))

        # The 0-based index of the last content block already on the page.
        block_number = payload.get('block_number') + 1
        # The reader's answer.
        answer = payload.get('answer')
        # The answer handler (submit, click, etc.)
        handler = payload.get('handler')

        params = payload.get('params', {})
        # Add the reader's answer to the parameter list.
        params['answer'] = answer

        dest_id, feedback, rule, recorded_answer = state.transition(
            answer, params, handler)

        if recorded_answer is not None:
            recorded_answer = json.dumps(recorded_answer)
            EventHandler.record_rule_hit(
                exploration_id, state_id, rule, recorded_answer)
            # Add this answer to the state's 'unresolved answers' list.
            if recorded_answer not in old_state.unresolved_answers:
                old_state.unresolved_answers[recorded_answer] = 0
            old_state.unresolved_answers[recorded_answer] += 1
            # TODO(sll): Make this async?
            old_state.put()

        assert dest_id

        html_output, widget_output = '', []
        # TODO(sll): The following is a special-case for multiple choice input,
        # in which the choice text must be displayed instead of the choice
        # number. We might need to find a way to do this more generically.
        if state.widget.widget_id == 'interactive-MultipleChoiceInput':
            answer = state.widget.params['choices'][int(answer)]

        # Append reader's answer.
        values['reader_html'] = feconf.JINJA_ENV.get_template(
            'reader/reader_response.html').render({'response': answer})

        if dest_id == feconf.END_DEST:
            # This leads to a FINISHED state.
            if feedback:
                html_output, widget_output = self._append_feedback(
                    feedback, html_output, widget_output, block_number, params)
            EventHandler.record_exploration_completed(exploration_id)
        else:
            state = State.get(dest_id, parent=exploration.key)
            EventHandler.record_state_hit(exploration_id, dest_id)

            if feedback:
                html_output, widget_output = self._append_feedback(
                    feedback, html_output, widget_output, block_number, params)

            # Populate new parameters.
            params = get_params(state, existing_params=params)
            # Append text for the new state only if the new and old states
            # differ.
            if old_state.id != state.id:
                state_html, state_widgets = parse_content_into_html(
                    state.content, block_number, params)
                # Separate text for the new state and feedback for the old state
                # by an additional line.
                if state_html and feedback:
                    html_output += '<br>'
                html_output += state_html
                widget_output += state_widgets

        if state.widget.widget_id in DEFAULT_ANSWERS:
            values['default_answer'] = DEFAULT_ANSWERS[state.widget.widget_id]
        values.update({
            'exploration_id': exploration.id, 'state_id': state.id,
            'oppia_html': html_output, 'widgets': widget_output,
            'block_number': block_number, 'params': params,
            'finished': (dest_id == feconf.END_DEST),
        })

        if dest_id != feconf.END_DEST:
            if state.widget.sticky and (
                    state.widget.widget_id == old_state.widget.widget_id):
                values['interactive_widget_html'] = ''
                values['sticky_interactive_widget'] = True
            else:
                values['interactive_widget_html'] = (
                    InteractiveWidget.get_raw_code(
                        state.widget.widget_id,
                        params=utils.parse_dict_with_params(
                            state.widget.params, params)
                    )
                )
        else:
            values['interactive_widget_html'] = ''

        self.render_json(values)
Пример #30
0
def reload_explorations():
    """Reload the default explorations."""
    Exploration.delete_demo_explorations()
    Exploration.load_demo_explorations()