Commit cd56848f1c40fdd4c91399b1ed666fe11436c8da
1 parent
526721fd
Exists in
master
and in
1 other branch
- review is working.
- fixed markdown rendering in questions.
Showing
20 changed files
with
172 additions
and
98 deletions
Show diff stats
BUGS.md
1 | 1 | |
2 | 2 | # BUGS |
3 | 3 | |
4 | +- markdown no teste nao funciona | |
4 | 5 | - numeracao das perguntas do teste esta a contar com paineis informativos... |
5 | 6 | - servir imagens das perguntas |
6 | -- review de um teste nao funciona (hardcoded...) | |
7 | -- testar opcao --allow-all | |
8 | 7 | - como alterar configuracao para mostrar logs de debug? |
9 | 8 | - hints nao funciona |
10 | 9 | - uniformizar question.py com a de aprendizations... |
... | ... | @@ -14,6 +13,14 @@ |
14 | 13 | |
15 | 14 | # TODO |
16 | 15 | |
16 | +- mathjax-node: | |
17 | + sudo pkg install node npm | |
18 | + npm install mathjax-node mathjax-node-cli # pacotes em ~/node_modules | |
19 | + node_modules/mathjax-node-cli/bin/tex2svg '\sqrt{x}' | |
20 | + usar isto para gerar svg que passa a fazer parte do texto da pergunta (markdown suporta tags svg?) | |
21 | + | |
22 | + | |
23 | + | |
17 | 24 | - Gerar pdf's com todos os testes no final (pdfkit). |
18 | 25 | - manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento |
19 | 26 | |
... | ... | @@ -31,6 +38,7 @@ |
31 | 38 | |
32 | 39 | # FIXED |
33 | 40 | |
41 | +- review de um teste nao funciona (hardcoded...) | |
34 | 42 | - testar SSL |
35 | 43 | - text-numeric não está a gerar a pergunta. faltam templates? |
36 | 44 | - testar perguntas warning/warn | ... | ... |
demo/questions/questions-tutorial.yaml
... | ... | @@ -2,19 +2,41 @@ |
2 | 2 | ref: tut-information |
3 | 3 | type: information |
4 | 4 | title: information (ou info) |
5 | - text: Texto informativo. Não conta para avaliação. | |
5 | + text: | | |
6 | + Texto informativo. Não conta para avaliação. | |
7 | + | |
8 | + $$ p(x) = \frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{(x-\mu)^2}{2\sigma^2}} $$ | |
6 | 9 | # --------------------------------------------------------------------------- |
7 | 10 | - |
8 | 11 | ref: tut-success |
9 | 12 | type: success |
10 | 13 | title: success |
11 | - text: Texto de positivo (sucesso). Não conta para avaliação. | |
14 | + text: | | |
15 | + Texto de positivo (sucesso). Não conta para avaliação. | |
16 | + | |
17 | + ```C | |
18 | + int main() { | |
19 | + printf("Hello world!"); | |
20 | + return 0; // comentario | |
21 | + } | |
22 | + ``` | |
23 | + | |
24 | + Inline `code`. | |
25 | + | |
12 | 26 | # --------------------------------------------------------------------------- |
13 | 27 | - |
14 | 28 | ref: tut-warning |
15 | 29 | type: warning |
16 | 30 | title: warning (ou warn) |
17 | - text: Texto de aviso. Não conta para avaliação. | |
31 | + text: | | |
32 | + Texto de aviso. Não conta para avaliação. | |
33 | + | |
34 | + Left | Center | Right | |
35 | + -----------------|:-------------:|----------: | |
36 | + $\sin(x^2)$ | *hello* | $1600.00 | |
37 | + $\frac{1}{2\pi}$ | **world** | $12.50 | |
38 | + $\sqrt{\pi}$ | `code` | $1.99 | |
39 | + | |
18 | 40 | # ---------------------------------------------------------------------------- |
19 | 41 | - |
20 | 42 | ref: tut-alert |
... | ... | @@ -48,7 +70,7 @@ |
48 | 70 | - Opção 3 (sim) |
49 | 71 | correct: [1,-1,-1,1] |
50 | 72 | # opcionais e valores por defeito |
51 | - shuffle: True | |
73 | + shuffle: True | |
52 | 74 | # ---------------------------------------------------------------------------- |
53 | 75 | - |
54 | 76 | ref: tut-text |
... | ... | @@ -82,8 +104,8 @@ |
82 | 104 | type: textarea |
83 | 105 | title: textarea |
84 | 106 | text: | |
85 | - Resposta num bloco de texto que pode ser usado para introduzir código. | |
86 | - A resposta é avaliada por um programa externo. | |
107 | + Resposta num bloco de texto que pode ser usado para introduzir código. | |
108 | + A resposta é avaliada por um programa externo. | |
87 | 109 | O programa externo, recebe a resposta no stdin e devolve a classificação no stdout. |
88 | 110 | Neste exemplo, o programa de avaliação verifica se a resposta contém as três palavras red, green e blue. |
89 | 111 | correct: correct/correct-question.py | ... | ... |
demo/questions/questions.yaml
... | ... | @@ -70,7 +70,7 @@ |
70 | 70 | # --------------------------------------------------------------------------- |
71 | 71 | - |
72 | 72 | ref: fractions |
73 | - type: text-numeric | |
73 | + type: numeric-interval | |
74 | 74 | text: Quanto é 1/4? |
75 | 75 | correct: [0.249, 0.251] |
76 | 76 | # --------------------------------------------------------------------------- | ... | ... |
serve.py
... | ... | @@ -163,7 +163,7 @@ class TestHandler(BaseHandler): |
163 | 163 | |
164 | 164 | # --- REVIEW ------------------------------------------------------------- |
165 | 165 | class ReviewHandler(BaseHandler): |
166 | - templates = { | |
166 | + _templates = { | |
167 | 167 | 'radio': 'review-question-radio.html', |
168 | 168 | 'checkbox': 'review-question-checkbox.html', |
169 | 169 | 'text': 'review-question-text.html', |
... | ... | @@ -181,12 +181,13 @@ class ReviewHandler(BaseHandler): |
181 | 181 | |
182 | 182 | @tornado.web.authenticated |
183 | 183 | def get(self): |
184 | - test_id=45 # FIXME | |
185 | - | |
186 | 184 | uid = self.current_user |
187 | 185 | if uid != '0': |
188 | 186 | self.redirect('/') # FIXME 404 not found? |
189 | 187 | |
188 | + test_id = self.get_query_argument('test_id', None) | |
189 | + # FIXME if test_id does not exist... | |
190 | + | |
190 | 191 | fname = self.testapp.get_json_filename_of_test(test_id) |
191 | 192 | try: |
192 | 193 | f = open(path.expanduser(fname)) |
... | ... | @@ -197,7 +198,7 @@ class ReviewHandler(BaseHandler): |
197 | 198 | else: |
198 | 199 | with f: |
199 | 200 | t = json.load(f) |
200 | - self.render('review.html', t=t, md=md_to_html_review, templ=self.templates) | |
201 | + self.render('review.html', t=t, md=md_to_html_review, templ=self._templates) | |
201 | 202 | |
202 | 203 | |
203 | 204 | # @cherrypy.expose | ... | ... |
templates/grade.html
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | <!-- ===================================================================== --> |
17 | 17 | <body> |
18 | 18 | |
19 | -<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-dark"> | |
19 | +<nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-dark"> | |
20 | 20 | <a class="navbar-brand" href="#">{{t['title']}}</a> |
21 | 21 | <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> |
22 | 22 | <span class="navbar-toggler-icon"></span> | ... | ... |
templates/question-alert.html
templates/question-checkbox.html
... | ... | @@ -4,13 +4,12 @@ |
4 | 4 | {% block answer %} |
5 | 5 | <fieldset data-role="controlgroup"> |
6 | 6 | <div class="list-group"> |
7 | - {% for n, opt in enumerate(question['options']) %} | |
7 | + {% for n, opt in enumerate(q['options']) %} | |
8 | 8 | <a class="list-group-item"> |
9 | 9 | <input type="checkbox" id="{{i}}:{{n}}" name="{{i}}" value="{{n}}"> |
10 | - <label for="{{n}}">{{ md(opt, question['ref'], question['files']) }}</label> | |
10 | + <label for="{{n}}">{{ md(opt, q['ref'], q['files']) }}</label> | |
11 | 11 | </a> |
12 | 12 | {% end %} |
13 | 13 | </div> |
14 | 14 | </fieldset> |
15 | -<input type="hidden" name="question_ref" value="{{ question['ref'] }}"> | |
16 | 15 | {% end %} |
17 | 16 | \ No newline at end of file | ... | ... |
templates/question-information.html
... | ... | @@ -3,10 +3,10 @@ |
3 | 3 | |
4 | 4 | <div class="alert alert-info border-info" role="alert"> |
5 | 5 | <h3> |
6 | - {{ question['title'] }} | |
6 | + {{ q['title'] }} | |
7 | 7 | </h3> |
8 | 8 | |
9 | 9 | <div id="text"> |
10 | - {{ md(question['text']) }} | |
10 | + {{ md(q['text'], q) }} | |
11 | 11 | </div> |
12 | 12 | </div> |
13 | 13 | \ No newline at end of file | ... | ... |
templates/question-radio.html
... | ... | @@ -4,10 +4,10 @@ |
4 | 4 | {% block answer %} |
5 | 5 | <fieldset data-role="controlgroup"> |
6 | 6 | <div class="list-group"> |
7 | - {% for n, opt in enumerate(question['options']) %} | |
7 | + {% for n, opt in enumerate(q['options']) %} | |
8 | 8 | <a class="list-group-item"> |
9 | 9 | <input type="radio" id="{{i}}:{{n}}" name="{{i}}" value="{{n}}"> |
10 | - <label for="{{n}}">{{ md(opt, question['ref'], question['files']) }}</label> | |
10 | + <label for="{{n}}">{{ md(opt, q['ref'], q['files']) }}</label> | |
11 | 11 | </a> |
12 | 12 | {% end %} |
13 | 13 | </div> | ... | ... |
templates/question-success.html
templates/question-textarea.html
1 | 1 | {% extends "question.html" %} |
2 | 2 | |
3 | 3 | {% block answer %} |
4 | - | |
5 | -<textarea class="form-control" rows="{{ question['lines'] }}" name="{{i}}">{{ question['answer'] or '' }}</textarea><br /> | |
6 | -<input type="hidden" name="question_ref" value="{{ question['ref'] }}"> | |
7 | - | |
4 | +<textarea class="form-control" rows="{{q['lines']}}" name="{{i}}">{{q['answer'] or ''}}</textarea><br /> | |
8 | 5 | {% end %} | ... | ... |
templates/question-warning.html
templates/question.html
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | {% block question %} |
4 | 4 | <div class="card border-dark mb-3"> |
5 | 5 | <h5 class="card-header text-white bg-dark"> |
6 | - {{ i+1 }}. {{ question['title'] }} | |
6 | + {{ i+1 }}. {{ q['title'] }} | |
7 | 7 | <div class="pull-right"> |
8 | 8 | <small>Classificar </small> |
9 | 9 | <input type="checkbox" class="question_disabler" data-size="mini" name="answered-{{i}}" id="answered-{{i}}" checked=""> |
... | ... | @@ -11,14 +11,14 @@ |
11 | 11 | </h5> |
12 | 12 | <div class="card-body"> |
13 | 13 | <div id="text"> |
14 | - {{ question['text'] }} | |
14 | + {{ md(q['text'], md) }} | |
15 | 15 | </div> |
16 | 16 | |
17 | 17 | {% block answer %}{% end %} |
18 | 18 | |
19 | 19 | <p class="text-right"> |
20 | 20 | <small> |
21 | - (Cotação: {{ round(question['points'], 2) }} pontos) | |
21 | + (Cotação: {{ round(q['points'], 2) }} pontos) | |
22 | 22 | </small> |
23 | 23 | </p> |
24 | 24 | ... | ... |
templates/review-question-checkbox.html
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | {% if q['correct'][n] > 0 %} |
12 | 12 | <div class="text-right text-success"> |
13 | 13 | <i class="fa fa-check" aria-hidden="true"></i> |
14 | - </div> | |
14 | + </div> | |
15 | 15 | {% else %} |
16 | 16 | <div class="text-right text-danger"> |
17 | 17 | <i class="fa fa-close" aria-hidden="true"></i> |
... | ... | @@ -19,9 +19,8 @@ |
19 | 19 | {% end %} |
20 | 20 | {% else %} |
21 | 21 | {{ md('<i class="fa fa-square-o" aria-hidden="true"></i> ' + opt, q) }} |
22 | - | |
23 | 22 | {% if q['correct'][n] > 0 %} |
24 | - <div class="text-right text-info"> | |
23 | + <div class="text-right text-danger"> | |
25 | 24 | <i class="fa fa-close" aria-hidden="true"></i> |
26 | 25 | </div> |
27 | 26 | {% elif q['correct'][n] < 0 %} | ... | ... |
templates/review-question-text.html
templates/review-question.html
... | ... | @@ -16,16 +16,16 @@ |
16 | 16 | </h5> |
17 | 17 | |
18 | 18 | <div class="card-body"> |
19 | - <div id="text"> | |
19 | + <p id="text"> | |
20 | 20 | {{ q['text'] }} |
21 | - </div> | |
21 | + </p> | |
22 | 22 | |
23 | 23 | {% block answer %}{% end %} |
24 | 24 | |
25 | 25 | {% if t['show_points'] %} |
26 | 26 | <p class="text-right"> |
27 | 27 | <small> |
28 | - (Cotação: {{ q['points'] }}) | |
28 | + (Cotação: {{ round(q['points'], 2) }}) | |
29 | 29 | </small> |
30 | 30 | </p> |
31 | 31 | {% end %} |
... | ... | @@ -43,14 +43,14 @@ |
43 | 43 | {% elif q['grade'] > 0.49 %} |
44 | 44 | <p class="text-warning"> |
45 | 45 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> |
46 | - {{ round(q['grade'] * q['points'], 2) }} | |
46 | + {{ round(q['grade'] * q['points'], 2) }} | |
47 | 47 | pontos<br> |
48 | 48 | {{ q['comments'] }} |
49 | 49 | </p> |
50 | 50 | {% else %} |
51 | 51 | <p class="text-danger"> |
52 | 52 | <i class="fa fa-thumbs-o-down" aria-hidden="true"></i> |
53 | - {{ round(q['grade'] * q['points'], 2) }} | |
53 | + {{ round(q['grade'] * q['points'], 2) }} | |
54 | 54 | pontos<br> |
55 | 55 | {{ q['comments'] }} |
56 | 56 | </p> | ... | ... |
templates/review.html
... | ... | @@ -27,32 +27,53 @@ |
27 | 27 | </head> |
28 | 28 | <!-- ===================================================================== --> |
29 | 29 | <body> |
30 | -<nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-primary"> | |
30 | +<nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-dark"> | |
31 | 31 | <a class="navbar-brand" href="#">Revisão de prova</a> |
32 | 32 | </nav> |
33 | 33 | <!-- ===================================================================== --> |
34 | 34 | <div class="container"> |
35 | - <div class="jumbotron"> | |
36 | - <big> | |
37 | - <dl class="dl-horizontal"> | |
38 | - <dt>Prova:</dt><dd>{{t['title']}}</dd> | |
39 | - <dt>Nome:</dt><dd>{{t['student']['name']}}</dd> | |
40 | - <dt>Número:</dt><dd>{{t['student']['number']}}</dd> | |
41 | - <dt>Início:</dt><dd>{{t['start_time'][:19]}}</dd> | |
42 | - <dt>Fim:</dt><dd>{{t['finish_time'][:19]}}</dd> | |
43 | - <dt>Nota:</dt> | |
44 | - <dd> | |
45 | - <span class="label label-primary">{{ t['grade'] }}</span> valores | |
46 | - {% if t['state'] == 'QUIT' %} | |
47 | - (DESISTÊNCIA) | |
48 | - {% end %} | |
49 | - </dd> | |
50 | - {% if t['comment'] != '' %} | |
51 | - <dt>Comentário:</dt><dd>{{ t['comment'] }}</dd> | |
52 | - {% end %} | |
53 | - </dl> | |
54 | - </big> | |
55 | - </div> | |
35 | + <div class="jumbotron"> | |
36 | + <h1 class="display-5">{{ t['title'] }}</h1> | |
37 | + {{ t.get('duration', '') }} | |
38 | + <hr> | |
39 | + | |
40 | + <h3> | |
41 | + <div class="row"> | |
42 | + <!-- <label for="nome" class="col-sm-1">Nome:</label> --> | |
43 | + <div class="col-sm-8" id="nome">{{t['student']['name']}}</div> | |
44 | + <label for="nome" class="col-sm-1">Nº.</label> | |
45 | + <div class="col-sm-3" id="numero">{{t['student']['number']}}</div> | |
46 | + </div> | |
47 | + </h3> | |
48 | + | |
49 | + <h5> | |
50 | + <div class="row"> | |
51 | + <label for="inicio" class="col-sm-2">Início:</label> | |
52 | + <div class="col-sm-10" id="inicio">{{t['start_time'][:19]}}</div> | |
53 | + </div> | |
54 | + <div class="row"> | |
55 | + <label for="fim" class="col-sm-2">Fim:</label> | |
56 | + <div class="col-sm-10" id="fim">{{t['finish_time'][:19]}}</div> | |
57 | + </div> | |
58 | + </h5> | |
59 | + <h3> | |
60 | + <div class="row"> | |
61 | + <label for="nota" class="col-sm-2">Nota:</label> | |
62 | + <div class="col-sm-10" id="nota"> | |
63 | + <span class="badge badge-primary">{{ t['grade'] }}</span> valores | |
64 | + {% if t['state'] == 'QUIT' %} | |
65 | + (DESISTÊNCIA) | |
66 | + {% end %} | |
67 | + </div> | |
68 | + </div> | |
69 | + {% if t['comment'] != '' %} | |
70 | + <div class="row"> | |
71 | + <label for="comentario" class="col-sm-2">Comentário:</label> | |
72 | + <div class="col-sm-10" id="comentario">{{ t['comment'] }}</div> | |
73 | + </div> | |
74 | + {% end %} | |
75 | + </h3> | |
76 | + </div> | |
56 | 77 | |
57 | 78 | {% for i, q in enumerate(t['questions']) %} |
58 | 79 | {% module Template(templ[q['type']], i=i, q=q, md=md, t=t) %} | ... | ... |
templates/test.html
... | ... | @@ -61,17 +61,28 @@ |
61 | 61 | </span> |
62 | 62 | </div> |
63 | 63 | </nav> |
64 | -<!-- ===================================================================== --> | |
65 | 64 | <div class="container"> |
66 | 65 | |
67 | 66 | <div class="jumbotron"> |
68 | 67 | <h1 class="display-5">{{ t['title'] }}</h1> |
69 | 68 | {{ t.get('duration', '') }} |
69 | + <hr> | |
70 | + | |
71 | + <h5> | |
72 | + <div class="row"> | |
73 | + <label for="inicio" class="col-sm-2">Início:</label> | |
74 | + <div class="col-sm-10" id="inicio">{{ str(t['start_time'].time())[:8]}}</div> | |
75 | + </div> | |
76 | + <div class="row"> | |
77 | + <label for="duracao" class="col-sm-2">Duração:</label> | |
78 | + <div class="col-sm-10" id="duracao">{{ t.get('duration', chr(8734)) }}</div> | |
79 | + </div> | |
80 | + </h5> | |
70 | 81 | </div> |
71 | 82 | |
72 | 83 | <form action="/test" method="post" id="test" autocomplete="off"> |
73 | 84 | {% for i, q in enumerate(t['questions']) %} |
74 | - {% module Template(templ[q['type']], i=i, question=q, md=md) %} | |
85 | + {% module Template(templ[q['type']], i=i, q=q, md=md) %} | |
75 | 86 | {% end %} |
76 | 87 | |
77 | 88 | <div class="form-row"> |
... | ... | @@ -84,47 +95,51 @@ |
84 | 95 | </div> |
85 | 96 | </form> |
86 | 97 | <hr> |
98 | +</div> <!-- container --> | |
87 | 99 | |
88 | - <!-- Modal de confirmacao submissao --> | |
89 | - <div class="modal fade" id="confirmar" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |
90 | - <div class="modal-dialog" role="document"> | |
91 | - <div class="modal-content"> | |
92 | - <div class="modal-header"> | |
93 | - <h4 class="modal-title">Deseja submeter o teste?</h4> | |
94 | - </div> | |
95 | - <div class="modal-body"> | |
96 | - O teste será enviado para classificação e já não poderá voltar atrás. | |
97 | - Antes de submeter, veja se respondeu a todas as questões e desactive as que não pretende classificar. | |
98 | - </div> | |
99 | - <div class="modal-footer"> | |
100 | - <button type="button" class="btn btn-danger btn-lg" data-dismiss="modal">Oops, NÃO!!!</button> | |
101 | - <button form="test" type="submit" class="btn btn-success btn-lg">Sim, submeter...</button> | |
102 | - </div> | |
103 | - </div> | |
100 | +<!-- ===================================================================== --> | |
101 | + | |
102 | +<!-- Modal de confirmacao submissao --> | |
103 | +<div class="modal fade" id="confirmar" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |
104 | + <div class="modal-dialog" role="document"> | |
105 | + <div class="modal-content"> | |
106 | + <div class="modal-header"> | |
107 | + <h4 class="modal-title">Deseja submeter o teste?</h4> | |
108 | + </div> | |
109 | + <div class="modal-body"> | |
110 | + O teste será enviado para classificação e já não poderá voltar atrás. | |
111 | + Antes de submeter, veja se respondeu a todas as questões e desactive as que não pretende classificar. | |
112 | + </div> | |
113 | + <div class="modal-footer"> | |
114 | + <button type="button" class="btn btn-danger btn-lg" data-dismiss="modal">Oops, NÃO!!!</button> | |
115 | + <button form="test" type="submit" class="btn btn-success btn-lg">Sim, submeter...</button> | |
104 | 116 | </div> |
105 | 117 | </div> |
118 | + </div> | |
119 | +</div> | |
106 | 120 | |
107 | - <!-- Modal de confirmacao sair --> | |
108 | - <div class="modal fade" id="sair" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |
109 | - <div class="modal-dialog" role="document"> | |
110 | - <div class="modal-content"> | |
111 | - <div class="modal-header"> | |
112 | - <h4 class="modal-title">Deseja desistir?</h4> | |
113 | - </div> | |
114 | - <div class="modal-body"> | |
115 | - Se desistir, será registada a desistência da prova e terá uma classificação de 0 valores. | |
116 | - </div> | |
117 | - <div class="modal-footer"> | |
118 | - <button type="button" class="btn btn-danger btn-lg" data-dismiss="modal">Não!</button> | |
119 | - <a href="/giveup" class="btn btn-success btn-lg" role="button">Sim, desisto</a> | |
120 | - </div> | |
121 | - </div> | |
121 | +<!-- Modal de confirmacao sair --> | |
122 | +<div class="modal fade" id="sair" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |
123 | + <div class="modal-dialog" role="document"> | |
124 | + <div class="modal-content"> | |
125 | + <div class="modal-header"> | |
126 | + <h4 class="modal-title">Deseja desistir?</h4> | |
127 | + </div> | |
128 | + <div class="modal-body"> | |
129 | + Se desistir, será registada a desistência da prova e terá uma classificação de 0 valores. | |
130 | + </div> | |
131 | + <div class="modal-footer"> | |
132 | + <button type="button" class="btn btn-danger btn-lg" data-dismiss="modal">Não!</button> | |
133 | + <a href="/giveup" class="btn btn-success btn-lg" role="button">Sim, desisto</a> | |
122 | 134 | </div> |
123 | 135 | </div> |
124 | - | |
136 | + </div> | |
125 | 137 | </div> |
126 | 138 | |
127 | 139 | |
140 | + | |
141 | +<!-- ===================================================================== --> | |
142 | + | |
128 | 143 | <!-- Scripts --> |
129 | 144 | <script src="/static/js/jquery.min.js"></script> |
130 | 145 | <script src="/static/popper/umd/popper.min.js"></script> | ... | ... |
test.py
... | ... | @@ -145,6 +145,7 @@ class TestFactory(dict): |
145 | 145 | test = [] |
146 | 146 | total_points = 0.0 |
147 | 147 | |
148 | + n = 1 | |
148 | 149 | for qq in self['questions']: |
149 | 150 | # generate Question() selected randomly from list of references |
150 | 151 | qref = random.choice(qq['ref']) |
... | ... | @@ -160,6 +161,8 @@ class TestFactory(dict): |
160 | 161 | q['points'] = qq.get('points', 0.0) |
161 | 162 | else: |
162 | 163 | q['points'] = qq.get('points', 1.0) |
164 | + q['number'] = n | |
165 | + n += 1 | |
163 | 166 | |
164 | 167 | total_points += q['points'] |
165 | 168 | test.append(q) | ... | ... |
tools.py
... | ... | @@ -58,6 +58,7 @@ def run_script(script, stdin='', timeout=5): |
58 | 58 | else: |
59 | 59 | return output |
60 | 60 | |
61 | +# --------------------------------------------------------------------------- | |
61 | 62 | def md_to_html(text, ref=None, files={}): |
62 | 63 | if ref is not None: |
63 | 64 | # given q['ref'] and q['files'] replaces references to files by a |
... | ... | @@ -72,6 +73,7 @@ def md_to_html(text, ref=None, files={}): |
72 | 73 | 'markdown.extensions.sane_lists' |
73 | 74 | ]) |
74 | 75 | |
76 | +# --------------------------------------------------------------------------- | |
75 | 77 | def md_to_html_review(text, q): |
76 | 78 | for k,f in q['files'].items(): |
77 | 79 | text = text.replace(k, '/absfile?name={}'.format(q['files'][k])) | ... | ... |