def check_response(self, answer, student_input, **kwargs): """Check student_input against a given answer list""" # Split the student response student_input = student_input.strip() if len(student_input) < 5: raise ConfigError('Unable to read interval from answer: "{}"'.format(student_input)) s_opening = student_input[0] s_closing = student_input[-1] s_middle = student_input[1:-1] # Ensure that the opening and closing brackets are valid if s_opening not in self.config['opening_brackets']: raise InvalidInput("Invalid opening bracket: '{}'. Valid options are: '".format(s_opening) + "', '".join(char for char in self.config['opening_brackets']) + "'.") if s_closing not in self.config['closing_brackets']: raise InvalidInput("Invalid closing bracket: '{}'. Valid options are: '".format(s_closing) + "', '".join(char for char in self.config['closing_brackets']) + "'.") # Let SingleListGrader do the grading of the middle bit middle_answer = { 'expect': answer['expect'][1:3], 'ok': answer['ok'], 'msg': answer['msg'], 'grade_decimal': answer['grade_decimal'] } result = super(IntervalGrader, self).check_response(middle_answer, s_middle, **kwargs) grade_list = result['individual'] # Grade the opening bracket self.grade_bracket(answer['expect'][0], s_opening, grade_list[0]) # Grade the closing bracket self.grade_bracket(answer['expect'][3], s_closing, grade_list[1]) # Convert the grade list to a single return result return self.process_grade_list(grade_list, 2, answer['msg'], answer['grade_decimal'])
def validate_user_integration_variable(self, varname): """Check the integration variable has no other meaning and is valid variable name""" if (varname in self.functions or varname in self.random_funcs or varname in self.constants): msg = ("Cannot use {} as integration variable; it is already has " "another meaning in this problem.") raise InvalidInput(msg.format(varname)) if not is_valid_variable_name(varname): msg = ( "Integration variable {} is an invalid variable name." "Variable name should begin with a letter and contain alphanumeric" "characters or underscores thereafter, but may end in single quotes." ) raise InvalidInput(msg.format(varname))
def validate_forbidden_strings_not_used(expr, forbidden_strings, forbidden_msg): """ Ignoring whitespace, checking that expr does not contain any forbidden_strings. Usage ===== Passes validation if no forbidden strings used: >>> validate_forbidden_strings_not_used( ... '2*sin(x)*cos(x)', ... ['*x', '+ x', '- x'], ... 'A forbidden string was used!' ... ) True Fails validation if any forbidden string is used: >>> try: ... validate_forbidden_strings_not_used( ... 'sin(x+x)', ... ['*x', '+ x', '- x'], ... 'A forbidden string was used!') ... except InvalidInput as error: ... print(error) A forbidden string was used! """ stripped_expr = expr.replace(' ', '') for forbidden in forbidden_strings: check_for = forbidden.replace(' ', '') if check_for in stripped_expr: # Don't give away the specific string that is being checked for! raise InvalidInput(forbidden_msg) return True
def validate_only_permitted_functions_used(used_funcs, permitted_functions): """ Check that the used_funcs contains only permitted_functions Arguments: used_functions ({str}): set of used function names permitted_functions ({str}): set of permitted function names Usage ===== >>> validate_only_permitted_functions_used( ... set(['f', 'sin']), ... set(['f', 'g', 'sin', 'cos']) ... ) True >>> try: ... validate_only_permitted_functions_used( ... set(['f', 'Sin', 'h']), ... set(['f', 'g', 'sin', 'cos'])) ... except InvalidInput as error: ... print(error) Invalid Input: function(s) 'Sin', 'h' not permitted in answer """ used_not_permitted = sorted( [f for f in used_funcs if f not in permitted_functions]) if used_not_permitted: func_names = ", ".join( ["'{f}'".format(f=f) for f in used_not_permitted]) message = "Invalid Input: function(s) {} not permitted in answer".format( func_names) raise InvalidInput(message) return True
def validate_only_permitted_functions_used(used_funcs, permitted_functions): """ Check that the used_funcs contains only permitted_functions Arguments: used_functions ({str}): set of used function names permitted_functions ({str}): set of permitted function names Usage ===== >>> validate_only_permitted_functions_used( ... set(['f', 'sin']), ... set(['f', 'g', 'sin', 'cos']) ... ) True >>> validate_only_permitted_functions_used( ... set(['f', 'Sin', 'h']), ... set(['f', 'g', 'sin', 'cos']) ... ) Traceback (most recent call last): InvalidInput: Invalid Input: function(s) 'h', 'Sin' not permitted in answer """ used_not_permitted = [f for f in used_funcs if f not in permitted_functions] if used_not_permitted: func_names = ", ".join(["'{f}'".format(f=f) for f in used_not_permitted]) message = "Invalid Input: function(s) {} not permitted in answer".format(func_names) raise InvalidInput(message) return True
def validate_user_dummy_variable(self, varname): """Check the dummy variable has no other meaning and is a valid variable name""" if varname in self.functions or varname in self.random_funcs or varname in self.constants: msg = ("Cannot use {varname} as {adj} variable; it is already has " "another meaning in this problem.") raise InvalidInput( msg.format(varname=varname, adj=self.wording['adjective'])) if not is_valid_variable_name(varname): msg = ( "{adj} variable {varname} is an invalid variable name." "Variable name should begin with a letter and contain alphanumeric" "characters or underscores thereafter, but may end in single quotes." ) raise InvalidInput( msg.format(varname=varname, adj=self.wording['adjective'].title()))
def validate_forbidden_strings_not_used(expr, forbidden_strings, forbidden_msg): """ Ignoring whitespace, checking that expr does not contain any forbidden_strings. expr can be a string, dictionary, or a list of strings. Usage ===== Passes validation if no forbidden strings used: >>> validate_forbidden_strings_not_used( ... '2*sin(x)*cos(x)', ... ['*x', '+ x', '- x'], ... 'A forbidden string was used!' ... ) True Fails validation if any forbidden string is used: >>> try: ... validate_forbidden_strings_not_used( ... 'sin(x+x)', ... ['*x', '+ x', '- x'], ... 'A forbidden string was used!') ... except InvalidInput as error: ... print(error) A forbidden string was used! Works on lists of strings: >>> try: ... validate_forbidden_strings_not_used( ... ['x', 'x^2'], ... ['*x', '+ x', '- x'], ... 'A forbidden string was used!') ... except InvalidInput as error: ... print(error) True And also dicts: >>> try: ... validate_forbidden_strings_not_used( ... {'upper': 'x', 'lowerl': 'x^2', 'integrand': 'x + x'}, ... ['*x', '+ x', '- x'], ... 'A forbidden string was used!') ... except InvalidInput as error: ... print(error) A forbidden string was used! """ if isinstance(expr, dict): expr = [v for k, v in expr.items()] elif not isinstance(expr, list): expr = [expr] for expression in expr: stripped_expr = expression.replace(' ', '') for forbidden in forbidden_strings: check_for = forbidden.replace(' ', '') if check_for in stripped_expr: # Don't give away the specific string that is being checked for! raise InvalidInput(forbidden_msg) return True
def construct_message(self, msg, msg_type): """ Depending on the configuration, construct the appropriate return dictionary for a bad entry or raises an error. Arguments: msg: Message to return if a message should be returned msg_type: Type of message to return ('err', 'msg', or None) """ invalid_response = {'ok': False, 'grade_decimal': 0, 'msg': ''} if msg_type == 'err': raise InvalidInput(msg) elif msg_type == 'msg' or self.config['debug']: invalid_response['msg'] = msg return invalid_response
def validate_required_functions_used(used_funcs, required_funcs): """ Raise InvalidInput error if used_funcs does not contain all required_funcs Examples: >>> validate_required_functions_used( ... ['sin', 'cos', 'f', 'g'], ... ['cos', 'f'] ... ) True >>> validate_required_functions_used( ... ['sin', 'cos', 'F', 'g'], ... ['cos', 'f'] ... ) Traceback (most recent call last): InvalidInput: Invalid Input: Answer must contain the function f """ for func in required_funcs: if func not in used_funcs: msg = "Invalid Input: Answer must contain the function {}" raise InvalidInput(msg.format(func)) return True
def validate_required_functions_used(used_funcs, required_funcs): """ Raise InvalidInput error if used_funcs does not contain all required_funcs Examples: >>> validate_required_functions_used( ... ['sin', 'cos', 'f', 'g'], ... ['cos', 'f'] ... ) True >>> try: ... validate_required_functions_used( ... ['sin', 'cos', 'F', 'g'], ... ['cos', 'f']) ... except InvalidInput as error: ... print(error) Invalid Input: Answer must contain the function f """ for func in required_funcs: if func not in used_funcs: msg = "Invalid Input: Answer must contain the function {}" raise InvalidInput(msg.format(func)) return True