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): """Handles POST requests.""" payload = json.loads(self.request.get('payload')) feedback = payload.get('feedback') url = payload.get('url_params').get('url') url = urllib.unquote_plus(url) EventHandler.record_feedback_submitted(url, feedback)
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')
def test_single_default_rule_hit(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_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'], 1) self.assertEquals(states[0]['state_id'], exp.init_state.get().id)
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)
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)
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)
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)