Commit 23013d8cb34e4c9b1564756d9b891525159ca6da

Authored by Miguel Barão
1 parent f6f24c2f
Exists in master and in 1 other branch dev

- simplified json to save the test (not compatible with previous version)

- code cleanup, moved sql code to database.py
Showing 4 changed files with 25 additions and 46 deletions   Show diff stats
BUGS.md
... ... @@ -8,8 +8,7 @@
8 8  
9 9 # TODO
10 10  
11   -- mostrar numero ordem em /results
12   -- simplificar a gravacao do teste em json. alterar o script json2md.py em conformidade
  11 +- alterar o script json2md.py em conformidade
13 12 - Menu para professor com link para /results e /students
14 13 - implementar singlepage/multipage. Fazer uma class para single page que trate de andar gerir o avanco e correcao das perguntas
15 14 - criar pergunta gerada por script externo. Na instanciacao QuestionScript() é corrido um script que devolve uma instancia de pergunta de qualquer tipo.
... ... @@ -19,6 +18,8 @@
19 18  
20 19 # FIXED
21 20  
  21 +- simplificar a gravacao do teste em json.
  22 +- mostrar numero ordem em /results
22 23 - modal a pedir confirmação de submissão.
23 24 - pontos devem estar normalizados escala 0-20
24 25 - mostrar numero de alunos online em /students
... ...
database.py
... ... @@ -12,7 +12,7 @@ class Database(object):
12 12 grades = c.execute('SELECT test_id,grade,finish_time FROM tests WHERE student_id==?', [uid])
13 13 return grades.fetchall()
14 14  
15   - # return students results from a given test
  15 + # return list of students and their results for a given test
16 16 def test_grades(self, test_id):
17 17 with sqlite3.connect(self.db) as c:
18 18 cmd = 'SELECT student_id,name,grade FROM students INNER JOIN tests ON students.number=tests.student_id WHERE test_id==? ORDER BY grade DESC;'
... ... @@ -20,11 +20,23 @@ class Database(object):
20 20 return results.fetchall()
21 21  
22 22 # get list of students in the database
23   - def students(self):
  23 + def get_students(self):
24 24 with sqlite3.connect(self.db) as c:
25 25 students = c.execute('SELECT number,name,password FROM students ORDER BY number ASC;')
26 26 return students.fetchall()
27 27  
  28 + # the following methods update de database data
  29 +
  30 + def save_test(self, t):
  31 + with sqlite3.connect(self.db) as c:
  32 + # store result of the test
  33 + values = (t['ref'], t['number'], t['grade'], str(t['start_time']), str(t['finish_time']))
  34 + c.execute('INSERT INTO tests VALUES (?,?,?,?,?)', values)
  35 +
  36 + # store grade of every question in the test
  37 + ans = [(t['ref'], q['ref'], t['number'], q['grade'], str(t['finish_time'])) for q in t['questions']]
  38 + c.executemany('INSERT INTO questions VALUES (?,?,?,?,?)', ans)
  39 +
28 40 def student_reset_pw(self, d):
29 41 # d = {'12345': 'mypassword', ...}
30 42 with sqlite3.connect(self.db) as c:
... ...
serve.py
... ... @@ -6,10 +6,7 @@
6 6  
7 7 import cherrypy
8 8 from mako.lookup import TemplateLookup
9   -import yaml
10 9 import argparse
11   -from datetime import datetime
12   -import os.path
13 10  
14 11 # my code
15 12 from myauth import AuthController, require
... ... @@ -26,7 +23,7 @@ class Root(object):
26 23 self.database = database.Database(testconf['database'])
27 24 self.auth = AuthController(database=testconf['database'])
28 25 self.templates = TemplateLookup(directories=['templates'], input_encoding='utf-8')
29   - self.loggedin = set() # students currently logged in #FIXME should be in database instead??
  26 + self.loggedin = set() # students currently logged in
