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)
def post(self, widget_id): """Handles POST requests, for parameterized widgets.""" payload = json.loads(self.request.get('payload')) logging.info(payload) params = 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 = 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.response.write(json.dumps({'widget': response}))
def test_parse_dict_with_params(self): """Test parse_dict_with_params method.""" parsed_dict = utils.parse_dict_with_params({'a': 'b'}, {}, '') self.assertEqual(parsed_dict, {'a': '\\"b\\"'}) parsed_dict = utils.parse_dict_with_params({'a': '{{b}}'}, {}, 'def') self.assertEqual(parsed_dict, {'a': '\\"def\\"'}) parsed_dict = utils.parse_dict_with_params({'a': '{{b}}'}, {'b': 3}, '') self.assertEqual(parsed_dict, {'a': '\\"3\\"'}) parsed_dict = utils.parse_dict_with_params( {'a': '{{b}}'}, {'b': 'c'}, '') self.assertEqual(parsed_dict, {'a': '\\"c\\"'}) # Test that the original dictionary is unchanged. orig_dict = {'a': '{{b}}'} parsed_dict = utils.parse_dict_with_params(orig_dict, {'b': 'c'}, '') self.assertEqual(orig_dict, {'a': '{{b}}'}) self.assertEqual(parsed_dict, {'a': '\\"c\\"'})
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)
def post(self, exploration_id, state_id): """Handles feedback interactions with readers.""" values = {'error': []} exploration = Exploration.get(exploration_id) state = State.get(state_id, exploration) 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') params = self.get_params(state, payload.get('params')) # The reader's answer. answer = payload.get('answer') # Add the reader's answer to the parameter list. This must happen before # the interactive widget is constructed. params['answer'] = answer interactive_widget_properties = ( InteractiveWidget.get_with_params( state.widget.widget_id)['actions']['submit']) dest_id, feedback, rule, recorded_answer = state.transition( answer, params, interactive_widget_properties) if 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 == 'MultipleChoiceInput': answer = state.widget.params['choices'][int(answer)] # Append reader's answer. values['reader_html'] = feconf.JINJA_ENV.get_template( '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, exploration) EventHandler.record_state_hit(exploration_id, state_id) if feedback: html_output, widget_output = self.append_feedback( feedback, html_output, widget_output, block_number, 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.append(state_widgets) if state.widget.widget_id in DEFAULT_ANSWERS: values['default_answer'] = DEFAULT_ANSWERS[state.widget.widget_id] values['exploration_id'] = exploration.id values['state_id'] = state.id values['oppia_html'] = html_output values['widgets'] = widget_output values['block_number'] = block_number + 1 values['params'] = params if dest_id != feconf.END_DEST: values['finished'] = False 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['finished'] = True values['interactive_widget_html'] = '' self.response.write(json.dumps(values))
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)