def _display_choices(self, choices): """Prints a mapping of numbers to choices and returns the mapping as a dictionary. """ print("Choose the number of the correct choice:") choice_map = {} for i, choice in enumerate(choices): i = str(i) print('{}) {}'.format(i, format.indent(choice, ' ' * (len(i) + 2)).strip())) choice = format.normalize(choice) choice_map[i] = choice return choice_map
def interact(self, unique_id, case_id, question_prompt, answer, choices=None, randomize=True, *, normalizer=lambda x: x): """Reads student input for unlocking tests until the student answers correctly. PARAMETERS: unique_id -- str; the ID that is recorded with this unlocking attempt. case_id -- str; the ID that is recorded with this unlocking attempt. question_prompt -- str; the question prompt answer -- list; a list of locked lines in a test case answer. choices -- list or None; a list of choices. If None or an empty list, signifies the question is not multiple choice. randomize -- bool; if True, randomizes the choices on first invocation. DESCRIPTION: Continually prompt the student for an answer to an unlocking question until one of the folliwng happens: 1. The student supplies the correct answer, in which case the supplied answer is returned 2. The student aborts abnormally (either by typing 'exit()' or using Ctrl-C/D. In this case, return None Correctness is determined by the verify method. RETURNS: list; the correct solution (that the student supplied). Each element in the list is a line of the correct output. """ if randomize and choices: choices = random.sample(choices, len(choices)) correct = False while not correct: if choices: assert len(answer) == 1, 'Choices must have 1 line of output' choice_map = self._display_choices(choices) question_timestamp = datetime.now() input_lines = [] for line_number, line in enumerate(answer): if len(answer) == 1: prompt = self.PROMPT else: prompt = '(line {}){}'.format(line_number + 1, self.PROMPT) student_input = format.normalize(self._input(prompt)) self._add_history(student_input) if student_input in self.EXIT_INPUTS: raise EOFError if choices and student_input in choice_map: student_input = choice_map[student_input] correct_answer = self._verify_student_input(student_input, line, normalizer) if correct_answer: input_lines.append(correct_answer) else: input_lines.append(student_input) break else: correct = True tg_id = -1 misU_count_dict = {} rationale = "Unknown - Default Value" if not correct: guidance_data = self.guidance_util.show_guidance_msg(unique_id, input_lines, self.hash_key) misU_count_dict, tg_id, printed_msg, rationale = guidance_data else: rationale = self.guidance_util.prompt_with_prob() print("-- OK! --") printed_msg = ["-- OK! --"] self.analytics.append({ 'id': unique_id, 'case_id': case_id, 'question timestamp': self.unix_time(question_timestamp), 'answer timestamp': self.unix_time(datetime.now()), 'prompt': question_prompt, 'answer': input_lines, 'correct': correct, 'treatment group id': tg_id, 'rationale': rationale, 'misU count': misU_count_dict, 'printed msg': printed_msg }) print() return input_lines
def _hash_fn(self, text): text = format.normalize(text) return locking.lock(self.assignment.name, text)
def _hash_fn(self, text): text = format.normalize(text) return hmac.new(self.assignment.name.encode('utf-8'), text.encode('utf-8')).hexdigest()
def interact(self, unique_id, case_id, question_prompt, answer, choices=None, randomize=True): """Reads student input for unlocking tests until the student answers correctly. PARAMETERS: unique_id -- str; the ID that is recorded with this unlocking attempt. case_id -- str; the ID that is recorded with this unlocking attempt. question_prompt -- str; the question prompt answer -- list; a list of locked lines in a test case answer. choices -- list or None; a list of choices. If None or an empty list, signifies the question is not multiple choice. randomize -- bool; if True, randomizes the choices on first invocation. DESCRIPTION: Continually prompt the student for an answer to an unlocking question until one of the folliwng happens: 1. The student supplies the correct answer, in which case the supplied answer is returned 2. The student aborts abnormally (either by typing 'exit()' or using Ctrl-C/D. In this case, return None Correctness is determined by the verify method. RETURNS: list; the correct solution (that the student supplied). Each element in the list is a line of the correct output. """ if randomize and choices: choices = random.sample(choices, len(choices)) correct = False while not correct: if choices: assert len(answer) == 1, 'Choices must have 1 line of output' choice_map = self._display_choices(choices) question_timestamp = datetime.now() input_lines = [] for line_number, line in enumerate(answer): if len(answer) == 1: prompt = self.PROMPT else: prompt = '(line {}){}'.format(line_number + 1, self.PROMPT) student_input = format.normalize(self._input(prompt)) self._add_history(student_input) if student_input in self.EXIT_INPUTS: raise EOFError if choices and student_input in choice_map: student_input = choice_map[student_input] input_lines.append(student_input) if not self._verify(student_input, line): # Try to evaluate student answer as Python expression and # use the result as the answer. try: eval_input = repr(eval(student_input, {}, {})) if not self._verify(eval_input, answer[line_number]): break # Replace student_input with evaluated input. input_lines[-1] = eval_input except Exception as e: # Incorrect answer. break else: correct = True self.analytics.append({ 'id': unique_id, 'case_id': case_id, 'question timestamp': self.unix_time(question_timestamp), 'answer timestamp': self.unix_time(datetime.now()), 'prompt': question_prompt, 'answer': input_lines, 'correct': correct, }) if not correct: print("-- Not quite. Try again! --") else: print("-- OK! --") print() return input_lines
def interact(self, answer, choices=None, randomize=True): """Reads student input for unlocking tests until the student answers correctly. PARAMETERS: answer -- list; a list of locked lines in a test case answer. choices -- list or None; a list of choices. If None or an empty list, signifies the question is not multiple choice. DESCRIPTION: Continually prompt the student for an answer to an unlocking question until one of the folliwng happens: 1. The student supplies the correct answer, in which case the supplied answer is returned 2. The student aborts abnormally (either by typing 'exit()' or using Ctrl-C/D. In this case, return None Correctness is determined by the verify method. RETURNS: list; the correct solution (that the student supplied). Each element in the list is a line of the correct output. """ # attempts = 0 if randomize and choices: choices = random.sample(choices, len(choices)) correct = False while not correct: # attempts += 1 if choices: assert len(answer) == 1, 'Choices must have 1 line of output' choice_map = self._display_choices(choices) input_lines = [] for i in range(len(answer)): if len(answer) == 1: prompt = self.PROMPT else: prompt = '(line {}){}'.format(i + 1, self.PROMPT) student_input = format.normalize(self._input(prompt)) self._add_history(student_input) if student_input in self.EXIT_INPUTS: raise EOFError if choices and student_input in choice_map: student_input = choice_map[student_input] if not self._verify(student_input, answer[i]): break else: input_lines.append(student_input) else: correct = True # TODO(albert): record analytis # Performt his before the function exits? # self._analytics[self._analytics['current']].append((attempts, correct)) # if input_lines.lower() in self.EXIT_INPUTS: # attempts -= 1 # self._analytics[self._analytics['current']].append((attempts, correct)) # return if not correct: print("-- Not quite. Try again! --") else: print("-- OK! --") print() # self._analytics[self._analytics['current']].append((attempts, correct)) return input_lines
def interact(self, unique_id, case_id, question_prompt, answer, choices=None, randomize=True): """Reads student input for unlocking tests until the student answers correctly. PARAMETERS: unique_id -- str; the ID that is recorded with this unlocking attempt. case_id -- str; the ID that is recorded with this unlocking attempt. question_prompt -- str; the question prompt answer -- list; a list of locked lines in a test case answer. choices -- list or None; a list of choices. If None or an empty list, signifies the question is not multiple choice. randomize -- bool; if True, randomizes the choices on first invocation. DESCRIPTION: Continually prompt the student for an answer to an unlocking question until one of the folliwng happens: 1. The student supplies the correct answer, in which case the supplied answer is returned 2. The student aborts abnormally (either by typing 'exit()' or using Ctrl-C/D. In this case, return None Correctness is determined by the verify method. RETURNS: list; the correct solution (that the student supplied). Each element in the list is a line of the correct output. """ if randomize and choices: choices = random.sample(choices, len(choices)) correct = False while not correct: if choices: assert len(answer) == 1, 'Choices must have 1 line of output' choice_map = self._display_choices(choices) question_timestamp = datetime.now() input_lines = [] for line_number, line in enumerate(answer): if len(answer) == 1: prompt = self.PROMPT else: prompt = '(line {}){}'.format(line_number + 1, self.PROMPT) student_input = format.normalize(self._input(prompt)) self._add_history(student_input) if student_input in self.EXIT_INPUTS: raise EOFError if choices and student_input in choice_map: student_input = choice_map[student_input] correct_answer = self._verify_student_input(student_input, line) if correct_answer: input_lines.append(correct_answer) else: input_lines.append(student_input) break else: correct = True tg_id = -1 misU_count_dict = {} rationale = "Unknown - Default Value" if not correct: guidance_data = self.guidance_util.show_guidance_msg(unique_id, input_lines, self.hash_key) misU_count_dict, tg_id, printed_msg, rationale = guidance_data else: rationale = self.guidance_util.prompt_with_prob() print("-- OK! --") printed_msg = ["-- OK! --"] self.analytics.append({ 'id': unique_id, 'case_id': case_id, 'question timestamp': self.unix_time(question_timestamp), 'answer timestamp': self.unix_time(datetime.now()), 'prompt': question_prompt, 'answer': input_lines, 'correct': correct, 'treatment group id': tg_id, 'rationale': rationale, 'misU count': misU_count_dict, 'printed msg': printed_msg }) print() return input_lines