import os import random import yaml import json import sqlite3 from datetime import datetime import questions import database # =========================================================================== def read_configuration(filename, debug=False, show_points=False, show_hints=False, practice=False, save_answers=False): # FIXME validar se ficheiros e directorios existem??? if not os.path.isfile(filename): print('Cannot find file "%s"' % filename) os.sys.exit(1) with open(filename, 'r') as f: test = yaml.load(f) # defaults: test['ref'] = str(test.get('ref', filename)) test['title'] = str(test.get('title', '')) test['show_hints'] = bool(test.get('show_hints', show_hints)) test['show_points'] = bool(test.get('show_points', show_points)) test['practice'] = bool(test.get('practice', practice)) test['debug'] = bool(test.get('debug', debug)) test['save_answers'] = bool(test.get('save_answers', save_answers)) if test['save_answers']: if 'answers_dir' not in test: raise exception('Missing "answers_dir" in the test configuration.') if not os.path.isdir(test['answers_dir']): print(' * Directory "%s" does not exist. Creating...' % test['answers_dir']) os.mkdir(test['answers_dir']) if 'database' not in test: print(' * Missing database in the test configuration.') os.sys.exit(1) if isinstance(test['files'], str): test['files'] = [test['files']] # replace ref,points by actual questions from pool pool = questions.QuestionsPool() pool.add_from_files(test['files']) for i, q in enumerate(test['questions']): # each question is a list of alternative versions, even if the list # contains only one element if isinstance(q, str): # questions: some_ref --> questions: [{'ref':'some_ref', 'points':1.0, ...}] test['questions'][i] = [pool[q]] test['questions'][i][0]['points'] = 1.0 elif isinstance(q, dict): if isinstance(q['ref'], str): q['ref'] = [q['ref']] p = float(q.get('points', 1.0)) l = [] for r in q['ref']: qq = pool[r] qq['points'] = p l.append(qq) test['questions'][i] = l return test # =========================================================================== class Test(dict): # ----------------------------------------------------------------------- def __init__(self, d): super().__init__(d) qlist = [] for i, qq in enumerate(self['questions']): q = random.choice(qq) # select from alternative versions qlist.append(questions.create_question(q)) # create instance self['questions'] = qlist self['start_time'] = datetime.now() # ----------------------------------------------------------------------- def update_answers(self, ans): '''given a dictionary ans={'ref':'some answer'} updates the answers of the test. FIXME: check if answer is to be corrected or not ''' for i, q in enumerate(self['questions']): if q['ref'] in ans: if q['type'] == 'checkbox' and ans[q['ref']] == None: # HACK: checkboxes in html return none instead of an empty list # we have to manualy replace by [] q['answer'] = [] else: q['answer'] = ans[q['ref']] # ----------------------------------------------------------------------- def correct(self): '''Corrects all the answers and computes the final grade.''' self['finish_time'] = datetime.now() total_points = 0.0 final_grade = 0.0 for q in self['questions']: final_grade += q.correct() * q['points'] total_points += q['points'] final_grade = 20.0 * max(final_grade / total_points, 0.0) self['grade'] = final_grade return final_grade # ----------------------------------------------------------------------- def save_json(self, path): filename = ' -- '.join((str(self['number']), self['ref'], str(self['finish_time']))) + '.json' filepath = os.path.abspath(os.path.join(path, filename)) with open(filepath, 'w') as f: json.dump(self, f, indent=2, default=str) # HACK default=str is required for datetime objects