def get_answer_value(valid_answers, cur=None): print('Valid Answers:') if all([isinstance(k, int) for k in valid_answers]): for k, v in enumerate(valid_answers): if k == cur: print('[%d]' % v) else: print(' %d ' % v) if cur is None: choice = int(input('Enter a value: ')) else: choice = int(ifinput('Enter a value: ', valid_answers[cur])) try: choice = [k for k, v in enumerate(valid_answers) if v == choice][0] except IndexError: return -1 else: for k, v in enumerate(valid_answers): if k == cur: print('[%d: %s]' % (k+1, v)) else: print(' %d: %s ' % (k+1, v)) if cur is None: choice = int(input('Select an answer 1-%d:' % len(valid_answers))) else: choice = int(ifinput('Select an answer 1-%d:' % len(valid_answers), cur+1)) choice -= 1 return choice
def merge_answers(self, question, answers, merge_to=None): """ Merge one or more answer values together, keeping the sequence of answers the same. References to the merged answers in criteria or caveats will be replaced with a reference to the 'merge_to' answer. examples: self.merge_answers(53, ['low-medium', 'medium'], merge_to='medium') will change all entries with 'low-medium' answer values to 'medium'. self.merge_answers(53, ['low-medium', 'medium'], merge_to='high') will change all entries with 'low-medium' or 'medium' answer values to 'high'. If merge_to is omitted, defaults to the first answer listed in the answers argument. self.merge_answers(53, ['low-medium', 'medium']) will change all entries with 'medium' answer values to 'low-medium'. In all cases the merged answers are subsequently deleted. pandas was a really terrible choice, internally. :param question: :param answers: :param merge_to: :return: """ if isinstance(answers, str): answers = [answers] cur = self._questions[question].valid_answers ans_ind = [indices(cur, lambda x: x == ans)[0] for ans in answers] if merge_to is None: merge_ind = ans_ind[0] else: merge_ind = indices(cur, lambda x: x == merge_to)[0] mapping = range(len(cur)) for i in mapping: if i in ans_ind: mapping[i] = merge_ind print ("Merging answers into %s:" % cur[merge_ind]) for i in ans_ind: print (" %s" % cur[i]) if ifinput("Really continue?", "y") != "y": print ("NOT merged.") return new_cri, new_cav = self._remap_answers(question, mapping) self._criteria = new_cri self._caveats = new_cav for i in ans_ind: if i != merge_ind: self.delete_answer(question, cur[i])
def delete_answer(self, question, answer): """ Deletes all references to the supplied answer. If any references are found, the user will be prompted for confirmation prior to deleting them. This method has to do three things: delete criteria / caveat entries with the matching question+answer, re-map all the answers for that question to their new indices, and delete the designated answer from the question's answer list :param question: :param answer: :return: """ cur = self._questions[question].valid_answers ind = [k for k, v in enumerate(cur) if v == answer] assert len(ind) == 1, "Not enough / too many answers found" ind = ind[0] cri_index = (self._criteria['QuestionID'] == question) & (self._criteria['Threshold'] == ind) cav_index = (self._caveats['QuestionID'] == question) & (self._caveats['Answer'] == ind) check = False if len(cri_index.loc[cri_index]) > 0: print('Matching Criteria:') print(self._criteria.loc[cri_index]) check = True if len(cav_index.loc[cav_index]) > 0: print('Matching Caveats:') print(self._caveats.loc[cav_index]) check = True if check: if ifinput('Really delete this answer?', 'y') != 'y': print('NOT deleted.') return a = range(len(cur)) mapping = a[:ind] + [None] + a[ind:-1] new_cri, new_cav = self._remap_answers(question, mapping) new_cri = new_cri.loc[~cri_index] new_cav = new_cav[~cav_index] # 'atomic' update del cur[ind] self._criteria = new_cri self._caveats = new_cav
def delete_answer(self, question, answer): """ Deletes all references to the supplied answer. If any references are found, the user will be prompted for confirmation prior to deleting them. This method has to do three things: delete criteria / caveat entries with the matching question+answer, re-map all the answers for that question to their new indices, and delete the designated answer from the question's answer list :param question: :param answer: :return: """ cur = self._questions[question].valid_answers ind = [k for k, v in enumerate(cur) if v == answer] assert len(ind) == 1, "Not enough / too many answers found" ind = ind[0] cri_index = (self._criteria["QuestionID"] == question) & (self._criteria["Threshold"] == ind) cav_index = (self._caveats["QuestionID"] == question) & (self._caveats["Answer"] == ind) check = False if len(cri_index.loc[cri_index]) > 0: print ("Matching Criteria:") print (self._criteria.loc[cri_index]) check = True if len(cav_index.loc[cav_index]) > 0: print ("Matching Caveats:") print (self._caveats.loc[cav_index]) check = True if check: if ifinput("Really delete this answer?", "y") != "y": print ("NOT deleted.") return a = range(len(cur)) mapping = a[:ind] + [-1] + a[ind:-1] # -1 gets deleted below new_cri, new_cav = self._remap_answers(question, mapping) new_cri = new_cri.loc[~cri_index] new_cav = new_cav[~cav_index] # 'atomic' update del cur[ind] # aha! delete by reference! self._criteria = new_cri self._caveats = new_cav