Esempio n. 1
0
 def tearDown(self):
     """Deletes all widgets and explorations."""
     InteractiveWidget.delete_all_widgets()
     explorations = exp_services.get_all_explorations()
     for exploration in explorations:
         exploration.delete()
     super(ExplorationDomainUnitTests, self).tearDown()
Esempio n. 2
0
    def setUp(self):
        """Loads the default widgets and creates dummy users."""
        super(ExplorationServicesUnitTests, self).setUp()
        InteractiveWidget.load_default_widgets()

        self.owner_id = "*****@*****.**"
        self.editor_id = "*****@*****.**"
        self.viewer_id = "*****@*****.**"
Esempio n. 3
0
def reload_widgets():
    """Reload the default classifiers and widgets."""
    Classifier.delete_all_classifiers()
    Classifier.load_default_classifiers()

    Widget.delete_all_widgets()
    InteractiveWidget.load_default_widgets()
    NonInteractiveWidget.load_default_widgets()
Esempio n. 4
0
    def setUp(self):
        """Loads the default widgets and creates a sample exploration."""
        super(StateModelUnitTests, self).setUp()
        InteractiveWidget.load_default_widgets()

        self.user_id = '*****@*****.**'

        self.exploration = Exploration.get(exp_services.create_new(
            self.user_id, 'A title', 'A category', 'A exploration_id'))
Esempio n. 5
0
    def test_put_method(self):
        """Test that put() only works when called on a Widget subclass."""
        widget = Widget(
            id='WidgetName', name='Widget Name', category='Category', template='Template')
        with self.assertRaises(NotImplementedError):
            widget.put()

        widget = InteractiveWidget(
            id='WidgetName', name='Widget Name', category='Category', template='Template',
            handlers=[AnswerHandler()])
        widget.put()
        InteractiveWidget.delete_all_widgets()
Esempio n. 6
0
    def test_loading_and_deletion_of_widgets(self):
        """Test loading and deletion of the default widgets."""
        self.assertEqual(InteractiveWidget.objects.count(), 0)
        self.assertEqual(NonInteractiveWidget.objects.count(), 0)

        InteractiveWidget.load_default_widgets()

        self.assertEqual(InteractiveWidget.objects.count(), 8)
        self.assertEqual(NonInteractiveWidget.objects.count(), 0)

        InteractiveWidget.delete_all_widgets()
        self.assertEqual(InteractiveWidget.objects.count(), 0)
        self.assertEqual(NonInteractiveWidget.objects.count(), 0)
Esempio n. 7
0
def get_state_for_frontend(state, exploration):
    """Returns a representation of the given state for the frontend."""

    state_repr = exp_services.export_state_to_dict(exploration.id, state.id)
    modified_state_dict = exp_services.export_state_internals_to_dict(
        exploration.id, state.id, human_readable_dests=True)

    # TODO(sll): The following is for backwards-compatibility and should be
    # deleted later.
    rules = {}
    for handler in state_repr['widget']['handlers']:
        rules[handler['name']] = handler['rules']
        for item in rules[handler['name']]:
            if item['name'] == 'Default':
                item['rule'] = 'Default'
            else:
                item['rule'] = InteractiveWidget.get(
                    state.widget.widget_id).get_readable_name(
                        handler['name'], item['name']
                    )
    state_repr['widget']['rules'] = rules
    state_repr['widget']['id'] = state_repr['widget']['widget_id']

    state_repr['yaml'] = utils.yaml_from_dict(modified_state_dict)
    return state_repr
Esempio n. 8
0
 def get(self, widget_id):
     """Handles GET requests."""
     try:
         self.render_json({
             'widget': InteractiveWidget.get_with_params(widget_id, {}),
         })
     except:
         raise self.PageNotFoundException
Esempio n. 9
0
    def _pre_put_hook(self):
        """Ensures that the widget and at least one handler for it exists."""
        # Every state should have an interactive widget.
        if not self.widget:
            self.widget = self.get_default_widget()
        elif not self.widget.handlers:
            self.widget.handlers = [self.get_default_handler()]
        # TODO(sll): Do other validation.

        # Add the corresponding AnswerHandler classifiers for easy reference.
        widget = InteractiveWidget.get(self.widget.widget_id)
        for curr_handler in self.widget.handlers:
            for w_handler in widget.handlers:
                if w_handler.name == curr_handler.name:
                    curr_handler.classifier = w_handler.classifier
