import os
import yaml
class QuestionException ( Exception ) :
""" Exception to be thrown when there is something wrong with
the question file . """
pass
class Questions ( object ) :
""" Object holding all the questions """
QUESTION_TYPES = [ " Text " , " Image " , " Music " , " Code " , " Video " ]
QUESTION_KEYS = [ " Name " , " Question " , " Answer " , " Type " , " Double-Jeopardy " , " Audio " ]
def __init__ ( self , qfile , appendPath = False ) :
self . qfile = qfile
self . _questions = None
self . _appendPath = appendPath
self . _basedir = os . path . dirname ( qfile )
self . _read_questions ( )
def get_sections ( self ) :
return [ s [ " Section " ] for s in self . _questions ]
def get_questions ( self , section ) :
sec = filter ( lambda s : s [ " Section " ] == section , self . _questions )
if len ( sec ) < 1 :
raise ValueError ( " Section %s does not exist " % ( section , ) )
return sec [ 0 ] [ " Questions " ]
def get_question ( self , section , question ) :
if type ( question ) != int or question < 1 or question > 5 :
raise ValueError ( " question parameter needs to be an integer between 1 and 5 " )
return self . get_questions ( section ) [ question - 1 ]
def get_number_from_section ( self , section ) :
for i , s in enumerate ( self . _questions , 1 ) :
if s [ " Section " ] == section :
return i
raise ValueError ( " Section ' %s ' does not exist " % section )
def _read_questions ( self ) :
f = None
# open file
try :
f = open ( self . qfile )
except OSError as e :
raise QuestionException ( " Could not read question file: %s " % e )
# load yaml
ysrc = None
try :
ysrc = yaml . safe_load ( f . read ( ) )
except Exception as e :
raise QuestionException ( " Error parsing question file %s : %s " % ( self . qfile , e ) )
# now to check the integrity of the question file
if type ( ysrc ) is not list :
raise QuestionException ( " The questionfile has to be a list of question " )
for i , sec in enumerate ( ysrc , 1 ) :
if not " Section " in sec . keys ( ) or not " Questions " in sec . keys ( ) :
raise QuestionException ( " Section %d needs to have the keys ' Section ' and ' Question ' (case-sensitive) " % i )
for j , q in enumerate ( sec [ " Questions " ] , 1 ) :
# check for keys we need in each question
if any ( [ x not in q . keys ( ) for x in [ " Question " , " Answer " , " Type " ] ] ) :
raise QuestionException ( " Question %d from section %d ( %s ) is missing one of the keywords Question, Answer or Type " % ( j , i , sec [ " Section " ] ) )
# check for keys we do not know
for key in q . keys ( ) :
if key not in self . QUESTION_KEYS :
raise QuestionException ( " Qestion %d from section %d ( %s ) has invalid keyword ' %s ' " % ( j , i , sec [ " Section " ] , key ) )
# check Double-Jeopardy is a bool and is set to false if non-existant
if " Double-Jeopardy " not in q . keys ( ) :
q [ " Double-Jeopardy " ] = False
elif type ( q [ " Double-Jeopardy " ] ) != bool :
raise QuestionException ( " The Double-Jeopardy key from question %d from section %d ( %s ) must be either true or false " % ( j , i , sec [ " Section " ] ) )
# check Audio is a bool and is set to false if non-existant
if " Audio " not in q . keys ( ) :
q [ " Audio " ] = False
elif type ( q [ " Audio " ] ) != bool :
raise QuestionException ( " The Audio key from question %d from section %d ( %s ) must be either true or false " % ( j , i , sec [ " Section " ] ) )
# check for broken question types
if q [ " Type " ] not in self . QUESTION_TYPES :
raise QuestionException ( " Question %d from Section %d ( %s ) has an invalid type ' %s ' (valid types are %s ) " % ( j , i , sec [ " Section " ] , q [ " Type " ] , " , " . join ( self . QUESTION_TYPES ) ) )
# check if file for music/image questions exist
if q [ " Type " ] in ( " Music " , " Image " , " Video " ) :
if self . _appendPath :
q [ " Question " ] = os . path . join ( self . _basedir , q [ " Question " ] )
if not os . path . isfile ( q [ " Question " ] ) :
raise QuestionException ( " File for question %d , section %d ( %s ) not found " % ( j , i , sec [ " Section " ] ) )
# check if this section has enough questions
if j != 5 :
raise QuestionException ( " Section %d ( %s ) needs to have exactly %d questions (has %d ) " % ( i , sec [ " Section " ] , 5 , j ) )
# check for only having unique section names
sections = [ s [ " Section " ] for s in ysrc ]
if len ( sections ) != len ( set ( sections ) ) :
raise QuestionException ( " All section names must be unique " )
# done, save yaml src to _questions
self . _questions = ysrc
return True
if __name__ == ' __main__ ' :
q = Questions ( " questions/template.q " )
print ( q . get_sections ( ) )
print ( q . get_questions ( " A " ) )
print ( q . get_question ( " A " , 1 ) )