def _chooseFile(self): """_chooseFile internal function Presents the user with a list of flashcard files in the current directory and asks them to select one of them. It then loads that file into a FlashcardSet and sets that internal variable. """ # Find all the flashcard files in the current directory validfilepattern = re.compile(setnameToFilename('(.*)')) allfiles = os.listdir() validfiles = [] for f in allfiles: match = validfilepattern.match(f) if match: validfiles.append(match.group(1)) filechoices = dict(enumerate(validfiles, 1)) # Ask the user which file to load, providing an option for adding a new file choice = ConsoleMenu.static_quickChoice( filechoices, { 'manual': True, 'manual_value': 'Create new file', 'manual_prompt': 'New file name: ' }) setname = choice[1] filename = setnameToFilename(setname) # Now unpickle the file, or initialize a default flashcard set if setname not in validfiles: self._set = FlashcardSet(setname) else: with open(filename, 'rb') as pickledfile: self._set = pickle.load(pickledfile)
def _chooseAnswer(self): """_chooseAnswer internal function Helper function to clean up run function. Lets user choose answer to change, rearrange answers, or add new answer. """ choices = dict(enumerate(self._card._valid_answers, 1)) choices['c'] = 'Change correct answer' choices['d'] = 'Delete an answer' options = { 'manual': True, 'manual_key': 'a', 'manual_value': 'Add new answer', 'manual_prompt': 'Enter new answer: ', 'abstain': True, 'abstain_key': 'q', 'abstain_value': 'Return to edit card menu', 'intro_text': 'Choose one of the answers, or change the correct answer' } choice = ConsoleMenu.static_quickChoice(choices, options) # If the user chooses to return to the edit card menu, nothing needs be done if choice == None: pass # If the user wants to add a new answer, just add it to the list elif choice[0] == 'a': self._card._valid_answers.append(choice[1]) # If the user wants to change the correct answer, convert to int and rearrange list elif choice[0] == 'c': inputtext = input('Enter key for correct answer (integer between 1 and ' \ +str(len(self._card._valid_answers))+'): ') # By default, do nothing (if the user entered an invalid value, screw 'em) newcorrect = None try: newcorrect = int(inputtext) - 1 except: pass # Move the new index to the front of the list, if it's a valid index if newcorrect != None and newcorrect >= 0 \ and newcorrect < len(self._card._valid_answers): self._card._valid_answers.insert( 0, self._card._valid_answers.pop(newcorrect)) # If the user wants to delete an answer, remove it from the list elif choice[0] == 'd': inputtext = input('Enter key for the answer to delete: ') deleteindex = None try: deleteindex = int(inputtext) - 1 except: pass # Delete the item, if it's a valid index if deleteindex != None and deleteindex >= 0 \ and deleteindex < len(self._card._valid_answers): del self._card._valid_answers[deleteindex] # If the user picked one of the answers, allow them to edit it else: answerindex = int(choice[0]) - 1 self._changeAnswer(answerindex)
def run(self): """run function Displays a menu of a few possible options and allows the user to pick among them. Includes importing a CSV, manually editing cards, running the cards, and displaying stats. """ choices = { 'i': "Import Cards", 'e': "Export Cards", 'c': "Edit cards", 'o': "Edit options", 'r': "Run cards", 's': "Show stats" } options = { 'abstain': True, 'abstain_key': 'q', 'abstain_value': "Quit" } choice = ConsoleMenu.static_quickChoice(choices, options) # If the user wants to exit, set the next to None and do nothing else if choice == None: self.next = None else: # Call the appropriate state, based on the user's choice if choice[0] == 'i': self.next = FCStateImportCSV(self._set, self) elif choice[0] == 'e': self._exportCSV() elif choice[0] == 'c': self.next = FCStateEditCardsMain(self._set, self) elif choice[0] == 'o': print('Go to ' + choice[1]) elif choice[0] == 'r': self.next = FCStateRunCardsMenu(self._set, self) elif choice[0] == 's': self._showStats()
def run(self): """run function Displays a menu of a few possible attributes to edit. Question, answers, confirm override, and answer type. """ choices = { 'u': 'Change question text (current: "' + self._card._question + '")', 'a': 'Change answers (current: "' + str(self._card._valid_answers) + '")', 't': 'Change answer type (current: "' + self._card._answer_type + '")', 'c': 'Change confirm override (current: "' + str(self._card._override_confirms) + '")' } options = { 'abstain': True, 'abstain_key': 'q', 'abstain_value': "Return to edit cards main menu", } choice = ConsoleMenu.static_quickChoice(choices, options) # If the user wants to return to main menu, set the next and do nothing else if choice == None: self.next = self._editcardsmain else: # Run a helper function based on the user choice if choice[0] == 'u': self._changeQuestionText() elif choice[0] == 'a': self._chooseAnswer() elif choice[0] == 't': self._changeAnswerType() elif choice[0] == 'c': self._changeOverrideConfirm()
def _changeOverrideConfirm(self): """_changeOverrideConfirm internal function Helper function to clean up run function. Lets user change override confirm. """ choices = {'1': True, '2': False} options = { 'abstain': True, 'abstain_key': 'q', 'abstain_value': 'Return to edit card menu', 'intro_text': 'Choose an override confirm setting (current: ' + str(self._card._override_confirms) + ')' } choice = ConsoleMenu.static_quickChoice(choices, options) # If the user wants to quit, nothing needs be done if choice == None: pass # Otherwise, set the new override confirm else: self._card._override_confirms = choice[1]
def _changeAnswerType(self): """_changeAnswerType internal function Helper function to clean up run function. Lets user change answer type. """ choices = dict(enumerate(FlashcardSet.ANSWER_TYPES, 1)) options = { 'abstain': True, 'abstain_key': 'q', 'abstain_value': 'Return to edit card menu', 'intro_text': 'Choose an answer type (current: ' + self._card._answer_type + ')' } choice = ConsoleMenu.static_quickChoice(choices, options) # If the user wants to quit, nothing needs be done if choice == None: pass # Otherwise, set the new answer type else: self._card._answer_type = FlashcardSet.ANSWER_TYPES[int(choice[0]) - 1]
def _askMultipleChoice(self, question, answers): """_askMultipleChoice internal function Simply presents the user with the question and a choice menu for an answer. Returns the answer the user chose. """ print("Question: " + question) # If the question is boolean, generate a choices dict of just True and False if len(answers) == 1 and answers[0] in ('True', 'False'): choices = {'1': 'True', '2': 'False'} # Otherwise, the answers are just what was passed to us # Make sure to randomize the order else: choices = dict( enumerate(sorted(answers, key=lambda k: random.random()), 1)) options = { 'abstain': True, 'abstain_key': 'q', 'abstain_value': 'Return to menu' } choice = ConsoleMenu.static_quickChoice(choices, options) if choice == None: return 'q' return choice[1]
def run(self): """run function Displays a menu of a few possible options and allows the user to pick among them. Includes a list of cards (shows question), an option to add a new card, and an option to jump back to the main menu. """ questionlist = [] cardlist = [] for card in self._set.getAllCards(): questionlist.append(card._question) cardlist.append(card) choices = dict(enumerate(questionlist, 1)) options = { 'abstain': True, 'abstain_key': 'q', 'abstain_value': "Return to main menu", 'manual': True, 'manual_key': 'n', 'manual_value': "Enter a new question", 'manual_prompt': "Enter the new question: " } choice = ConsoleMenu.static_quickChoice(choices, options) # If the user wants to return to main menu, set the next and do nothing else if choice == None: self.next = self._mainmenu else: # If they entered a new question if choice[0] == 'n': # Grab the new question question = choice[1] # Grab the answers from the user valid_answers = [] while len(valid_answers) == 0: print("For freeform questions, enter the correct answer.") print( "For True/False questions, enter 'True' or 'False' (case/spelling sensitive!)." ) print( "For multiple-choice questions, enter all possible answers, starting with the correct answer." ) print( "To stop entering answers, hit enter without typing text." ) inputtext = None while inputtext != "": inputtext = input("Enter answer: ") if inputtext != "": valid_answers.append(inputtext) # If the user did not enter any answers, yell at them if len(valid_answers) == 0: print("ERROR: At least one answer must be entered.") # From those two, we can guess the rest of the attributes to create a new card card = FlashcardCard(question, valid_answers) # Replace any existing card with the same question self._set.addCard(card, True) # We've added the card, so let's jump back to the menu again self.next = self # They chose one of the existing cards else: # Pull the card from the cardlist; note that the choice key is int by design; # We also need to subtract 1 because we added 1 for the choice menu card = cardlist[int(choice[0]) - 1] # Send the user to the Edit Card state self.next = FCStateEditCard(self, card)
def run(self): """run function Displays the number of cards in the set. Displays a menu of possible runs, with an option to customize. Then switches to the RunCardList state to actually run. """ choices = { '1': "Run the lowest-scoring card", '5': "Run 5 lowest-scoring cards", '10': "Run 10 lowest-scoring cards", '10r': "Run 10 random cards", '15': "Run 10 lowest-scoring cards with 5 random cards", 'third': "Run the lowest-scoring 1/3rd of the cards", 'half': "Run the lowest-scoring 1/2 of the cards", 'all': "Run all the cards in the order they were added", 'endless': "Run the lowest-ranking card in the set until you quit", 'custom': "Define a custom run of the cards" } options = { 'abstain':True, 'abstain_key':'q', 'abstain_value':'Return to main menu', 'intro_text':str(len(self._set.getAllCards()))+" cards in set. " \ +"Choose a run from the list below" } choice = ConsoleMenu.static_quickChoice(choices, options) if choice == None: self.next = self._mainmenu return # Put together a list of cards to run, based on the user choice cardlist = [] if choice[0] == '1': cardlist = self._set.getSortedCards() elif choice[0] == '5': cardlist = self._set.getSortedCards(5) elif choice[0] == '10': cardlist = self._set.getSortedCards(10) elif choice[0] == '10r': cardlist = self._set.getSortedCards(0, 10) elif choice[0] == '15': cardlist = self._set.getSortedCards(10, 5) elif choice[0] == 'third': cardlist = self._set.getSortedCards( int(len(self._set.getAllCards()) / 3)) elif choice[0] == 'half': cardlist = self._set.getSortedCards( int(len(self._set.getAllCards()) / 2)) elif choice[0] == 'all': cardlist = self._set.getAllCards()[:] elif choice[0] == 'endless': # Endless starts with just one card cardlist = self._set.getSortedCards() elif choice[0] == 'custom': inputranked = input( "Enter the number of lowest-scoring cards to run: ") inputrandom = input("Enter the number of random cards to run: ") numranked = None numrandom = None try: numranked = int(inputranked) numrandom = int(inputrandom) except: print( "ERROR: One of the inputs above was not an integer. Returning to run menu." ) return cardlist = self._set.getSortedCards(numranked, numrandom) # Randomize the order of the cards, so we're not always going by rank cardlist.sort(key=lambda k: random.random()) # We now have a cardlist, so kick off the run # If in endless mode, pass in the full flashcard set as well, to indicate so if choice[0] == 'endless': self.next = FCStateRunCardList(cardlist, self._mainmenu, self._set, endless) else: self.next = FCStateRunCardList(cardlist, self._mainmenu, self._set)
def run(self): """run function Displays a list of .csv files in the directory, allows the user to choose one of them, and then imports the questions from that .csv. """ # Provide the user with instructions print( "This process imports a list of questions and valid/correct answers from a CSV" ) print( "file. Columns should be separated by the ~ symbol. The first column is the" ) print( "question. The second column is the correct answer. For multiple-choice questions," ) print( "you can specify the other choices in the 3rd, 4th, 5th, etc. columns." ) print( "The question type (boolean, word, freeform, multiple-choice, etc.) is" ) print( "automatically determined based on the form of the answers, but it can be manually" ) print("changed later by editing the cards.") print() # Find all the CSV files in the current directory validfilepattern = re.compile('.*\.[Cc][Ss][Vv]') allfiles = os.listdir() validfiles = [] for f in allfiles: match = validfilepattern.match(f) if match: validfiles.append(f) filechoices = dict(enumerate(validfiles, 1)) # Ask the user which file to load, providing an option to cancel choice = ConsoleMenu.static_quickChoice( filechoices, { 'abstain': True, 'abstain_key': 'q', 'abstain_value': 'Cancel Card Import' }) if choice == None: self.next = self._mainmenu return # Now that we have a filename, let's read it in filename = choice[1] with open(filename, 'r') as csvfile: csvreader = csv.reader(csvfile, delimiter='~') linenumber = 0 questionsimported = 0 for row in csvreader: linenumber += 1 if len(row) < 2: print("Error on line "+str(linenumber)+": Not enough fields. Need at " \ +"least 2 (question and correct answer).") continue else: question = row[0].strip() valid_answers = [] for answer in row[1:]: if answer.strip() != "": valid_answers.append(answer.strip()) cardadded = self._set.addCard( FlashcardCard(question, valid_answers)) if cardadded: questionsimported += 1 else: print("Warning on line "+str(linenumber)+": Question already exists " \ +"in flashcard set: "+question) print(str(questionsimported) + " questions successfully imported") # We're done importing, return to the main menu self.next = self._mainmenu