def test_parse_and_process(self):
     x=Symbol('x')
     y=Symbol('y')
     expr = parse_and_process("2x")
     self.assertEqual(2*x, expr)
     expr = parse_and_process("xy", split_symbols=True)
     self.assertEqual(expr, x*y)
     expr = parse_and_process("var1*var2", 
                              local_dict = {'var1':x, 'var2': y})
     self.assertEqual(expr, x*y)
 def test_parse_and_process_evaluate_level(self):
     from sympy import Derivative
     local_dict = {'Derivative': Derivative}
     x=Symbol('x')
     y=Symbol('y')
     expr = parse_and_process("x+2+y*x+x*y", 
                              evaluate_level=EVALUATE_NONE)
     self.assertNotEqual(expr, 2+x+x)
     self.assertEqual(repr(expr), 'x + 2 + y*x + x*y')
     
     expr = parse_and_process("Derivative(x^2,x)", local_dict= local_dict,
                              evaluate_level = EVALUATE_PARTIAL)
     self.assertEqual(Derivative(x**2,x), expr)
     expr = parse_and_process("Derivative(x^2,x)", local_dict= local_dict,
                              evaluate_level = EVALUATE_FULL)
     self.assertEqual(2*x, expr)
     expr = parse_and_process("Derivative(x^2,x)", local_dict= local_dict)
     self.assertEqual(2*x, expr)
Example #3
0
    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
Example #4
0
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