Commit ef03716c7924a1fd0189dff27cd5bac90d50dde5
1 parent
e07f3a34
Exists in
master
and in
1 other branch
- minor corrections in MANUAL.md
- fixed order of the students in /result, added elapsed time and cosmetic changes - new icon (pomba)
Showing
7 changed files
with
75 additions
and
31 deletions
Show diff stats
BUGS.md
1 | - | |
1 | + | |
2 | 2 | |
3 | 3 | # BUGS |
4 | 4 | |
5 | 5 | !!! - questions type script, necessário dar um caminho exacto relativamete ao directorio do server em vez da pergunta. deveria ser possivel mover as perguntas de directorio sem rebentar os caminhos. |
6 | 6 | |
7 | -- colisoes nas referencias das perguntas. a referencia deveria influir o nome do ficheiro? | |
7 | +- parece que é preciso criar à mão a pasta para as respostas (ans/...) depois apercebo-me que os caminhos no teste dizem respeito à directoria donde o teste é corrido... as respostas deveriam guardadas no directório dado. | |
8 | +- colisoes nas referencias das perguntas. a referencia deveria influir o nome do ficheiro? acho que nao | |
8 | 9 | - se database for mal configurada, é criada uma base de dados vazia e rebenta na autenticacao. |
9 | 10 | - mostrar erro quando nao consegue importar questions files |
10 | 11 | - testar regex na definicao das perguntas. como se faz rawstring em yaml? singlequote? problemas de backslash??? sim... necessário fazer \\ em varios casos, mas não é claro! e.g. \n é convertido em espaço mas \w é convertido em \\ e w. |
11 | - | |
12 | +- testar envio de parametros para stdin para perguntas tipo generator. | |
13 | +- usar pomba da ue moderna. | |
12 | 14 | |
13 | 15 | # TODO |
14 | 16 | |
17 | +- pacotes exactos usados para instalar. | |
18 | +- SQLAlchemy em vez da classe database. | |
15 | 19 | - Criar botão para o docente fazer enable/disable do aluno explicitamente (exames presenciais). |
16 | -- testar envio de parametros para stdin para perguntas tipo generator. | |
17 | 20 | - hash das passwords obtidas da concatenacao do numero de aluno com password (evita que passwords repetidas sejam detectadas). |
18 | 21 | - permitir enviar varios testes, aluno escolhe qual o teste que quer fazer. |
19 | 22 | - criar script json2md.py ou outra forma de gerar um teste ja realizado |
... | ... | @@ -23,10 +26,11 @@ |
23 | 26 | - criar perguntas de outros tipos, e.g. associação, ordenação, varios textinput |
24 | 27 | - perguntas para professor corrigir mais tarde. |
25 | 28 | - testar com microsoft surface. |
26 | - | |
29 | +- share do score em /results (email) | |
27 | 30 | |
28 | 31 | # FIXED |
29 | 32 | |
33 | +- /results esta ordenado por numero e nao por data | |
30 | 34 | - numeros das perguntas não fazem sentido quando há caixas de informação (numerar informacao tb?) |
31 | 35 | - Quando apresenta o teste, preencher com os valores definidos em answer (permite que professor dê informação à partida, e no modo practice fiquem com o preenchido anteriormente) |
32 | 36 | - information points é definido onde? test.y ou questions.py? | ... | ... |
MANUAL.md
... | ... | @@ -113,18 +113,20 @@ A test is a file in `yaml` format that can reside anywhere on the filesystem. It |
113 | 113 | # Each question has a default value of 1.0 point, but it can be overridden. |
114 | 114 | # The points defined here do not need to be normalized (it's automatic). |
115 | 115 | questions: |
116 | - - ref: | |
117 | - - first-question-1 # randomly choose one from these 3 questions | |
118 | - - first-question-2 | |
119 | - - first-question-3 | |
120 | - points: 0.5 | |
116 | + - ref: | |
117 | + - first-question-1 # randomly choose one from these 3 questions | |
118 | + - first-question-2 | |
119 | + - first-question-3 | |
120 | + points: 0.5 | |
121 | 121 | |
122 | - - ref: second-question # just one question, 1.0 point (unnormalized) | |
122 | + - ref: second-question # one question, 1.0 point (unnormalized) | |
123 | 123 | |
124 | - - third-question # "ref:" not needed in simple cases | |
124 | + - third-question # "ref:" not needed in simple cases | |
125 | 125 | |
126 | - - wrong-question # ref: missing because we also have | |
127 | - points: 2 # points: | |
126 | +This following one is wrong: | |
127 | + | |
128 | + - wrong-question # missing "ref:" key | |
129 | + points: 2 | |
128 | 130 | |
129 | 131 | Some of the options have default values if they are omitted. The defaults are the following: |
130 | 132 | |
... | ... | @@ -134,6 +136,7 @@ Some of the options have default values if they are omitted. The defaults are th |
134 | 136 | show_hints: False |
135 | 137 | show_points: False |
136 | 138 | practice_mode: False |
139 | + show_ref: False | |
137 | 140 | debug: False |
138 | 141 | points: 1.0 |
139 | 142 | ... | ... |
database.py
... | ... | @@ -20,12 +20,12 @@ class Database(object): |
20 | 20 | |
21 | 21 | # only the best result for each student |
22 | 22 | cmd = ''' |
23 | - SELECT student_id, name, MAX(grade) | |
23 | + SELECT student_id, name, MAX(grade), finish_time | |
24 | 24 | FROM students INNER JOIN tests |
25 | 25 | ON students.number=tests.student_id |
26 | 26 | WHERE test_id==? AND student_id!=0 |
27 | 27 | GROUP BY student_id |
28 | - ORDER BY grade DESC;''' | |
28 | + ORDER BY grade DESC, finish_time DESC;''' | |
29 | 29 | return c.execute(cmd, [test_id]).fetchall() |
30 | 30 | |
31 | 31 | # get list of students in the database | ... | ... |
serve.py
... | ... | @@ -23,7 +23,7 @@ class Root(object): |
23 | 23 | self.database = database.Database(testconf['database']) |
24 | 24 | self.auth = AuthController(database=testconf['database']) |
25 | 25 | self.templates = TemplateLookup(directories=['templates'], input_encoding='utf-8') |
26 | - self.tags = {'online': set(), 'finished': set()} # should be in application, not server | |
26 | + self.tags = {'online': set(), 'finished': set()} # FIXME should be in application, not server | |
27 | 27 | |
28 | 28 | # --- DEFAULT ------------------------------------------------------------ |
29 | 29 | # any path, e.g. /xpto/aargh is redirected to the test | ... | ... |
static/favicon.ico
No preview for this file type
templates/grade.html
templates/results.html
... | ... | @@ -25,6 +25,10 @@ |
25 | 25 | box-shadow: 0px 2px 10px 3px rgba(0, 0, 0, .2); |
26 | 26 | border-radius:5px; |
27 | 27 | } |
28 | + .progress { | |
29 | + margin-bottom: 0px; | |
30 | + border-radius: 25px; | |
31 | + } | |
28 | 32 | </style> |
29 | 33 | </head> |
30 | 34 | <!-- ===================================================================== --> |
... | ... | @@ -62,25 +66,41 @@ |
62 | 66 | ${t['title']} |
63 | 67 | </div> |
64 | 68 | <!-- <div class="panel-body"> --> |
65 | - <table class="table"> | |
69 | + <table class="table table-hover"> | |
66 | 70 | <thead> |
67 | - <tr> | |
68 | - <th>Posição</th> | |
69 | - <th>Número</th> | |
70 | - <th>Nome</th> | |
71 | - <th>Nota (0-20)</th> | |
72 | - </tr> | |
71 | + <tr> | |
72 | + <th class="col-md-1 text-center">#</th> | |
73 | + <th class="col-md-7 text-left">Nome</th> | |
74 | + <th class="col-md-4 text-center">Nota</th> | |
75 | + <th class="col-md-0"></th> | |
76 | + </tr> | |
73 | 77 | </thead> |
74 | 78 | <tbody> |
75 | - % for r in results: | |
79 | + <%! | |
80 | + from datetime import datetime | |
81 | + %> | |
82 | + % for r in results: | |
76 | 83 | <tr> |
77 | - <td>${loop.index+1}</td> | |
78 | - <td>${r[0]}</td> <!-- numero --> | |
84 | + <td class="text-center"> | |
85 | + % if loop.index == 0: | |
86 | + <h4> | |
87 | + <!-- <span class="label label-primary"> --> | |
88 | + 1º | |
89 | + <!-- </span> --> | |
90 | + </h4> | |
91 | + % else: | |
92 | + <!-- <span class="label label-primary"> --> | |
93 | + ${loop.index+1} | |
94 | + <!-- </span> --> | |
95 | + % endif | |
96 | + </td> | |
97 | + <!-- <td>${r[0]}</td> --> <!-- numero --> | |
79 | 98 | <td> |
80 | 99 | % if loop.index == 0: |
81 | - <img src="\crown.jpg" /> | |
82 | - %endif | |
83 | - ${r[1]} | |
100 | + <h4 class="text-uppercase"><img src="\crown.jpg" /> ${r[1]}</h4> | |
101 | + % else: | |
102 | + ${r[1]} | |
103 | + % endif | |
84 | 104 | </td> <!-- nome --> |
85 | 105 | <td> <!-- nota --> |
86 | 106 | <div class="progress"> |
... | ... | @@ -95,6 +115,23 @@ |
95 | 115 | </div> |
96 | 116 | </div> |
97 | 117 | </td> |
118 | + <td class="text-right"> | |
119 | + <% | |
120 | + dt = datetime.now() - datetime.strptime(r[3], '%Y-%m-%d %H:%M:%S.%f') | |
121 | + %> | |
122 | + <small> | |
123 | + % if dt.days > 0: | |
124 | + ${dt.days}d | |
125 | + % elif dt.seconds >= 3600: | |
126 | + (${dt.seconds // 3600} horas | |
127 | + % elif dt.seconds >= 60: | |
128 | + (${dt.seconds // 60} minutos | |
129 | + % else: | |
130 | + (${dt.seconds} segundos | |
131 | + % endif | |
132 | + | |
133 | + </small> | |
134 | + </td> | |
98 | 135 | </tr> |
99 | 136 | % endfor |
100 | 137 | </tbody> | ... | ... |