def ensure_operation(op_name, root=None): if root is None: root = parse_program() result = find_operation(op_name, root) if not result: gently("You are not using the <code>{}</code> operator.<br><br><i>(missing_op)<i>".format(op_name)) return result
def prevent_operation(op_name, root=None): if root is None: root = parse_program() result = find_operation(op_name, root) if result != False: gently("You may not use the <code>{}</code> operator.".format(op_name)) return result
def ensure_cisc108_tests(test_count, report=None): student = compatibility.get_student_data() if 'assert_equal' not in student.data: gently("You have not imported assert_equal from the cisc108 module.") return False assert_equal = student.data['assert_equal'] if not hasattr(assert_equal, 'student_tests'): gently( "The assert_equal function has been modified. Do not let it be overwritten!", label="Assertion Function Corrupted") return False student_tests = assert_equal.student_tests if student_tests.tests == 0: gently("You are not unit testing the result.", label="No Student Unit Tests") return False elif student_tests.tests < test_count: gently("You have not written enough unit tests.", label="Not Enough Student Unit Tests") return False elif student_tests.failures > 0: gently("Your unit tests are not passing.", label="Student Unit Tests Failing") return False return True
def prevent_operation(op_name, root=None): if root is None: root = parse_program() result = find_operation(op_name, root) if result: gently("You may not use the <code>{}</code> operator.<br><br><i>(bad_op)<i>".format(op_name)) return result
def check_author_name_on_header(): code = get_program() m_author = re.search('Author: \\w+', code) if not m_author: gently( "You need to add your name to the author field at the top of the file." "<br><br><i>(name_missing)<i></br></br>")
def check_print_output(multiple_lines): for line in multiple_lines: if line not in get_output(): gently( "You are not doing the correct calculation<br><br><i>(catch_all)<i></br></br>" ) return True
def ensure_operation(op_name, root=None): if root is None: root = parse_program() result = find_operation(op_name, root) if result == False: gently( "You are not using the <code>{}</code> operator.".format(op_name)) return result
def ensure_operation(op_name, root=None): if root is None: root = parse_program() result = find_operation(op_name, root) if not result: gently( "You are not using the <code>{}</code> operator.<br><br><i>(missing_op)<i>" .format(op_name)) return result
def no_nested_function_definitions(): ast = parse_program() defs = ast.find_all('FunctionDef') for a_def in defs: if not is_top_level(a_def): gently( "You have defined a function inside of another block. For instance, you may have placed it inside another function definition, or inside of a loop. Do not nest your function definition!" ) return False return True
def no_nested_function_definitions(): ast = parse_program() defs = ast.find_all('FunctionDef') for a_def in defs: if not is_top_level(a_def): gently("You have defined a function inside of another block. For instance, you may have placed it inside " "another function definition, or inside of a loop. Do not nest your function definition!" "<br><br><i>(nest_func)<i>") return False return True
def check_output_on_header(expected_output): code = get_program() expected_output = str(expected_output) between_stars = code.split("*****")[2].strip() between_stars = "\\n".join([x.strip() for x in between_stars.split("\\n")]) if 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM' in between_stars: gently("In your code, you need to 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM'") elif not expected_output in between_stars: gently("The output you copied between the *****, seems to be incorrect. You may have copied it into the wrong " "location, or it is incomplete.")
def check_output_on_header(expected_output): code = get_program() expected_output = str(expected_output) between_stars = code.split("*****")[2].strip() between_stars = "\\n".join([x.strip() for x in between_stars.split("\\n")]) if 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM' in between_stars: gently("In your code, you need to 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM'" "<br><br><i>(wrong_output_blank)<i></br></br>") elif expected_output not in between_stars: gently("The output you copied between the *****, seems to be incorrect. You may have copied it into the wrong " "location, or it is incomplete.<br><br><i>(wrong_output_fill)<i></br></br>")
def prevent_unused_result(): ast = parse_program() exprs = ast.find_all('Expr') for expr in exprs: if expr.value.ast_name == "Call": a_call = expr.value if a_call.func.ast_name == 'Attribute': if a_call.func.attr == 'append': pass elif a_call.func.attr in ('replace', 'strip', 'lstrip', 'rstrip'): gently("Remember! You cannot modify a string directly. Instead, you should assign the result back " "to the string variable.<br><br><i>(str_mutate)<i>")
def ensure_correct_plot(function_name): for a_plot, label in PLOT_LABEL.items(): if function_name == a_plot: if not function_is_called(function_name): gently("You are not calling the <code>{func_name}</code> function." "<br><br><i>(no_{func_name}_call)<i>".format(func_name=function_name)) return True elif function_is_called(a_plot): gently("You have called the <code>{}</code> function, which makes a {}." "<br><br><i>(wrong_plt)<i>".format(a_plot, label)) return True return False
def match_signature(name, length, *parameters): ast = parse_program() defs = ast.find_all('FunctionDef') for a_def in defs: if a_def._name == name: found_length = len(a_def.args.args) if found_length < length: gently( "The function named <code>{}</code> has fewer parameters ({}) than expected ({})." "<br><br><i>(insuff_args)<i>".format( name, found_length, length)) elif found_length > length: gently( "The function named <code>{}</code> has more parameters ({}) than expected ({})." "<br><br><i>(excess_args)<i>".format( name, found_length, length)) elif parameters: for parameter, arg in zip(parameters, a_def.args.args): arg_name = get_arg_name(arg) if arg_name != parameter: gently( "Error in definition of <code>{}</code>. Expected a parameter named {}, instead " "found {}.<br><br><i>(name_missing)<i>".format( name, parameter, arg_name)) return None else: return a_def else: return a_def else: gently("No function named <code>{name}</code> was found." "<br><br><i>(missing_func_{name})<i>".format(name=name)) return None
def match_signature(name, length, *parameters): ast = parse_program() defs = ast.find_all('FunctionDef') for a_def in defs: if a_def._name == name: found_length = len(a_def.args.args) if found_length < length: gently("The function named <code>{}</code> has fewer parameters ({}) than expected ({}). " "<br><br><i>(insuff_args)<i>".format(name, found_length, length)) elif found_length > length: gently("The function named <code>{}</code> has more parameters ({}) than expected ({}). " "<br><br><i>(excess_args)<i>".format(name, found_length, length)) elif parameters: for parameter, arg in zip(parameters, a_def.args.args): arg_name = get_arg_name(arg) if arg_name != parameter: gently("Error in definition of <code>{}</code>. Expected a parameter named {}, instead " "found {}. <br><br><i>(name_missing)<i>".format(name, parameter, arg_name)) return None else: return a_def else: return a_def else: gently("No function named <code>{name}</code> was found." "<br><br><i>(missing_func_{name})<i>".format(name=name)) return None
def all_documented(): ast = parse_program() defs = ast.find_all('FunctionDef') + ast.find_all("ClassDef") for a_def in defs: if a_def.name == "__init__": continue if (a_def.body and (a_def.body[0].ast_name != "Expr" or a_def.body[0].value.ast_name != "Str")): if a_def.ast_name == 'FunctionDef': gently("You have an undocumented function: " + a_def.name) else: gently("You have an undocumented class: " + a_def.name) return False return True
def prevent_unused_result(): ast = parse_program() exprs = ast.find_all('Expr') for expr in exprs: if expr.value.ast_name == "Call": a_call = expr.value if a_call.func.ast_name == 'Attribute': if a_call.func.attr == 'append': pass elif a_call.func.attr in ('replace', 'strip', 'lstrip', 'rstrip'): gently( "Remember! You cannot modify a string directly. Instead, you should assign the result back " "to the string variable.<br><br><i>(str_mutate)<i>")
def all_documented(): ast = parse_program() defs = ast.find_all('FunctionDef') + ast.find_all("ClassDef") for a_def in defs: if a_def.name == "__init__": continue if (a_def.body and (a_def.body[0].ast_name != "Expr" or a_def.body[0].value.ast_name != "Str")): if a_def.ast_name == 'FunctionDef': gently("You have an undocumented function: " + a_def.name) else: gently("You have an undocumented class: " + a_def.name) return False return True
def ensure_correct_plot(function_name): for a_plot, label in PLOT_LABEL.items(): if function_name == a_plot: if not function_is_called(function_name): gently( "You are not calling the <code>{func_name}</code> function." "<br><br><i>(no_{func_name}_call)<i>".format( func_name=function_name)) return True elif function_is_called(a_plot): gently( "You have called the <code>{}</code> function, which makes a {}." "<br><br><i>(wrong_plt)<i>".format(a_plot, label)) return True return False
def ensure_coverage(percentage=.5, destructive=False, report=None): ''' Note that this avoids destroying the current sandbox instance stored on the report, if there is one present. Args: destructive (bool): Whether or not to remove the sandbox. ''' if report is None: report = MAIN_REPORT student_code = report['source']['code'] unexecuted_lines, percent_covered = check_coverage(report) if unexecuted_lines: if percent_covered <= percentage: gently("Your code coverage is not adequate. You must cover at least half your code to receive feedback.") return False return True
def all_labels_present(): # TODO: make sure it's before the show, maybe check for default values """ plt.title("Distribution of Number of Sentences in Long Books") plt.xlabel("Number of Sentences") plt.ylabel("Number of Long Books") plt.show() Returns: """ match = find_match("plt.title(___)\nplt.show()") match02 = find_match("plt.xlabel(___)\nplt.show()") match03 = find_match("plt.ylabel(___)\nplt.show()") if (not match) or (not match02) or (not match03): gently('Make sure you supply labels to all your axes and provide a title and then call show' '<br><br><i>(labels_present)<i></br>') return True return False
def ensure_coverage(percentage=.5, destructive=False, report=None): ''' Note that this avoids destroying the current sandbox instance stored on the report, if there is one present. Args: destructive (bool): Whether or not to remove the sandbox. ''' if report is None: report = MAIN_REPORT student_code = report['source']['code'] unexecuted_lines, percent_covered = check_coverage(report) if unexecuted_lines: if percent_covered <= percentage: gently( "Your code coverage is not adequate. You must cover at least half your code to receive feedback." ) return False return True
def all_labels_present( ): # TODO: make sure it's before the show, maybe check for default values """ plt.title("Distribution of Number of Sentences in Long Books") plt.xlabel("Number of Sentences") plt.ylabel("Number of Long Books") plt.show() Returns: """ match = find_match("plt.title(___)\nplt.show()") match02 = find_match("plt.xlabel(___)\nplt.show()") match03 = find_match("plt.ylabel(___)\nplt.show()") if (not match) or (not match02) or (not match03): gently( 'Make sure you supply labels to all your axes and provide a title and then call show' '<br><br><i>(labels_present)<i></br>') return True return False
def ensure_cisc108_tests(test_count, report=None): student = compatibility.get_student_data() if 'assert_equal' not in student.data: gently("You have not imported assert_equal from the cisc108 module.") return False assert_equal = student.data['assert_equal'] if not hasattr(assert_equal, 'student_tests'): gently("The assert_equal function has been modified. Do not let it be overwritten!", label="Assertion Function Corrupted") return False student_tests = assert_equal.student_tests if student_tests.tests == 0: gently("You are not unit testing the result.", label="No Student Unit Tests") return False elif student_tests.tests < test_count: gently("You have not written enough unit tests.", label="Not Enough Student Unit Tests") return False elif student_tests.failures > 0: gently("Your unit tests are not passing.", label="Student Unit Tests Failing") return False return True
def unit_test(name, *tests): """ Show a table :param name: :param tests: :return: """ student = compatibility.get_student_data() if name in student.data: the_function = student.data[name] if callable(the_function): result = ( "<table class='blockpy-feedback-unit table table-condensed table-bordered table-hover'>" "<tr class='active'><th></th><th>Arguments</th><th>Returned</th><th>Expected</th></tr>" ) success = True success_count = 0 for test in tests: inp = test[:-1] inputs = ', '.join( ["<code>{}</code>".format(repr(i)) for i in inp]) out = test[-1] tip = "" if isinstance(out, tuple): tip = out[1] out = out[0] message = ("<td><code>{}</code></td>" * 3) test_out = the_function(*inp) message = message.format(inputs, repr(test_out), repr(out)) if (isinstance(out, float) and isinstance(test_out, (float, int)) and abs(out - test_out) < DELTA): message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 elif out != test_out: # gently(message) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 result += message if success: return the_function else: result = "I ran your function <code>{}</code> on some new arguments, " \ "and it failed {}/{} tests.".format(name, len(tests) - success_count, len(tests)) + result gently(result + "</table>") return None else: gently( "You defined {}, but did not define it as a function.".format( name)) return None else: gently("The function <code>{}</code> was not defined.".format(name)) return None
def check_pool_exam(name, questions, force=None, seed=None): _setup_questions(MAIN_REPORT) # Choose a question if force is None: if seed is None: force = MAIN_REPORT['questions']['seed'] if isinstance(force, str): force = _name_hash(force + name) else: force = seed elif isinstance(force, str): force = _name_hash(force + name) question = questions[force % len(questions)] # Ask it show_question(question['instructions']) # Check if they're done if 'settings' not in question: question['settings'] = {} question['settings'][SETTING_SHOW_CASE_DETAILS] = False results = list(load_question(question)) if results: message, label = results[0] gently(message, label=label)
def unit_test(name, *tests): """ Show a table :param name: :param tests: :return: """ student = compatibility.get_student_data() if name in student.data: the_function = student.data[name] if callable(the_function): result = ("<table class='blockpy-feedback-unit table table-condensed table-bordered table-hover'>" "<tr class='active'><th></th><th>Arguments</th><th>Returned</th><th>Expected</th></tr>" ) success = True success_count = 0 for test in tests: inp = test[:-1] inputs = ', '.join(["<code>{}</code>".format(repr(i)) for i in inp]) out = test[-1] tip = "" if isinstance(out, tuple): tip = out[1] out = out[0] message = ("<td><code>{}</code></td>" * 3) test_out = the_function(*inp) message = message.format(inputs, repr(test_out), repr(out)) if (isinstance(out, float) and isinstance(test_out, (float, int)) and abs(out - test_out) < DELTA): message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 elif out != test_out: # gently(message) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 result += message if success: return the_function else: result = "I ran your function <code>{}</code> on some new arguments, " \ "and it failed {}/{} tests.".format(name, len(tests) - success_count, len(tests)) + result gently(result + "</table>") return None else: gently("You defined {}, but did not define it as a function.".format(name)) return None else: gently("The function <code>{}</code> was not defined.".format(name)) return None
def ensure_prints(count): prints = find_function_calls('print') if not prints: gently("You are not using the print function!<br><br><i>(no_print)<i>") return False elif len(prints) > count: gently("You are printing too many times!<br><br><i>(multiple_print)<i>") return False elif len(prints) < count: gently("You are not printing enough things!<br><br><i>(too_few_print)<i>") return False else: for a_print in prints: if not is_top_level(a_print): gently("You have a print function that is not at the top level. That is incorrect for this problem!" "<br><br><i>(not_top_level_print)<i>") return False return prints
def check_author_name_on_header(): code = get_program() m_author = re.search('Author: \\w+', code) if not m_author: gently("You need to add your name to the author field at the top of the file." "<br><br><i>(name_missing)<i></br></br>")
def check_print_output(multiple_lines): for line in multiple_lines: if line not in get_output(): gently("You are not doing the correct calculation<br><br><i>(catch_all)<i></br></br>") return True
def output_test(name, *tests): student = compatibility.get_student_data() if name in student.data: the_function = student.data[name] if callable(the_function): result = ( "<table class='blockpy-feedback-unit table table-condensed table-bordered table-hover'>" "<tr class='active'><th></th><th>Arguments</th><th>Expected</th><th>Actual</th></tr>" ) success = True success_count = 0 for test in tests: inp = test[:-1] inputs = ', '.join( ["<code>{}</code>".format(repr(i)) for i in inp]) out = test[-1] tip = "" if isinstance(out, tuple): tip = out[1] out = out[0] message = "<td><code>{}</code></td>" + ( "<td><pre>{}</pre></td>" * 2) test_out = compatibility.capture_output(the_function, *inp) if isinstance(out, str): if len(test_out) < 1: message = message.format(inputs, repr(out), "<i>No output</i>", tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False elif len(test_out) > 1: message = message.format(inputs, repr(out), "<i>Too many outputs</i>", tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False elif out not in test_out: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 elif out != test_out: if len(test_out) < 1: message = message.format(inputs, repr(out), "<i>No output</i>", tip) else: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 result += message if success: return the_function else: result = ( "I ran your function <code>{}</code> on some new arguments, and it gave the wrong output " "{}/{} times.".format(name, len(tests) - success_count, len(tests)) + result) gently(result + "</table>") return None else: gently("You defined {}, but did not define it as a function." "<br><br><i>(not_func_def)<i>".format(name)) return None else: gently( "The function <code>{}</code> was not defined.<br><br><i>(no_func_def)<i>" .format(name)) return None
def gently_r(message, code, line=None, tldr="explain"): gently(message + "<br><br><i>({})<i></br></br>".format(code), line, label=tldr) return message
def ensure_show(): if not function_is_called("show"): gently("You have not called <code>show</code> function, which " "actually creates the graph.<br><br><i>(no_show)<i>") return True return False
def output_test(name, *tests): student = compatibility.get_student_data() if name in student.data: the_function = student.data[name] if callable(the_function): result = ("<table class='blockpy-feedback-unit table table-condensed table-bordered table-hover'>" "<tr class='active'><th></th><th>Arguments</th><th>Expected</th><th>Actual</th></tr>" ) success = True success_count = 0 for test in tests: inp = test[:-1] inputs = ', '.join(["<code>{}</code>".format(repr(i)) for i in inp]) out = test[-1] tip = "" if isinstance(out, tuple): tip = out[1] out = out[0] message = "<td><code>{}</code></td>" + ("<td><pre>{}</pre></td>" * 2) test_out = compatibility.capture_output(the_function, *inp) if isinstance(out, str): if len(test_out) < 1: message = message.format(inputs, repr(out), "<i>No output</i>", tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False elif len(test_out) > 1: message = message.format(inputs, repr(out), "<i>Too many outputs</i>", tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False elif out not in test_out: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 elif out != test_out: if len(test_out) < 1: message = message.format(inputs, repr(out), "<i>No output</i>", tip) else: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = message.format(inputs, repr(out), repr(test_out[0]), tip) message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 result += message if success: return the_function else: result = ("I ran your function <code>{}</code> on some new arguments, and it gave the wrong output " "{}/{} times.".format(name, len(tests) - success_count, len(tests)) + result) gently(result + "</table>") return None else: gently("You defined {}, but did not define it as a function." "<br><br><i>(not_func_def)<i>".format(name)) return None else: gently("The function <code>{}</code> was not defined.<br><br><i>(no_func_def)<i>".format(name)) return None
def check_print_output(multiple_lines): for line in multiple_lines: if line not in get_output(): gently("You are not doing the correct calculation")
def unit_test(name, *tests): """ Show a table :param name: :param tests: :return: """ student = compatibility.get_student_data() if name in student.data: the_function = student.data[name] if callable(the_function): result = TEST_TABLE_UNITS success = True success_count = 0 for test in tests: inp = test[:-1] inputs = ', '.join( ["<code>{}</code>".format(repr(i)) for i in inp]) out = test[-1] tip = "" if isinstance(out, tuple): tip = out[1] out = out[0] message = ("<td><code>{}</code></td>" * 3) ran = True try: test_out = the_function(*inp) except Exception as e: message = message.format(inputs, str(e), repr(out)) message = "<tr class=''>" + RED_X + message + "</tr>" success = False ran = False if not ran: result += message continue message = message.format(inputs, repr(test_out), repr(out)) if (isinstance(out, float) and isinstance(test_out, (float, int)) and abs(out - test_out) < DELTA): message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 elif out != test_out: # gently(message) message = "<tr class=''>" + RED_X + message + "</tr>" if tip: message += "<tr class='table-info'><td colspan=4>" + tip + "</td></tr>" success = False else: message = "<tr class=''>" + GREEN_CHECK + message + "</tr>" success_count += 1 result += message if success: return the_function else: result = "I ran your function <code>{}</code> on some new arguments, " \ "and it failed {}/{} tests.".format(name, len(tests) - success_count, len(tests)) + result gently_r(result + "</table>", "tests_failed") return None else: gently( "You defined {}, but did not define it as a function.".format( name)) return None else: gently("The function <code>{}</code> was not defined.".format(name)) return None
def ensure_show(): if not function_is_called("show"): gently("You have not called <code>show</code> function, which " "actually creates the graph.<br><br><i>(no_show)<i>") return True return False
def gently_r(message, code, line=None, tldr="explain"): gently(message + "<br><br><i>({})<i></br></br>".format(code), line, label=tldr) return message