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, }))
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)
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))
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)
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()
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), [])
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')
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')
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 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_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)
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'))
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)
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')
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()))
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)
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()
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)
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})
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()
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)
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 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 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) )
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]
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
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])
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)
def reload_explorations(): """Reload the default explorations.""" Exploration.delete_demo_explorations() Exploration.load_demo_explorations()