Esempio n. 10
0
    def test_parameterized_widget(self):
        """Test that parameterized widgets are correctly handled."""
        self.assertEqual(InteractiveWidget.objects.count(), 0)

        Classifier.load_default_classifiers()
        InteractiveWidget.load_default_widgets()

        MUSIC_STAFF_ID = 'interactive-MusicStaff'

        widget = InteractiveWidget.get(MUSIC_STAFF_ID)
        self.assertEqual(widget.id, MUSIC_STAFF_ID)
        self.assertEqual(widget.name, 'Music staff')

        code = InteractiveWidget.get_raw_code(MUSIC_STAFF_ID)
        self.assertIn('GLOBALS.noteToGuess = JSON.parse(\'\\"', code)

        code = InteractiveWidget.get_raw_code(MUSIC_STAFF_ID, {'noteToGuess': 'abc'})

        self.assertIn('GLOBALS.noteToGuess = JSON.parse(\'abc\');', code)

        # The get_with_params() method cannot be called directly on Widget.
        # It must be called on a subclass.
        with self.assertRaises(AttributeError):
            parameterized_widget_dict = Widget.get_with_params(
                MUSIC_STAFF_ID, {'noteToGuess': 'abc'})
        with self.assertRaises(NotImplementedError):
            parameterized_widget_dict = Widget._get_with_params(
                MUSIC_STAFF_ID, {'noteToGuess': 'abc'})

        parameterized_widget_dict = InteractiveWidget.get_with_params(
            MUSIC_STAFF_ID, {'noteToGuess': 'abc'})
        self.assertItemsEqual(parameterized_widget_dict.keys(), [
            'id', 'name', 'category', 'description', 'template',
            'static_template', 'params', 'handlers', 'raw'])
        self.assertEqual(parameterized_widget_dict['id'], MUSIC_STAFF_ID)
        self.assertIn('GLOBALS.noteToGuess = JSON.parse(\'abc\');',
                      parameterized_widget_dict['raw'])
        self.assertEqual(parameterized_widget_dict['params'],
                         {'noteToGuess': 'abc'})
        InteractiveWidget.delete_all_widgets()
Esempio n. 11
0
def modify_using_dict(exploration_id, state_id, sdict):
    """Modifies the properties of a state using values from a dict."""
    exploration = Exploration.get(exploration_id)
    state = exploration.get_state_by_id(state_id)

    state.content = [
        Content(type=item['type'], value=item['value'])
        for item in sdict['content']
    ]

    state.param_changes = []
    for pc in sdict['param_changes']:
        instance = get_or_create_param(
            exploration_id, pc['name'], obj_type=pc['obj_type'])
        instance.values = pc['values']
        state.param_changes.append(instance)

    wdict = sdict['widget']
    state.widget = WidgetInstance(
        widget_id=wdict['widget_id'], sticky=wdict['sticky'],
        params=wdict['params'], handlers=[])

    # Augment the list of parameters in state.widget with the default widget
    # params.
    for wp in InteractiveWidget.get(wdict['widget_id']).params:
        if wp.name not in wdict['params']:
            state.widget.params[wp.name] = wp.value

    for handler in wdict['handlers']:
        handler_rules = [Rule(
            name=rule['name'],
            inputs=rule['inputs'],
            dest=State._get_id_from_name(rule['dest'], exploration),
            feedback=rule['feedback']
        ) for rule in handler['rules']]

        state.widget.handlers.append(AnswerHandlerInstance(
            name=handler['name'], rules=handler_rules))

    state.put()
    return state
Esempio n. 12
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
        # 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': exploration.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, exploration.init_state_id)
Esempio n. 13
0
    def post(self, widget_id):
        """Handles POST requests, for parameterized widgets."""
        params = self.payload.get('params', {})
        if isinstance(params, list):
            new_params = {}
            for item in params:
                new_params[item['name']] = item['default_value']
            params = new_params

        state_params_dict = {}
        state_params_given = self.payload.get('state_params')
        if state_params_given:
            for param in state_params_given:
                # Pick a random parameter for each key.
                state_params_dict[param['name']] = (
                    utils.get_random_choice(param['values']))

        response = InteractiveWidget.get_with_params(
            widget_id, params=utils.parse_dict_with_params(
                params, state_params_dict)
        )

        self.render_json({'widget': response})
Esempio n. 14
0
 def setUp(self):
     """Loads the default widgets."""
     super(ExplorationDataUnitTests, self).setUp()
     InteractiveWidget.load_default_widgets()
Esempio n. 15
0
 def tearDown(self):
     """Deletes the dummy users and any other widgets and explorations."""
     InteractiveWidget.delete_all_widgets()
     explorations = exp_services.get_all_explorations()
     for exploration in explorations:
         exploration.delete()
