def evalf(value, n_digits=15): from mitesting.math_objects import math_object if isinstance(value,math_object): expression=value.return_expression() copy_from=value else: from sympy import sympify,SympifyError try: expression=sympify(value) except SympifyError: return value copy_from=None from mitesting.customized_commands import evalf_expression return math_object(evalf_expression(expression,n_digits), copy_parameters_from=copy_from)
def render(self, context): kwargs = dict([(k, v.resolve(context)) for k, v in self.kwargs.items()]) expression = force_text(self.expression.resolve(context)) local_dict = context["_sympy_local_dict_"] from mitesting.math_objects import math_object from mitesting.sympy_customized import parse_and_process, EVALUATE_NONE expression = parse_and_process(expression, local_dict=local_dict, evaluate_level=EVALUATE_NONE) expression=math_object(expression, **kwargs) if self.asvar: context[self.asvar] = expression return '' else: return expression
def setup_expression_context(question, rng, seed, user_responses=None, random_outcomes={}): """ Set up the question context by parsing all expressions for question. Returns context that contains all evaluated expressions with keys given by the expression names. Before evaluating expressions, initializes global dictionary with allowed sympy commands for the question. Random expressions are based on state of random instance rng set by seed. If multiple attempts are required to meet all conditions, new values of seed are randomly generated for each attempt. The successful seed is returned. The first step is to evaluate normal expressions, i.e., those that have not been flagged as post user response. user_responses is a list of dictionaries of user responses to answers embedded in the question. If any answers have been marked to be asssigned to expressions, the second step is to parse those responses using user_dict for local_dict and assign the result to the corresponding expression. The third step is to evaluate any expressions flagged as being post user response. Both the local_dict and user_dict are added to the expression context. In addition, if some expressions were EXPRESSION_WITH_ALTERNATES, then the following are created: - alternate_dicts: a list of local_dicts with different alternates - alternate_exprs: a dictionary indexed by expression name, where each entry is a list of alternate versions of the expression. This will be created starting with the first EXPRESSION_WITH_ALTERNATES, and will continue being created for all subsequent expressions. - alternate_funcs: a dictionary indexed by expression name, where each entry is a list of alternate versions of a FUNCTION. This will be created for all FUNCTIONS once the first EXPRESSION_WITH_ALTERNATES is encountered. These lists and dictionaries are added to the expression context. Return a dictionary with the following: - expression_context: a Context() with mappings from the expressions - error_in_expressions: True if encountered any errors in normal expressions - error_in_expressions_post_user: the same but for post user expressions - expression_error: dictionary of error messages from normal expressions - expression_error_post_user: the same but for post user expressions - failed_conditions: True if failed conditions for all attempts - failed_condition_message: message of which expression last failed - seed: seed used in last attempt to generate contenxt """ rng.seed(seed) max_tries=500 success=False failed_condition_message="" failed_conditions=True from mitesting.utils import get_new_seed for i in range(max_tries): if i>0: seed=get_new_seed(rng) rng.seed(seed) # remove any specifications for random outcomes # since they caused a failed condition random_outcomes.clear() expression_context = Context({}) random_group_indices={} error_in_expressions = False expression_error = {} # initialize global dictionary using the comamnds # found in allowed_sympy_commands. # Also adds standard symbols to dictionary. local_dict = question.return_sympy_local_dict() user_dict = question.return_sympy_local_dict( user_response=True) alternate_dicts = [] alternate_exprs = {} alternate_funcs = {} try: from mitesting.models import Expression # first processes the expressions that aren't flagged # as post user response for expression in question.expression_set\ .filter(post_user_response=False): try: evaluate_results=expression.evaluate( local_dict=local_dict, user_dict=user_dict, alternate_dicts = alternate_dicts, random_group_indices=random_group_indices, rng=rng, random_outcomes=random_outcomes) # on FailedCondition, reraise to stop evaluating expressions except Expression.FailedCondition: raise # for any other exception, record exception and # allow to continue processing expressions except Exception as exc: error_in_expressions = True expression_error[expression.name] = str(exc) expression_context[expression.name] = '??' if expression.expression_type == expression.RANDOM_WORD: expression_context[expression.name + "_plural"] = "??" else: # if random word, add singular and plural to context if expression.expression_type == expression.RANDOM_WORD: expression_evaluated\ =evaluate_results['expression_evaluated'] expression_context[expression.name] \ = expression_evaluated[0] expression_context[expression.name + "_plural"] \ = expression_evaluated[1] else: expression_context[expression.name] \ = evaluate_results['expression_evaluated'] # the following lists will be empty until the # first EXPRESSION_WITH_ALTERNATES is encountered alternate_exprs[expression.name] \ = evaluate_results['alternate_exprs'] alternate_funcs[expression.name] \ = evaluate_results['alternate_funcs'] the_expr = expression_context[expression.name] # if make it through all expressions without encountering # a failed condition, then record fact and # break out of loop failed_conditions = False break # on FailedCondition, continue loop, but record # message in case it is final pass through loop except Expression.FailedCondition as exc: failed_condition_message = exc.args[0] # add state to expression context as convenience to # reset state if not generating regular expression # Also, sympy global dict is accessed from template tags expression_context['_sympy_local_dict_'] = local_dict expression_context['_user_dict_'] = user_dict expression_context['_alternate_dicts_'] = alternate_dicts expression_context['_alternate_exprs_'] = alternate_exprs expression_context['_alternate_funcs_'] = alternate_funcs error_in_expressions_post_user = False expression_error_post_user = {} # if haven't failed conditions, process user responses and # expressions flagged as post user response if not failed_conditions: # next processes any user responses # that are assigned to expressions from mitesting.sympy_customized import EVALUATE_NONE from mitesting.math_objects import math_object from mitesting.sympy_customized import parse_and_process from sympy import Symbol, Dummy from mitesting.models import QuestionAnswerOption import pickle, base64 # ExpressionFromAnswer contains information about any # answers that were assigned to expressions for expression in question.expressionfromanswer_set.all(): # will assign Dummy(default_value) if no response given for answer # or if error in parsing respons default_value= re.sub('_long_underscore_', '\uff3f', expression.default_value) math_expr= Dummy(default_value) answer_number=expression.answer_number try: response=user_responses[answer_number-1] except (IndexError, TypeError): pass else: if response['code']==expression.answer_code: if expression.answer_type==\ QuestionAnswerOption.MULTIPLE_CHOICE: mc_dict=pickle.loads(base64.b64decode(expression.answer_data)) try: response_text=mc_dict[int(response['response'])] except (ValueError, KeyError): response_text=default_value math_expr=Symbol(response_text) else: try: math_expr = parse_and_process( response['response'], local_dict=user_dict, split_symbols=\ expression.split_symbols_on_compare, evaluate_level=EVALUATE_NONE, assume_real_variables=expression.real_variables, parse_subscripts = expression.parse_subscripts ) except: pass # add expression to local_dict and any alternate_dicts # that may have been created. local_dict[expression.name]=math_expr for alt_dict in alternate_dicts: alt_dict[expression.name]=math_expr # add to context expression_context[expression.name] = \ math_object(math_expr, evaluate_level=EVALUATE_NONE) # last, process expressions flagged as post user response for expression in question.expression_set\ .filter(post_user_response=True): try: evaluate_results=expression.evaluate( local_dict=local_dict, user_dict=user_dict, alternate_dicts=alternate_dicts, random_group_indices=random_group_indices, rng=rng, random_outcomes=random_outcomes) # record exception and allow to continue processing expressions except Exception as exc: error_in_expressions_post_user = True expression_error_post_user[expression.name] = str(exc) expression_context[expression.name] = '??' else: expression_context[expression.name] \ = evaluate_results['expression_evaluated'] # the following lists will be empty until the # first EXPRESSION_WITH_ALTERNATES is encountered alternate_exprs[expression.name] \ = evaluate_results['alternate_exprs'] alternate_funcs[expression.name] \ = evaluate_results['alternate_funcs'] results = { 'error_in_expressions': error_in_expressions, 'expression_error': expression_error, 'error_in_expressions_post_user': error_in_expressions_post_user, 'expression_error_post_user': expression_error_post_user, 'failed_conditions': failed_conditions, 'failed_condition_message': failed_condition_message, 'expression_context': expression_context, 'seed': seed, } return results