def create_from_yaml(cls, yaml_file, user, title, category, exploration_id=None, image_id=None): """Creates an exploration from a YAML file.""" init_state_name = yaml_file[: yaml_file.index(":\n")] if not init_state_name or "\n" in init_state_name: raise Exception( "Invalid YAML file: the name of the initial state " "should be left-aligned on the first line and " "followed by a colon" ) exploration = cls.create( user, title, category, exploration_id=exploration_id, init_state_name=init_state_name, image_id=image_id ) init_state = State.get_by_name(init_state_name, exploration) try: exploration_dict = utils.dict_from_yaml(yaml_file) state_list = [] for state_name, state_description in exploration_dict.iteritems(): state = init_state if state_name == init_state_name else exploration.add_state(state_name) state_list.append({"state": state, "desc": state_description}) for index, state in enumerate(state_list): State.modify_using_dict(exploration, state["state"], state["desc"]) except Exception: exploration.delete() raise return exploration
def put(self, unused_user, exploration, state): """Saves updates to a state.""" payload = json.loads(self.request.get('payload')) yaml_file = payload.get('yaml_file') if yaml_file and feconf.ALLOW_YAML_FILE_UPLOAD: # The user has uploaded a YAML file. Process only this action. state = State.modify_using_dict( exploration, state, utils.dict_from_yaml(yaml_file)) self.response.write(json.dumps( get_state_for_frontend(state, exploration))) return state_name = payload.get('state_name') param_changes = payload.get('param_changes') interactive_widget = payload.get('interactive_widget') interactive_params = payload.get('interactive_params') interactive_rulesets = payload.get('interactive_rulesets') sticky_interactive_widget = payload.get('sticky_interactive_widget') content = payload.get('content') unresolved_answers = payload.get('unresolved_answers') if 'state_name' in payload: # Replace the state name with this one, after checking validity. if state_name == feconf.END_DEST: raise self.InvalidInputException('Invalid state name: END') exploration.rename_state(state, state_name) if 'param_changes' in payload: state.param_changes = [ ParamChange( name=param_change['name'], values=param_change['values'], obj_type='UnicodeString' ) for param_change in param_changes ] if interactive_widget: state.widget.widget_id = interactive_widget if interactive_params: state.widget.params = interactive_params if sticky_interactive_widget is not None: state.widget.sticky = sticky_interactive_widget if interactive_rulesets: ruleset = interactive_rulesets['submit'] utils.recursively_remove_key(ruleset, u'$$hashKey') state.widget.handlers = [AnswerHandlerInstance( name='submit', rules=[])] # This is part of the state. The rules should be put into it. state_ruleset = state.widget.handlers[0].rules # TODO(yanamal): Do additional calculations here to get the # parameter changes, if necessary. for rule_ind in range(len(ruleset)): rule = ruleset[rule_ind] state_rule = Rule() state_rule.name = rule.get('name') state_rule.inputs = rule.get('inputs') state_rule.dest = rule.get('dest') state_rule.feedback = rule.get('feedback') # Generate the code to be executed. if rule['rule'] == 'Default': # This is the default rule. assert rule_ind == len(ruleset) - 1 state_rule.name = 'Default' state_ruleset.append(state_rule) continue # Normalize the params here, then store them. classifier_func = state_rule.name.replace(' ', '') first_bracket = classifier_func.find('(') mutable_rule = rule['rule'] params = classifier_func[first_bracket + 1: -1].split(',') for index, param in enumerate(params): if param not in rule['inputs']: raise self.InvalidInputException( 'Parameter %s could not be replaced.' % param) typed_object = state.get_typed_object(mutable_rule, param) # TODO(sll): Make the following check more robust. if (not isinstance(rule['inputs'][param], basestring) or '{{' not in rule['inputs'][param] or '}}' not in rule['inputs'][param]): normalized_param = typed_object.normalize( rule['inputs'][param]) else: normalized_param = rule['inputs'][param] if normalized_param is None: raise self.InvalidInputException( '%s has the wrong type. Please replace it with a ' '%s.' % (rule['inputs'][param], typed_object.__name__)) state_rule.inputs[param] = normalized_param state_ruleset.append(state_rule) if content: state.content = [Content(type=item['type'], value=item['value']) for item in content] if 'unresolved_answers' in payload: state.unresolved_answers = {} for answer, count in unresolved_answers.iteritems(): if count > 0: state.unresolved_answers[answer] = count state.put() self.response.write(json.dumps( get_state_for_frontend(state, exploration)))