Commit 729b68db93b52de30ba8472d8ae27b350100ce1f

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

- add button to get detailed grades of questions

perguntations/app.py
@@ -281,6 +281,37 @@ class App(): @@ -281,6 +281,37 @@ class App():
281 # def get_student_name(self, uid): 281 # def get_student_name(self, uid):
282 # return self.online[uid]['student']['name'] 282 # return self.online[uid]['student']['name']
283 283
  284 + def get_questions_csv(self):
  285 + '''generates a CSV with the grades of the test'''
  286 + test_id = self.testfactory['ref']
  287 +
  288 + with self.db_session() as sess:
  289 + grades = sess.query(Question.student_id, Question.starttime,
  290 + Question.ref, Question.grade)\
  291 + .filter(Question.test_id == test_id)\
  292 + .order_by(Question.student_id)\
  293 + .all()
  294 +
  295 + cols = ['Aluno', 'Início'] + \
  296 + [r for question in self.testfactory['questions']
  297 + for r in question['ref']]
  298 +
  299 + tests = {}
  300 + for q in grades:
  301 + student, qref, qgrade = q[:2], q[2], q[3]
  302 + tests.setdefault(student, {})[qref] = qgrade
  303 +
  304 + rows = [{'Aluno': test[0], 'Início': test[1], **q}
  305 + for test, q in tests.items()]
  306 +
  307 + csvstr = io.StringIO()
  308 + writer = csv.DictWriter(csvstr, fieldnames=cols, restval=None,
  309 + delimiter=';', quoting=csv.QUOTE_ALL)
  310 + writer.writeheader()
  311 + writer.writerows(rows)
  312 + return test_id, csvstr.getvalue()
  313 +
  314 +
284 def get_test_csv(self): 315 def get_test_csv(self):
285 '''generates a CSV with the grades of the test''' 316 '''generates a CSV with the grades of the test'''
286 with self.db_session() as sess: 317 with self.db_session() as sess:
@@ -292,7 +323,7 @@ class App(): @@ -292,7 +323,7 @@ class App():
292 323
293 csvstr = io.StringIO() 324 csvstr = io.StringIO()
294 writer = csv.writer(csvstr, delimiter=';', quoting=csv.QUOTE_ALL) 325 writer = csv.writer(csvstr, delimiter=';', quoting=csv.QUOTE_ALL)
295 - writer.writerow(('Número', 'Nota', 'Início', 'Fim')) 326 + writer.writerow(('Aluno', 'Nota', 'Início', 'Fim'))
296 writer.writerows(grades) 327 writer.writerows(grades)
297 return self.testfactory['ref'], csvstr.getvalue() 328 return self.testfactory['ref'], csvstr.getvalue()
298 329
perguntations/models.py
1 ''' 1 '''
2 -Database tables 2 +SQLAlchemy ORM
  3 +
  4 +The classes below correspond to database tables
3 ''' 5 '''
4 6
5 7
@@ -26,10 +28,10 @@ class Student(Base): @@ -26,10 +28,10 @@ class Student(Base):
26 questions = relationship('Question', back_populates='student') 28 questions = relationship('Question', back_populates='student')
27 29
28 def __repr__(self): 30 def __repr__(self):
29 - return f'Student:\n\  
30 - id: "{self.id}"\n\  
31 - name: "{self.name}"\n\  
32 - password: "{self.password}"' 31 + return (f'Student:\n'
  32 + f' id: "{self.id}"\n'
  33 + f' name: "{self.name}"\n'
  34 + f' password: "{self.password}"\n')
