From 35e20aae9a759026652373e935c24f56c9bf4467 Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Sat, 2 May 2020 14:02:43 +0100 Subject: [PATCH] adds button to /admin page to download CSV file with grades. --- perguntations/app.py | 19 ++++++++++++++++++- perguntations/serve.py | 37 ++++++++++++++++++++++++++++++------- perguntations/static/js/admin.js | 50 +++++++++++++++++++++----------------------------- perguntations/templates/admin.html | 33 ++++++++++++++++++--------------- 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/perguntations/app.py b/perguntations/app.py index b170e03..c4dc8c3 100644 --- a/perguntations/app.py +++ b/perguntations/app.py @@ -6,6 +6,8 @@ Main application module # python standard libraries import asyncio from contextlib import contextmanager # `with` statement in db sessions +import csv +import io import json import logging from os import path @@ -264,7 +266,7 @@ class App(): # ------------------------------------------------------------------------ def event_test(self, uid, cmd, value): - '''handle browser events the occur during the test''' + '''handles browser events the occur during the test''' if cmd == 'focus': logger.info('Student %s: focus %s', uid, value) elif cmd == 'size': @@ -279,6 +281,21 @@ class App(): # def get_student_name(self, uid): # return self.online[uid]['student']['name'] + def get_test_csv(self): + '''generates a CSV with the grades of the test''' + with self.db_session() as sess: + grades = sess.query(Test.student_id, Test.grade, + Test.starttime, Test.finishtime)\ + .filter(Test.ref == self.testfactory['ref'])\ + .order_by(Test.student_id)\ + .all() + + csvstr = io.StringIO() + writer = csv.writer(csvstr, delimiter=';', quoting=csv.QUOTE_ALL) + writer.writerow(('Número', 'Nota', 'Início', 'Fim')) + writer.writerows(grades) + return csvstr.getvalue() + def get_student_test(self, uid, default=None): '''get test from online student''' return self.online[uid].get('test', default) diff --git a/perguntations/serve.py b/perguntations/serve.py index 3919ab0..79c0b16 100644 --- a/perguntations/serve.py +++ b/perguntations/serve.py @@ -42,6 +42,7 @@ class WebApplication(tornado.web.Application): (r'/file', FileHandler), # (r'/root', MainHandler), # FIXME # (r'/ws', AdminSocketHandler), + (r'/adminwebservice', AdminWebservice), (r'/studentwebservice', StudentWebservice), (r'/', RootHandler), ] @@ -62,7 +63,10 @@ class WebApplication(tornado.web.Application): # ---------------------------------------------------------------------------- def admin_only(func): ''' - Decorator used to restrict access to the administrator + Decorator used to restrict access to the administrator. For example: + + @admin_only() + def get(self): ... ''' @functools.wraps(func) async def wrapper(self, *args, **kwargs): @@ -75,14 +79,14 @@ def admin_only(func): # ---------------------------------------------------------------------------- class BaseHandler(tornado.web.RequestHandler): ''' - Base handler. Other handlers will inherit this one. + Handlers should inherit this one instead of tornado.web.RequestHandler. + It automatically gets the user cookie, which is required to identify the + user in most handlers. ''' @property def testapp(self): - ''' - simplifies access to the application - ''' + '''simplifies access to the application''' return self.application.testapp def get_current_user(self): @@ -155,8 +159,8 @@ class BaseHandler(tornado.web.RequestHandler): class StudentWebservice(BaseHandler): ''' - Receive ajax from students in the test: - focus, unfocus + Receive ajax from students in the test in response from focus, unfocus and + resize events. ''' @tornado.web.authenticated @@ -167,6 +171,25 @@ class StudentWebservice(BaseHandler): value = json.loads(self.get_body_argument('value', None)) self.testapp.event_test(uid, cmd, value) + +# ---------------------------------------------------------------------------- +class AdminWebservice(BaseHandler): + ''' + Receive ajax requests from admin + ''' + + @tornado.web.authenticated + @admin_only + async def get(self): + '''admin webservices that do not change state''' + cmd = self.get_query_argument('cmd') + if cmd == 'testcsv': + self.set_header('Content-Type', 'text/csv') + self.set_header('content-Disposition', + 'attachment; filename=notas.csv') + self.write(self.testapp.get_test_csv()) + await self.flush() + # ---------------------------------------------------------------------------- class AdminHandler(BaseHandler): '''Handle /admin''' diff --git a/perguntations/static/js/admin.js b/perguntations/static/js/admin.js index 7d63603..22c37ca 100644 --- a/perguntations/static/js/admin.js +++ b/perguntations/static/js/admin.js @@ -14,35 +14,27 @@ jQuery.postJSON = function(url, args) { $(document).ready(function() { function button_handlers() { // button handlers (runs once) - $("#allow_all").click( - function() { - $(":checkbox").prop("checked", true).trigger('change'); - } - ); - $("#deny_all").click( - function() { - $(":checkbox").prop("checked", false).trigger('change'); - } - ); - $("#reset_password").click( - function () { - $.postJSON("/admin", { - "cmd": "reset_password", - "value": $("#reset_number").val() - }); - } - ); - $("#inserir_novo_aluno").click( - function () { - $.postJSON("/admin", { - "cmd": "insert_student", - "value": JSON.stringify({ - "number": $("#novo_numero").val(), - "name": $("#novo_nome").val() - }) - }); - } - ); + $("#allow_all").click(function() { + $(":checkbox").prop("checked", true).trigger('change'); + }); + $("#deny_all").click(function() { + $(":checkbox").prop("checked", false).trigger('change'); + }); + $("#reset_password").click(function () { + $.postJSON("/admin", { + "cmd": "reset_password", + "value": $("#reset_number").val() + }); + }); + $("#inserir_novo_aluno").click(function () { + $.postJSON("/admin", { + "cmd": "insert_student", + "value": JSON.stringify({ + "number": $("#novo_numero").val(), + "name": $("#novo_nome").val() + }) + }); + }); // authorization checkboxes in the students_table: $("tbody", "#students_table").on("change", "input", autorizeStudent); } diff --git a/perguntations/templates/admin.html b/perguntations/templates/admin.html index 7ddadc6..247de80 100644 --- a/perguntations/templates/admin.html +++ b/perguntations/templates/admin.html @@ -68,24 +68,27 @@
-

- Ref:
- Enunciado:
- Base de dados:
- Testes submetidos: +

+ Ref:
+ Enunciado:
+ Base de dados:
+ Testes submetidos: +

+ Obter CSV com as notas +

- - - - - - - - - - + + + + + + + + + +
#OkNúmeroNomeEstadoNota
#OkNúmeroNomeEstadoNota
-- libgit2 0.21.2