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 | # BUGS | 2 | # BUGS |
| 3 | 3 | ||
| 4 | +- markdown no teste nao funciona | ||
| 4 | - numeracao das perguntas do teste esta a contar com paineis informativos... | 5 | - numeracao das perguntas do teste esta a contar com paineis informativos... |
| 5 | - servir imagens das perguntas | 6 | - servir imagens das perguntas |
| 6 | -- review de um teste nao funciona (hardcoded...) | ||
| 7 | -- testar opcao --allow-all | ||
| 8 | - como alterar configuracao para mostrar logs de debug? | 7 | - como alterar configuracao para mostrar logs de debug? |
| 9 | - hints nao funciona | 8 | - hints nao funciona |
| 10 | - uniformizar question.py com a de aprendizations... | 9 | - uniformizar question.py com a de aprendizations... |
| @@ -14,6 +13,14 @@ | @@ -14,6 +13,14 @@ | ||
| 14 | 13 | ||
| 15 | # TODO | 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 | - Gerar pdf's com todos os testes no final (pdfkit). | 24 | - Gerar pdf's com todos os testes no final (pdfkit). |
| 18 | - manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento | 25 | - manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento |
| 19 | 26 | ||
| @@ -31,6 +38,7 @@ | @@ -31,6 +38,7 @@ | ||
| 31 | 38 | ||
| 32 | # FIXED | 39 | # FIXED |
| 33 | 40 | ||
| 41 | +- review de um teste nao funciona (hardcoded...) | ||
| 34 | - testar SSL | 42 | - testar SSL |
| 35 | - text-numeric não está a gerar a pergunta. faltam templates? | 43 | - text-numeric não está a gerar a pergunta. faltam templates? |
| 36 | - testar perguntas warning/warn | 44 | - testar perguntas warning/warn |
demo/questions/questions-tutorial.yaml
| @@ -2,19 +2,41 @@ | @@ -2,19 +2,41 @@ | ||
| 2 | ref: tut-information | 2 | ref: tut-information |
| 3 | type: information | 3 | type: information |
| 4 | title: information (ou info) | 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 | ref: tut-success | 11 | ref: tut-success |
| 9 | type: success | 12 | type: success |
| 10 | title: success | 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 | ref: tut-warning | 28 | ref: tut-warning |
| 15 | type: warning | 29 | type: warning |
| 16 | title: warning (ou warn) | 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 | ref: tut-alert | 42 | ref: tut-alert |
| @@ -48,7 +70,7 @@ | @@ -48,7 +70,7 @@ | ||
| 48 | - Opção 3 (sim) | 70 | - Opção 3 (sim) |
| 49 | correct: [1,-1,-1,1] | 71 | correct: [1,-1,-1,1] |
| 50 | # opcionais e valores por defeito | 72 | # opcionais e valores por defeito |
| 51 | - shuffle: True | 73 | + shuffle: True |
| 52 | # ---------------------------------------------------------------------------- | 74 | # ---------------------------------------------------------------------------- |
| 53 | - | 75 | - |
| 54 | ref: tut-text | 76 | ref: tut-text |
| @@ -82,8 +104,8 @@ | @@ -82,8 +104,8 @@ | ||
| 82 | type: textarea | 104 | type: textarea |
| 83 | title: textarea | 105 | title: textarea |
| 84 | text: | | 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 | O programa externo, recebe a resposta no stdin e devolve a classificação no stdout. | 109 | O programa externo, recebe a resposta no stdin e devolve a classificação no stdout. |
| 88 | Neste exemplo, o programa de avaliação verifica se a resposta contém as três palavras red, green e blue. | 110 | Neste exemplo, o programa de avaliação verifica se a resposta contém as três palavras red, green e blue. |
| 89 | correct: correct/correct-question.py | 111 | correct: correct/correct-question.py |
demo/questions/questions.yaml
| @@ -70,7 +70,7 @@ | @@ -70,7 +70,7 @@ | ||
| 70 | # --------------------------------------------------------------------------- | 70 | # --------------------------------------------------------------------------- |
| 71 | - | 71 | - |
| 72 | ref: fractions | 72 | ref: fractions |
| 73 | - type: text-numeric | 73 | + type: numeric-interval |
| 74 | text: Quanto é 1/4? | 74 | text: Quanto é 1/4? |
| 75 | correct: [0.249, 0.251] | 75 | correct: [0.249, 0.251] |
| 76 | # --------------------------------------------------------------------------- | 76 | # --------------------------------------------------------------------------- |
serve.py
| @@ -163,7 +163,7 @@ class TestHandler(BaseHandler): | @@ -163,7 +163,7 @@ class TestHandler(BaseHandler): | ||
| 163 | 163 | ||
| 164 | # --- REVIEW ------------------------------------------------------------- | 164 | # --- REVIEW ------------------------------------------------------------- |
| 165 | class ReviewHandler(BaseHandler): | 165 | class ReviewHandler(BaseHandler): |
| 166 | - templates = { | 166 | + _templates = { |
| 167 | 'radio': 'review-question-radio.html', | 167 | 'radio': 'review-question-radio.html', |
| 168 | 'checkbox': 'review-question-checkbox.html', | 168 | 'checkbox': 'review-question-checkbox.html', |
| 169 | 'text': 'review-question-text.html', | 169 | 'text': 'review-question-text.html', |
| @@ -181,12 +181,13 @@ class ReviewHandler(BaseHandler): | @@ -181,12 +181,13 @@ class ReviewHandler(BaseHandler): | ||
| 181 | 181 | ||
| 182 | @tornado.web.authenticated | 182 | @tornado.web.authenticated |
| 183 | def get(self): | 183 | def get(self): |
| 184 | - test_id=45 # FIXME | ||
| 185 | - | ||
| 186 | uid = self.current_user | 184 | uid = self.current_user |
| 187 | if uid != '0': | 185 | if uid != '0': |
| 188 | self.redirect('/') # FIXME 404 not found? | 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 | fname = self.testapp.get_json_filename_of_test(test_id) | 191 | fname = self.testapp.get_json_filename_of_test(test_id) |
| 191 | try: | 192 | try: |
| 192 | f = open(path.expanduser(fname)) | 193 | f = open(path.expanduser(fname)) |
| @@ -197,7 +198,7 @@ class ReviewHandler(BaseHandler): | @@ -197,7 +198,7 @@ class ReviewHandler(BaseHandler): | ||
| 197 | else: | 198 | else: |
| 198 | with f: | 199 | with f: |
| 199 | t = json.load(f) | 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 | # @cherrypy.expose | 204 | # @cherrypy.expose |
templates/grade.html
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | <!-- ===================================================================== --> | 16 | <!-- ===================================================================== --> |
| 17 | <body> | 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 | <a class="navbar-brand" href="#">{{t['title']}}</a> | 20 | <a class="navbar-brand" href="#">{{t['title']}}</a> |
| 21 | <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> | 21 | <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> |
| 22 | <span class="navbar-toggler-icon"></span> | 22 | <span class="navbar-toggler-icon"></span> |
templates/question-alert.html
| @@ -3,10 +3,10 @@ | @@ -3,10 +3,10 @@ | ||
| 3 | 3 | ||
| 4 | <div class="alert alert-danger border-danger" role="alert"> | 4 | <div class="alert alert-danger border-danger" role="alert"> |
| 5 | <h3> | 5 | <h3> |
| 6 | - {{ question['title'] }} | 6 | + {{ q['title'] }} |
| 7 | </h3> | 7 | </h3> |
| 8 | 8 | ||
| 9 | <div id="text"> | 9 | <div id="text"> |
| 10 | - {{ md(question['text']) }} | 10 | + {{ md(q['text'], q) }} |
| 11 | </div> | 11 | </div> |
| 12 | </div> | 12 | </div> |
templates/question-checkbox.html
| @@ -4,13 +4,12 @@ | @@ -4,13 +4,12 @@ | ||
| 4 | {% block answer %} | 4 | {% block answer %} |
| 5 | <fieldset data-role="controlgroup"> | 5 | <fieldset data-role="controlgroup"> |
| 6 | <div class="list-group"> | 6 | <div class="list-group"> |
| 7 | - {% for n, opt in enumerate(question['options']) %} | 7 | + {% for n, opt in enumerate(q['options']) %} |
| 8 | <a class="list-group-item"> | 8 | <a class="list-group-item"> |
| 9 | <input type="checkbox" id="{{i}}:{{n}}" name="{{i}}" value="{{n}}"> | 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 | </a> | 11 | </a> |
| 12 | {% end %} | 12 | {% end %} |
| 13 | </div> | 13 | </div> |
| 14 | </fieldset> | 14 | </fieldset> |
| 15 | -<input type="hidden" name="question_ref" value="{{ question['ref'] }}"> | ||
| 16 | {% end %} | 15 | {% end %} |
| 17 | \ No newline at end of file | 16 | \ No newline at end of file |
templates/question-information.html
| @@ -3,10 +3,10 @@ | @@ -3,10 +3,10 @@ | ||
| 3 | 3 | ||
| 4 | <div class="alert alert-info border-info" role="alert"> | 4 | <div class="alert alert-info border-info" role="alert"> |
| 5 | <h3> | 5 | <h3> |
| 6 | - {{ question['title'] }} | 6 | + {{ q['title'] }} |
| 7 | </h3> | 7 | </h3> |
| 8 | 8 | ||
| 9 | <div id="text"> | 9 | <div id="text"> |
| 10 | - {{ md(question['text']) }} | 10 | + {{ md(q['text'], q) }} |
| 11 | </div> | 11 | </div> |
| 12 | </div> | 12 | </div> |
| 13 | \ No newline at end of file | 13 | \ No newline at end of file |
templates/question-radio.html
| @@ -4,10 +4,10 @@ | @@ -4,10 +4,10 @@ | ||
| 4 | {% block answer %} | 4 | {% block answer %} |
| 5 | <fieldset data-role="controlgroup"> | 5 | <fieldset data-role="controlgroup"> |
| 6 | <div class="list-group"> | 6 | <div class="list-group"> |
| 7 | - {% for n, opt in enumerate(question['options']) %} | 7 | + {% for n, opt in enumerate(q['options']) %} |
| 8 | <a class="list-group-item"> | 8 | <a class="list-group-item"> |
| 9 | <input type="radio" id="{{i}}:{{n}}" name="{{i}}" value="{{n}}"> | 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 | </a> | 11 | </a> |
| 12 | {% end %} | 12 | {% end %} |
| 13 | </div> | 13 | </div> |
templates/question-success.html
| @@ -3,10 +3,10 @@ | @@ -3,10 +3,10 @@ | ||
| 3 | 3 | ||
| 4 | <div class="alert alert-success border-success" role="alert"> | 4 | <div class="alert alert-success border-success" role="alert"> |
| 5 | <h3> | 5 | <h3> |
| 6 | - {{ question['title'] }} | 6 | + {{ q['title'] }} |
| 7 | </h3> | 7 | </h3> |
| 8 | 8 | ||
| 9 | <div id="text"> | 9 | <div id="text"> |
| 10 | - {{ md(question['text']) }} | 10 | + {{ md(q['text'], q) }} |
| 11 | </div> | 11 | </div> |
| 12 | </div> | 12 | </div> |
templates/question-textarea.html
| 1 | {% extends "question.html" %} | 1 | {% extends "question.html" %} |
| 2 | 2 | ||
| 3 | {% block answer %} | 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 | {% end %} | 5 | {% end %} |
templates/question-warning.html
| @@ -3,10 +3,10 @@ | @@ -3,10 +3,10 @@ | ||
| 3 | 3 | ||
| 4 | <div class="alert alert-warning border-warning" role="alert"> | 4 | <div class="alert alert-warning border-warning" role="alert"> |
| 5 | <h3> | 5 | <h3> |
| 6 | - {{ question['title'] }} | 6 | + {{ q['title'] }} |
| 7 | </h3> | 7 | </h3> |
| 8 | 8 | ||
| 9 | <div id="text"> | 9 | <div id="text"> |
| 10 | - {{ md(question['text']) }} | 10 | + {{ md(q['text'], q) }} |
| 11 | </div> | 11 | </div> |
| 12 | </div> | 12 | </div> |
templates/question.html
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | {% block question %} | 3 | {% block question %} |
| 4 | <div class="card border-dark mb-3"> | 4 | <div class="card border-dark mb-3"> |
| 5 | <h5 class="card-header text-white bg-dark"> | 5 | <h5 class="card-header text-white bg-dark"> |
| 6 | - {{ i+1 }}. {{ question['title'] }} | 6 | + {{ i+1 }}. {{ q['title'] }} |
| 7 | <div class="pull-right"> | 7 | <div class="pull-right"> |
| 8 | <small>Classificar </small> | 8 | <small>Classificar </small> |
| 9 | <input type="checkbox" class="question_disabler" data-size="mini" name="answered-{{i}}" id="answered-{{i}}" checked=""> | 9 | <input type="checkbox" class="question_disabler" data-size="mini" name="answered-{{i}}" id="answered-{{i}}" checked=""> |
| @@ -11,14 +11,14 @@ | @@ -11,14 +11,14 @@ | ||
| 11 | </h5> | 11 | </h5> |
| 12 | <div class="card-body"> | 12 | <div class="card-body"> |
| 13 | <div id="text"> | 13 | <div id="text"> |
| 14 | - {{ question['text'] }} | 14 | + {{ md(q['text'], md) }} |
| 15 | </div> | 15 | </div> |
| 16 | 16 | ||
| 17 | {% block answer %}{% end %} | 17 | {% block answer %}{% end %} |
| 18 | 18 | ||
| 19 | <p class="text-right"> | 19 | <p class="text-right"> |
| 20 | <small> | 20 | <small> |
| 21 | - (Cotação: {{ round(question['points'], 2) }} pontos) | 21 | + (Cotação: {{ round(q['points'], 2) }} pontos) |
| 22 | </small> | 22 | </small> |
| 23 | </p> | 23 | </p> |
| 24 | 24 |
templates/review-question-checkbox.html
| @@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
| 11 | {% if q['correct'][n] > 0 %} | 11 | {% if q['correct'][n] > 0 %} |
| 12 | <div class="text-right text-success"> | 12 | <div class="text-right text-success"> |
| 13 | <i class="fa fa-check" aria-hidden="true"></i> | 13 | <i class="fa fa-check" aria-hidden="true"></i> |
| 14 | - </div> | 14 | + </div> |
| 15 | {% else %} | 15 | {% else %} |
| 16 | <div class="text-right text-danger"> | 16 | <div class="text-right text-danger"> |
| 17 | <i class="fa fa-close" aria-hidden="true"></i> | 17 | <i class="fa fa-close" aria-hidden="true"></i> |
| @@ -19,9 +19,8 @@ | @@ -19,9 +19,8 @@ | ||
| 19 | {% end %} | 19 | {% end %} |
| 20 | {% else %} | 20 | {% else %} |
| 21 | {{ md('<i class="fa fa-square-o" aria-hidden="true"></i> ' + opt, q) }} | 21 | {{ md('<i class="fa fa-square-o" aria-hidden="true"></i> ' + opt, q) }} |
| 22 | - | ||
| 23 | {% if q['correct'][n] > 0 %} | 22 | {% if q['correct'][n] > 0 %} |
| 24 | - <div class="text-right text-info"> | 23 | + <div class="text-right text-danger"> |
| 25 | <i class="fa fa-close" aria-hidden="true"></i> | 24 | <i class="fa fa-close" aria-hidden="true"></i> |
| 26 | </div> | 25 | </div> |
| 27 | {% elif q['correct'][n] < 0 %} | 26 | {% elif q['correct'][n] < 0 %} |
templates/review-question-text.html
| @@ -2,5 +2,12 @@ | @@ -2,5 +2,12 @@ | ||
| 2 | {% autoescape %} | 2 | {% autoescape %} |
| 3 | 3 | ||
| 4 | {% block answer %} | 4 | {% block answer %} |
| 5 | + | ||
| 6 | +<div class="card bg-light"> | ||
| 7 | + <div class="card-body"> | ||
| 5 | <pre>{{ q['answer'] if q['answer'] is not None else '' }}</pre> | 8 | <pre>{{ q['answer'] if q['answer'] is not None else '' }}</pre> |
| 9 | + </div> | ||
| 10 | +</div> | ||
| 11 | + | ||
| 12 | + | ||
| 6 | {% end %} | 13 | {% end %} |
| 7 | \ No newline at end of file | 14 | \ No newline at end of file |
templates/review-question.html
| @@ -16,16 +16,16 @@ | @@ -16,16 +16,16 @@ | ||
| 16 | </h5> | 16 | </h5> |
| 17 | 17 | ||
| 18 | <div class="card-body"> | 18 | <div class="card-body"> |
| 19 | - <div id="text"> | 19 | + <p id="text"> |
| 20 | {{ q['text'] }} | 20 | {{ q['text'] }} |
| 21 | - </div> | 21 | + </p> |
| 22 | 22 | ||
| 23 | {% block answer %}{% end %} | 23 | {% block answer %}{% end %} |
| 24 | 24 | ||
| 25 | {% if t['show_points'] %} | 25 | {% if t['show_points'] %} |
| 26 | <p class="text-right"> | 26 | <p class="text-right"> |
| 27 | <small> | 27 | <small> |
| 28 | - (Cotação: {{ q['points'] }}) | 28 | + (Cotação: {{ round(q['points'], 2) }}) |
| 29 | </small> | 29 | </small> |
| 30 | </p> | 30 | </p> |
| 31 | {% end %} | 31 | {% end %} |
| @@ -43,14 +43,14 @@ | @@ -43,14 +43,14 @@ | ||
| 43 | {% elif q['grade'] > 0.49 %} | 43 | {% elif q['grade'] > 0.49 %} |
| 44 | <p class="text-warning"> | 44 | <p class="text-warning"> |
| 45 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> | 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 | pontos<br> | 47 | pontos<br> |
| 48 | {{ q['comments'] }} | 48 | {{ q['comments'] }} |
| 49 | </p> | 49 | </p> |
| 50 | {% else %} | 50 | {% else %} |
| 51 | <p class="text-danger"> | 51 | <p class="text-danger"> |
| 52 | <i class="fa fa-thumbs-o-down" aria-hidden="true"></i> | 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 | pontos<br> | 54 | pontos<br> |
| 55 | {{ q['comments'] }} | 55 | {{ q['comments'] }} |
| 56 | </p> | 56 | </p> |
templates/review.html
| @@ -27,32 +27,53 @@ | @@ -27,32 +27,53 @@ | ||
| 27 | </head> | 27 | </head> |
| 28 | <!-- ===================================================================== --> | 28 | <!-- ===================================================================== --> |
| 29 | <body> | 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 | <a class="navbar-brand" href="#">Revisão de prova</a> | 31 | <a class="navbar-brand" href="#">Revisão de prova</a> |
| 32 | </nav> | 32 | </nav> |
| 33 | <!-- ===================================================================== --> | 33 | <!-- ===================================================================== --> |
| 34 | <div class="container"> | 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 | {% for i, q in enumerate(t['questions']) %} | 78 | {% for i, q in enumerate(t['questions']) %} |
| 58 | {% module Template(templ[q['type']], i=i, q=q, md=md, t=t) %} | 79 | {% module Template(templ[q['type']], i=i, q=q, md=md, t=t) %} |
templates/test.html
| @@ -61,17 +61,28 @@ | @@ -61,17 +61,28 @@ | ||
| 61 | </span> | 61 | </span> |
| 62 | </div> | 62 | </div> |
| 63 | </nav> | 63 | </nav> |
| 64 | -<!-- ===================================================================== --> | ||
| 65 | <div class="container"> | 64 | <div class="container"> |
| 66 | 65 | ||
| 67 | <div class="jumbotron"> | 66 | <div class="jumbotron"> |
| 68 | <h1 class="display-5">{{ t['title'] }}</h1> | 67 | <h1 class="display-5">{{ t['title'] }}</h1> |
| 69 | {{ t.get('duration', '') }} | 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 | </div> | 81 | </div> |
| 71 | 82 | ||
| 72 | <form action="/test" method="post" id="test" autocomplete="off"> | 83 | <form action="/test" method="post" id="test" autocomplete="off"> |
| 73 | {% for i, q in enumerate(t['questions']) %} | 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 | {% end %} | 86 | {% end %} |
| 76 | 87 | ||
| 77 | <div class="form-row"> | 88 | <div class="form-row"> |
| @@ -84,47 +95,51 @@ | @@ -84,47 +95,51 @@ | ||
| 84 | </div> | 95 | </div> |
| 85 | </form> | 96 | </form> |
| 86 | <hr> | 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 | </div> | 116 | </div> |
| 105 | </div> | 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 | </div> | 134 | </div> |
| 123 | </div> | 135 | </div> |
| 124 | - | 136 | + </div> |
| 125 | </div> | 137 | </div> |
| 126 | 138 | ||
| 127 | 139 | ||
| 140 | + | ||
| 141 | +<!-- ===================================================================== --> | ||
| 142 | + | ||
| 128 | <!-- Scripts --> | 143 | <!-- Scripts --> |
| 129 | <script src="/static/js/jquery.min.js"></script> | 144 | <script src="/static/js/jquery.min.js"></script> |
| 130 | <script src="/static/popper/umd/popper.min.js"></script> | 145 | <script src="/static/popper/umd/popper.min.js"></script> |
test.py
| @@ -145,6 +145,7 @@ class TestFactory(dict): | @@ -145,6 +145,7 @@ class TestFactory(dict): | ||
| 145 | test = [] | 145 | test = [] |
| 146 | total_points = 0.0 | 146 | total_points = 0.0 |
| 147 | 147 | ||
| 148 | + n = 1 | ||
| 148 | for qq in self['questions']: | 149 | for qq in self['questions']: |
| 149 | # generate Question() selected randomly from list of references | 150 | # generate Question() selected randomly from list of references |
| 150 | qref = random.choice(qq['ref']) | 151 | qref = random.choice(qq['ref']) |
| @@ -160,6 +161,8 @@ class TestFactory(dict): | @@ -160,6 +161,8 @@ class TestFactory(dict): | ||
| 160 | q['points'] = qq.get('points', 0.0) | 161 | q['points'] = qq.get('points', 0.0) |
| 161 | else: | 162 | else: |
| 162 | q['points'] = qq.get('points', 1.0) | 163 | q['points'] = qq.get('points', 1.0) |
| 164 | + q['number'] = n | ||
| 165 | + n += 1 | ||
| 163 | 166 | ||
| 164 | total_points += q['points'] | 167 | total_points += q['points'] |
| 165 | test.append(q) | 168 | test.append(q) |
tools.py
| @@ -58,6 +58,7 @@ def run_script(script, stdin='', timeout=5): | @@ -58,6 +58,7 @@ def run_script(script, stdin='', timeout=5): | ||
| 58 | else: | 58 | else: |
| 59 | return output | 59 | return output |
| 60 | 60 | ||
| 61 | +# --------------------------------------------------------------------------- | ||
| 61 | def md_to_html(text, ref=None, files={}): | 62 | def md_to_html(text, ref=None, files={}): |
| 62 | if ref is not None: | 63 | if ref is not None: |
| 63 | # given q['ref'] and q['files'] replaces references to files by a | 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,6 +73,7 @@ def md_to_html(text, ref=None, files={}): | ||
| 72 | 'markdown.extensions.sane_lists' | 73 | 'markdown.extensions.sane_lists' |
| 73 | ]) | 74 | ]) |
| 74 | 75 | ||
| 76 | +# --------------------------------------------------------------------------- | ||
| 75 | def md_to_html_review(text, q): | 77 | def md_to_html_review(text, q): |
| 76 | for k,f in q['files'].items(): | 78 | for k,f in q['files'].items(): |
| 77 | text = text.replace(k, '/absfile?name={}'.format(q['files'][k])) | 79 | text = text.replace(k, '/absfile?name={}'.format(q['files'][k])) |