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 281 # def get_student_name(self, uid):
282 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 315 def get_test_csv(self):
285 316 '''generates a CSV with the grades of the test'''
286 317 with self.db_session() as sess:
... ... @@ -292,7 +323,7 @@ class App():
292 323  
293 324 csvstr = io.StringIO()
294 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 327 writer.writerows(grades)
297 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 28 questions = relationship('Question', back_populates='student')
27 29  
28 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 40 __tablename__ = 'tests'
39 41 id = Column(Integer, primary_key=True) # auto_increment
40 42 ref = Column(String)
41   - title = Column(String) # FIXME depends on ref and should come from another table...
  43 + title = Column(String)
42 44 grade = Column(Float)
43 45 state = Column(String) # ACTIVE, FINISHED, QUIT, NULL
44 46 comment = Column(String)
... ... @@ -52,17 +54,17 @@ class Test(Base):
52 54 questions = relationship('Question', back_populates='test')
53 55  
54 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 84 test = relationship('Test', back_populates='questions')
83 85  
84 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 191 self.write(data)
192 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 204 class AdminHandler(BaseHandler):
196 205 '''Handle /admin'''
... ...
perguntations/templates/admin.html
... ... @@ -76,7 +76,8 @@
76 76 Base de dados: <code id="database">--</code><br>
77 77 </p>
78 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 81 </p>
81 82 </div> <!-- jumbotron -->
82 83  
... ...