33 35
34 36
35 # ---------------------------------------------------------------------------- 37 # ----------------------------------------------------------------------------
@@ -38,7 +40,7 @@ class Test(Base): @@ -38,7 +40,7 @@ class Test(Base):
38 __tablename__ = 'tests' 40 __tablename__ = 'tests'
39 id = Column(Integer, primary_key=True) # auto_increment 41 id = Column(Integer, primary_key=True) # auto_increment
40 ref = Column(String) 42 ref = Column(String)
41 - title = Column(String) # FIXME depends on ref and should come from another table... 43 + title = Column(String)
42 grade = Column(Float) 44 grade = Column(Float)
43 state = Column(String) # ACTIVE, FINISHED, QUIT, NULL 45 state = Column(String) # ACTIVE, FINISHED, QUIT, NULL
44 comment = Column(String) 46 comment = Column(String)
@@ -52,17 +54,17 @@ class Test(Base): @@ -52,17 +54,17 @@ class Test(Base):
52 questions = relationship('Question', back_populates='test') 54 questions = relationship('Question', back_populates='test')
53 55
54 def __repr__(self): 56 def __repr__(self):
55 - return f'Test:\n\  
56 - id: "{self.id}"\n\  
57 - ref: "{self.ref}"\n\  
58 - title: "{self.title}"\n\  
59 - grade: "{self.grade}"\n\  
60 - state: "{self.state}"\n\  
61 - comment: "{self.comment}"\n\  
62 - starttime: "{self.starttime}"\n\  
63 - finishtime: "{self.finishtime}"\n\  
64 - filename: "{self.filename}"\n\  
65 - student_id: "{self.student_id}"\n' 57 + return (f'Test:\n'
  58 + f' id: "{self.id}"\n'
  59 + f' ref: "{self.ref}"\n'
  60 + f' title: "{self.title}"\n'
  61 + f' grade: "{self.grade}"\n'
  62 + f' state: "{self.state}"\n'
  63 + f' comment: "{self.comment}"\n'
  64 + f' starttime: "{self.starttime}"\n'
  65 + f' finishtime: "{self.finishtime}"\n'
  66 + f' filename: "{self.filename}"\n'
  67 + f' student_id: "{self.student_id}"\n')
66 68
67 69
68 # --------------------------------------------------------------------------- 70 # ---------------------------------------------------------------------------
@@ -82,11 +84,11 @@ class Question(Base): @@ -82,11 +84,11 @@ class Question(Base):
82 test = relationship('Test', back_populates='questions') 84 test = relationship('Test', back_populates='questions')
83 85
84 def __repr__(self): 86 def __repr__(self):
85 - return f'Question:\n\  
86 - id: "{self.id}"\n\  
87 - ref: "{self.ref}"\n\  
88 - grade: "{self.grade}"\n\  
89 - starttime: "{self.starttime}"\n\  
90 - finishtime: "{self.finishtime}"\n\  
91 - student_id: "{self.student_id}"\n\  
92 - test_id: "{self.test_id}"\n' 87 + return (f'Question:\n'
  88 + f' id: "{self.id}"\n'
  89 + f' ref: "{self.ref}"\n'
  90 + f' grade: "{self.grade}"\n'
  91 + f' starttime: "{self.starttime}"\n'
  92 + f' finishtime: "{self.finishtime}"\n'
  93 + f' student_id: "{self.student_id}"\n'
  94 + f' test_id: "{self.test_id}"\n')
perguntations/serve.py
@@ -191,6 +191,15 @@ class AdminWebservice(BaseHandler): @@ -191,6 +191,15 @@ class AdminWebservice(BaseHandler):
191 self.write(data) 191 self.write(data)
192 await self.flush() 192 await self.flush()
193 193
  194 + if cmd == 'questionscsv':
  195 + test_ref, data = self.testapp.get_questions_csv()
  196 + self.set_header('Content-Type', 'text/csv')
  197 + self.set_header('content-Disposition',
  198 + f'attachment; filename={test_ref}-detailed.csv')
  199 + self.write(data)
  200 + await self.flush()
  201 +
  202 +
194 # ---------------------------------------------------------------------------- 203 # ----------------------------------------------------------------------------
195 class AdminHandler(BaseHandler): 204 class AdminHandler(BaseHandler):
196 '''Handle /admin''' 205 '''Handle /admin'''
perguntations/templates/admin.html
@@ -76,7 +76,8 @@ @@ -76,7 +76,8 @@
76 Base de dados: <code id="database">--</code><br> 76 Base de dados: <code id="database">--</code><br>
77 </p> 77 </p>
78 <p> 78 <p>
79 - <a href="/adminwebservice?cmd=testcsv" class="btn btn-primary">Obter CSV com as notas</a> 79 + <a href="/adminwebservice?cmd=testcsv" class="btn btn-primary">Obter CSV das notas</a>
  80 + <a href="/adminwebservice?cmd=questionscsv" class="btn btn-primary">Obter CSV detalhado</a>
80 </p> 81 </p>
81 </div> <!-- jumbotron --> 82 </div> <!-- jumbotron -->
82 83