30 27  
31 28 # --- DEFAULT ------------------------------------------------------------
32 29 # any path, e.g. /xpto/aargh is redirected to the test
... ... @@ -41,7 +38,6 @@ class Root(object):
41 38 # def students(self, reset_pw=None):
42 39 def students(self, **reset_pw):
43 40 uid = cherrypy.session.get('userid')
44   -
45 41 if uid != '0':
46 42 raise cherrypy.HTTPRedirect('/') #FIXME use authorization @require(admin)
47 43  
... ... @@ -51,16 +47,15 @@ class Root(object):
51 47 cherrypy.log.error('Password updated for student %s.' % str(num), 'APPLICATION')
52 48  
53 49 students = self.database.students()
54   - # print(students)
55 50 template = self.templates.get_template('students.html')
56 51 return template.render(students=students, loggedin=self.loggedin)
57 52  
58 53 # --- RESULTS ------------------------------------------------------------
59 54 @cherrypy.expose
60 55 def results(self):
61   - results = self.database.test_grades(self.testconf['ref'])
  56 + r = self.database.test_grades(self.testconf['ref'])
62 57 template = self.templates.get_template('results.html')
63   - return template.render(t=self.testconf, results=results)
  58 + return template.render(t=self.testconf, results=r)
64 59  
65 60 # --- TEST ---------------------------------------------------------------
66 61 @cherrypy.expose
... ... @@ -77,7 +72,7 @@ class Root(object):
77 72 cherrypy.session['test'] = t = test.Test(self.testconf)
78 73 t['number'] = uid
79 74 t['name'] = name
80   - self.loggedin.add(uid) # track logged in students # FIXME should be in the auth module...
  75 + self.loggedin.add(uid) # track logged in students
81 76  
82 77 # Generate question
83 78 template = self.templates.get_template('test.html')
... ... @@ -109,7 +104,7 @@ class Root(object):
109 104 t.correct()
110 105 if t['save_answers']:
111 106 t.save_json(self.testconf['answers_dir'])
112   - t.save_database(self.testconf['database']) # FIXME save_database should be in database.py
  107 + self.database.save_test(t)
113 108  
114 109 # ---- Expire session ----
115 110 self.loggedin.discard(t['number'])
... ...
test.py
... ... @@ -6,6 +6,7 @@ import json
6 6 import sqlite3
7 7 from datetime import datetime
8 8 import questions
  9 +import database
9 10  
10 11 # ============================================================================
11 12 def read_configuration(filename):
... ... @@ -68,7 +69,6 @@ def read_configuration(filename):
68 69  
69 70 # ============================================================================
70 71 class Test(dict):
71   - '''Creates an instance of a test'''
72 72 # -----------------------------------------------------------------------
73 73 def __init__(self, d):
74 74 super().__init__(d)
... ... @@ -113,38 +113,9 @@ class Test(dict):
113 113  
114 114 # -----------------------------------------------------------------------
115 115 def save_json(self, path):
116   - # ---- Saves test (JSON) ---- FIXME simplify and update json2md
117   - header = {
118   - 'title': self['title'],
119   - 'number': self['number'],
120   - 'name': self['name'],
121   - 'testid': self['ref'],
122   - 'start_time': str(self['start_time']),
123   - 'finish_time': str(self['finish_time']),
124   - 'final_grade': str(self['grade'])
125   - }
126 116 filename = ' -- '.join((str(self['number']), self['ref'],
127 117 str(self['finish_time']))) + '.json'
128 118 filepath = os.path.abspath(os.path.join(path, filename))
129 119 with open(filepath, 'w') as f:
130   - test = {'header': header, 'answers': self['questions']}
131   - json.dump(test, f, indent=4)
132   -
133   - # -----------------------------------------------------------------------
134   - def save_database(self, db):
135   - # ---- Saves grade to database ---- FIXME SQLAlchemy??
136   - t = self
137   - ans = []
138   - for q in t['questions']:
139   - ans.append((t['ref'], q['ref'], t['number'], q['grade'], str(t['finish_time'])))
140   - values = (t['ref'], t['number'], t['grade'], str(t['start_time']), str(t['finish_time']))
141   -
142   - conn = sqlite3.connect(db)
143   - conn.execute('INSERT INTO tests VALUES (?,?,?,?,?)', values)
144   - conn.executemany('INSERT INTO questions VALUES (?,?,?,?,?)', ans)
145   - conn.commit()
146   - conn.close()
147   -
148   - # -----------------------------------------------------------------------
149   - def __str__(self):
150   - return str(self)
  120 + json.dump(self, f, indent=2, default=str)
  121 + # HACK default=str is required for datetime objects
... ...