Esempio n. 16
0
 def tearDown(self):
     self.user.delete()
     InteractiveWidget.delete_all_widgets()
     explorations = exp_services.get_all_explorations()
     for exploration in explorations:
         exploration.delete()
Esempio n. 17
0
 def setUp(self):
     self.user = User(email='*****@*****.**')
     self.user.save()
     InteractiveWidget.load_default_widgets()
Esempio n. 18
0
    def test_pre_put_validation(self):
        """Test pre-put checks for widget handlers."""
        widget = InteractiveWidget(
            id='WidgetName', name='Widget Name', category='Category', template='Template')
        with self.assertRaises(ValidationError):
            widget.handlers = []
            widget.put()

        with self.assertRaises(ValidationError):
            widget.handlers = [AnswerHandler(), AnswerHandler()]
            widget.put()

        widget.handlers = [
            AnswerHandler(name='click'), AnswerHandler(name='click')]
        with self.assertRaises(ValidationError):
            widget.put()

        widget.handlers = [
            AnswerHandler(name='submit'), AnswerHandler(name='click')]
        widget.put()
        InteractiveWidget.delete_all_widgets()
Esempio n. 19
0
    def test_required_properties(self):
        """Test validation of required widget properties."""
        widget = InteractiveWidget(id='Widget Name', name='Widget Name')
        with self.assertRaises(ValidationError):
            widget.put()

        widget.category = 'Category'
        with self.assertRaises(ValidationError):
            widget.put()

        widget.template = 'Template'
        with self.assertRaises(ValidationError):
            widget.put()

        widget.handlers = [AnswerHandler()]
        widget.put()
        InteractiveWidget.delete_all_widgets()
Esempio n. 20
0
    def post(self, exploration_id, state_id):
        """Handles feedback interactions with readers."""
        values = {}

        exploration = Exploration.get(exploration_id)
        old_state = exploration.get_state_by_id(state_id)

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

        params = self.payload.get('params', {})
        params['answer'] = answer

        rule = old_state.classify(handler, answer, params)
        new_state_id = rule.dest
        feedback = rule.get_feedback_string()

        recorded_answer = answer
        # TODO(sll): This is a special case for multiple-choice input
        # which should really be handled generically.
        if old_state.widget.widget_id == 'interactive-MultipleChoiceInput':
            recorded_answer = old_state.widget.params['choices'][int(answer)]

        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()

        html_output, widget_output = '', []
        old_params = params

        if new_state_id == feconf.END_DEST:
            # This leads to a FINISHED state.
            new_state = None
            if feedback:
                html_output, widget_output = self._append_feedback(
                    feedback, html_output, widget_output, block_number,
                    old_params)
            EventHandler.record_exploration_completed(exploration_id)
        else:
            new_state = exploration.get_state_by_id(new_state_id)
            EventHandler.record_state_hit(exploration_id, new_state_id)

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

            # Populate new parameters.
            params = get_params(new_state, existing_params=old_params)
            # Append text for the new state only if the new and old states
            # differ.
            if old_state.id != new_state.id:
                state_html, state_widgets = parse_content_into_html(
                    new_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

        # Render the response in the customized html if
        # - the response is not rendered in the sticky interactive widget, and
        # - there is a static rendering html provided for that widget.
        sticky = (
            new_state_id != feconf.END_DEST and
            new_state.widget.sticky and
            new_state.widget.widget_id == old_state.widget.widget_id
        )
        custom_response = ''
        if not sticky:
            response_params = utils.parse_dict_with_params(
                old_state.widget.params, old_params)
            response_params['answer'] = old_params['answer']
            response_params['iframe_content'] = False
            custom_response = InteractiveWidget.get_raw_static_code(
                old_state.widget.widget_id, response_params)

            if custom_response:
                response_params['iframe_content'] = True
                values['response_iframe'] = (
                    InteractiveWidget.get_raw_static_code(
                        old_state.widget.widget_id, response_params)
                )

        # Append reader's answer.
        response = custom_response if custom_response else answer
        if sticky:
            response = ''
        values['reader_html'] = self.jinja2_env.get_template(
            'reader/reader_response.html').render({
                'response': response,
                'custom_response': bool(custom_response),
            })

        if new_state_id != feconf.END_DEST and new_state.widget.widget_id in DEFAULT_ANSWERS:
            values['default_answer'] = DEFAULT_ANSWERS[new_state.widget.widget_id]
        values['state_id'] = new_state_id
        values.update({
            'exploration_id': exploration_id,
            'oppia_html': html_output, 'widgets': widget_output,
            'block_number': block_number, 'params': params,
            'finished': (new_state_id == feconf.END_DEST),
        })

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

        self.render_json(values)