Commit 23013d8cb34e4c9b1564756d9b891525159ca6da
1 parent
f6f24c2f
Exists in
master
and in
1 other branch
- 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 +8,7 @@ | ||
8 | 8 | ||
9 | # TODO | 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 | - Menu para professor com link para /results e /students | 12 | - Menu para professor com link para /results e /students |
14 | - implementar singlepage/multipage. Fazer uma class para single page que trate de andar gerir o avanco e correcao das perguntas | 13 | - implementar singlepage/multipage. Fazer uma class para single page que trate de andar gerir o avanco e correcao das perguntas |
15 | - criar pergunta gerada por script externo. Na instanciacao QuestionScript() é corrido um script que devolve uma instancia de pergunta de qualquer tipo. | 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,6 +18,8 @@ | ||
19 | 18 | ||
20 | # FIXED | 19 | # FIXED |
21 | 20 | ||
21 | +- simplificar a gravacao do teste em json. | ||
22 | +- mostrar numero ordem em /results | ||
22 | - modal a pedir confirmação de submissão. | 23 | - modal a pedir confirmação de submissão. |
23 | - pontos devem estar normalizados escala 0-20 | 24 | - pontos devem estar normalizados escala 0-20 |
24 | - mostrar numero de alunos online em /students | 25 | - mostrar numero de alunos online em /students |
database.py
@@ -12,7 +12,7 @@ class Database(object): | @@ -12,7 +12,7 @@ class Database(object): | ||
12 | grades = c.execute('SELECT test_id,grade,finish_time FROM tests WHERE student_id==?', [uid]) | 12 | grades = c.execute('SELECT test_id,grade,finish_time FROM tests WHERE student_id==?', [uid]) |
13 | return grades.fetchall() | 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 | def test_grades(self, test_id): | 16 | def test_grades(self, test_id): |
17 | with sqlite3.connect(self.db) as c: | 17 | with sqlite3.connect(self.db) as c: |
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;' | 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,11 +20,23 @@ class Database(object): | ||
20 | return results.fetchall() | 20 | return results.fetchall() |
21 | 21 | ||
22 | # get list of students in the database | 22 | # get list of students in the database |
23 | - def students(self): | 23 | + def get_students(self): |
24 | with sqlite3.connect(self.db) as c: | 24 | with sqlite3.connect(self.db) as c: |
25 | students = c.execute('SELECT number,name,password FROM students ORDER BY number ASC;') | 25 | students = c.execute('SELECT number,name,password FROM students ORDER BY number ASC;') |
26 | return students.fetchall() | 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 | def student_reset_pw(self, d): | 40 | def student_reset_pw(self, d): |
29 | # d = {'12345': 'mypassword', ...} | 41 | # d = {'12345': 'mypassword', ...} |
30 | with sqlite3.connect(self.db) as c: | 42 | with sqlite3.connect(self.db) as c: |
serve.py
@@ -6,10 +6,7 @@ | @@ -6,10 +6,7 @@ | ||
6 | 6 | ||
7 | import cherrypy | 7 | import cherrypy |
8 | from mako.lookup import TemplateLookup | 8 | from mako.lookup import TemplateLookup |
9 | -import yaml | ||
10 | import argparse | 9 | import argparse |
11 | -from datetime import datetime | ||
12 | -import os.path | ||
13 | 10 | ||
14 | # my code | 11 | # my code |
15 | from myauth import AuthController, require | 12 | from myauth import AuthController, require |
@@ -26,7 +23,7 @@ class Root(object): | @@ -26,7 +23,7 @@ class Root(object): | ||
26 | self.database = database.Database(testconf['database']) | 23 | self.database = database.Database(testconf['database']) |
27 | self.auth = AuthController(database=testconf['database']) | 24 | self.auth = AuthController(database=testconf['database']) |
28 | self.templates = TemplateLookup(directories=['templates'], input_encoding='utf-8') | 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 | # --- DEFAULT ------------------------------------------------------------ | 28 | # --- DEFAULT ------------------------------------------------------------ |
32 | # any path, e.g. /xpto/aargh is redirected to the test | 29 | # any path, e.g. /xpto/aargh is redirected to the test |
@@ -41,7 +38,6 @@ class Root(object): | @@ -41,7 +38,6 @@ class Root(object): | ||
41 | # def students(self, reset_pw=None): | 38 | # def students(self, reset_pw=None): |
42 | def students(self, **reset_pw): | 39 | def students(self, **reset_pw): |
43 | uid = cherrypy.session.get('userid') | 40 | uid = cherrypy.session.get('userid') |
44 | - | ||
45 | if uid != '0': | 41 | if uid != '0': |
46 | raise cherrypy.HTTPRedirect('/') #FIXME use authorization @require(admin) | 42 | raise cherrypy.HTTPRedirect('/') #FIXME use authorization @require(admin) |
47 | 43 | ||
@@ -51,16 +47,15 @@ class Root(object): | @@ -51,16 +47,15 @@ class Root(object): | ||
51 | cherrypy.log.error('Password updated for student %s.' % str(num), 'APPLICATION') | 47 | cherrypy.log.error('Password updated for student %s.' % str(num), 'APPLICATION') |
52 | 48 | ||
53 | students = self.database.students() | 49 | students = self.database.students() |
54 | - # print(students) | ||
55 | template = self.templates.get_template('students.html') | 50 | template = self.templates.get_template('students.html') |
56 | return template.render(students=students, loggedin=self.loggedin) | 51 | return template.render(students=students, loggedin=self.loggedin) |
57 | 52 | ||
58 | # --- RESULTS ------------------------------------------------------------ | 53 | # --- RESULTS ------------------------------------------------------------ |
59 | @cherrypy.expose | 54 | @cherrypy.expose |
60 | def results(self): | 55 | def results(self): |
61 | - results = self.database.test_grades(self.testconf['ref']) | 56 | + r = self.database.test_grades(self.testconf['ref']) |
62 | template = self.templates.get_template('results.html') | 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 | # --- TEST --------------------------------------------------------------- | 60 | # --- TEST --------------------------------------------------------------- |
66 | @cherrypy.expose | 61 | @cherrypy.expose |
@@ -77,7 +72,7 @@ class Root(object): | @@ -77,7 +72,7 @@ class Root(object): | ||
77 | cherrypy.session['test'] = t = test.Test(self.testconf) | 72 | cherrypy.session['test'] = t = test.Test(self.testconf) |
78 | t['number'] = uid | 73 | t['number'] = uid |
79 | t['name'] = name | 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 | # Generate question | 77 | # Generate question |
83 | template = self.templates.get_template('test.html') | 78 | template = self.templates.get_template('test.html') |
@@ -109,7 +104,7 @@ class Root(object): | @@ -109,7 +104,7 @@ class Root(object): | ||
109 | t.correct() | 104 | t.correct() |
110 | if t['save_answers']: | 105 | if t['save_answers']: |
111 | t.save_json(self.testconf['answers_dir']) | 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 | # ---- Expire session ---- | 109 | # ---- Expire session ---- |
115 | self.loggedin.discard(t['number']) | 110 | self.loggedin.discard(t['number']) |
test.py
@@ -6,6 +6,7 @@ import json | @@ -6,6 +6,7 @@ import json | ||
6 | import sqlite3 | 6 | import sqlite3 |
7 | from datetime import datetime | 7 | from datetime import datetime |
8 | import questions | 8 | import questions |
9 | +import database | ||
9 | 10 | ||
10 | # ============================================================================ | 11 | # ============================================================================ |
11 | def read_configuration(filename): | 12 | def read_configuration(filename): |
@@ -68,7 +69,6 @@ def read_configuration(filename): | @@ -68,7 +69,6 @@ def read_configuration(filename): | ||
68 | 69 | ||
69 | # ============================================================================ | 70 | # ============================================================================ |
70 | class Test(dict): | 71 | class Test(dict): |
71 | - '''Creates an instance of a test''' | ||
72 | # ----------------------------------------------------------------------- | 72 | # ----------------------------------------------------------------------- |
73 | def __init__(self, d): | 73 | def __init__(self, d): |
74 | super().__init__(d) | 74 | super().__init__(d) |
@@ -113,38 +113,9 @@ class Test(dict): | @@ -113,38 +113,9 @@ class Test(dict): | ||
113 | 113 | ||
114 | # ----------------------------------------------------------------------- | 114 | # ----------------------------------------------------------------------- |
115 | def save_json(self, path): | 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 | filename = ' -- '.join((str(self['number']), self['ref'], | 116 | filename = ' -- '.join((str(self['number']), self['ref'], |
127 | str(self['finish_time']))) + '.json' | 117 | str(self['finish_time']))) + '.json' |
128 | filepath = os.path.abspath(os.path.join(path, filename)) | 118 | filepath = os.path.abspath(os.path.join(path, filename)) |
129 | with open(filepath, 'w') as f: | 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 |