Quiz based learning system in Python Description:

I am building a quiz based learning system in Python. The idea is once a day you have to give this test to improve your learning, it's different from regular rote learning. The user will be shown question first and optionally series of hints, the user then can match his answer to the original answer and honestly gives score to his attempt. I didn't go for programmatically checking the answers because it would be difficult to check semantics of two strings.

Goal:

The end goal is to provide a flexible way to add new questions and hints and add new question types. I am not favour of very fancy UI but something
which feels intuitive should work.

In the long run, I will be integrating it with Slack as a bot.

Code:

from enum import Enum

class Category(Enum):
  CS, MATH, PUZZLE = range(3)

class Score(Enum):
  BAD, OK, GOOD = range(3)

data = [{
  'question': 'What does the fox say?',
  'hints': ['not the sound of cat', 'not the sound of dog'],
  'answer': 'Its a mystery',
  'category': Category.PUZZLE,
  'score': Score.BAD
}, {
  'question': 'What is the worst case run time of binary search of n elements?',
  'hints': [
    'Think about the best case',
    'Would need to search exhaustively until one element'],
  'answer': 'Log(n) base 2',
  'category': Category.CS,
  'score': Score.BAD
}];

def runQuiz(questions, category):
  total = 0
  needHint = '/h'
  for q in filter(lambda q: q['category'] == category, questions):
    answer = input(q['question'])
    if answer == needHint:
      for hint in q['hints']:
        moreHints = input(hint)
        if moreHints != needHint:
          break
    print('Correct answer is: {0}'.format(q['answer']))
    rating = int(input('Rate your solution(1-3): '))
    q.update({'score': rating})
    total += rating
  print('You are done for today, your score is {0}'.format(total))
  return input('Want to start again?(y/*)')

def selectCategory():
  category = input("""
    Select category
      1. Computer Science
      2. Mathematics
      3. Puzzles
      4. Random (Default)
  """)
  selectedCategory = {
    1: Category.CS,
    2: Category.MATH,
    3: Category.PUZZLE
  }.get(int(category), Category.CS)
  return selectedCategory

def main():
  print('Make sure you are not feeling thirsty')
  input()
  print('Clean your surrounding')
  input()
  print('Remember your goal')
  input()
  print('If you need any hint type /h')
  input()
  print('Lets go')
  selectedCategory = selectCategory()
  print('You have selected {0} category'.format(selectedCategory.name))
  print('')
  shouldStartAgain = runQuiz(data, selectedCategory)
  if shouldStartAgain == 'y':
    main()
  else:
    print('You are done for today, sleep well!')

if __name__ == '__main__':
  main()


Note:

Although the current solution doesn't meet all the requirements but it is complete in itself as its fully functional code. Its in early stage of development so I would like to take proper design decisions as soon as possible.

TODO:

Find a way to persist the questions with updated score.
Make the questions with lower score come first.

Questions:


Is there any better way to deal with terminal?
Keeping in mind that I would like to use this system as web service too, what design decisions would be helpful?
It may be too broad, but what good programming practices should I follow, for example when and where to choose objects, enums etc?
Expanding a bit on what the first answer mentioned: There are no Quiz, Question etc. classes.

Your current code has this data:

data = [{
  'question': 'What does the fox say?',
  'hints': ['not the sound of cat', 'not the sound of dog'],
  'answer': 'Its a mystery',
  'category': Category.PUZZLE,
  'score': Score.BAD
}, {
  'question': 'What is the worst case run time of binary search of n elements?',
  'hints': [
    'Think about the best case',
    'Would need to search exhaustively until one element'],
  'answer': 'Log(n) base 2',
  'category': Category.CS,
  'score': Score.BAD
}];


So it is a list of objects of this:

Question - String
Hints - array of String
Answer - String
Category - enum value
Score - enum value


This looks like the start of a Question class. But wait, how/why does a Question have a Score? What does it mean that the score for a question is "Bad"? That depends on how you answer that question, doesn't it? I would remove that from this data, that value belongs elsewhere. You could possibly have some "points" integer value in the data to indicate how many points a question is worth though.

You say that you are not looking to make your program give the points, but the user to honestly give the points. This is good, and reminds me about Anki Flashcards (although Anki doesn't have hints AFAIK).


  TODO: Find a way to persist the questions with updated score. Make the questions with lower score come first.


Ah, this is where the score comes in again. It's important to distinguish between a Question and the user's answer to a question. Maybe your system should support multiple users that should be able to answer different things? Then you need to connect each question that a user has answered to a score.

So, besides the Question class that I've already mentioned, you could have some more things:


UserAnswer (contains reference to Question and to a Score), a list of UserAnswers is basically a user's progress, so you could load/save multiple of these for each "Lesson" a user has ongoing/completed.
Quiz (contains a list of question, might not be necessary though as you have a category so you could filter the list of all questions based on a category)
Category can be a string instead of an enum, as I don't see a reason to programmatically hard-code a limit of possible categories


Also, I would strongly recommend to keep your data as a file or in a database and not inside your code.