Commit 50a03cac86d6cf3d861a293fd909be2d3714acb4
1 parent
71adb971
Exists in
master
and in
1 other branch
- /admin review includes student identification.
- Fixed bug where textarea was being initialized with answer from previous submission from another student. - Fixed get CSV with grades. - Fixed many regressions from the upgrade to bootstrap 5.1. - Changed menu in /admin. - Removed dependency from fontawesome (but added bootstrap-icons). - Removed jquery where not needed.
Showing
12 changed files
with
143 additions
and
180 deletions
Show diff stats
BUGS.md
... | ... | @@ -2,21 +2,22 @@ |
2 | 2 | |
3 | 3 | ## BUGS |
4 | 4 | |
5 | -- review por nome e numero no cabecalho jumbotron | |
6 | -- talvez a base de dados devesse ter como chave do teste um id que fosse único | |
7 | - desse teste particular (não um auto counter, nem ref do teste) | |
8 | 5 | - em caso de timeout na submissão (e.g. JOBE ou script nao responde) a correcção |
9 | 6 | não termina e o teste não é guardado. |
10 | -- reload do teste recomeça a contagem no inicio do tempo. | |
7 | +- modo --review nao implementado em testfactory.py | |
8 | +- talvez a base de dados devesse ter como chave do teste um id que fosse único | |
9 | + desse teste particular (não um auto counter, nem ref do teste) | |
11 | 10 | - em admin, quando scale_max não é 20, as cores das barras continuam a reflectir |
12 | 11 | a escala 0,20. a tabela teste na DB não tem a escala desse teste. |
12 | +- a revisao do teste não mostra as imagens que nao estejam ja em cache. | |
13 | +- reload do teste recomeça a contagem no inicio do tempo. | |
13 | 14 | - mensagems de erro do assembler aparecem na mesma linha na correcao e nao |
14 | - fazerm rendering do `$t`, ver se servidor faz parse do markdown dessas | |
15 | + fazem rendering do `$t`, ver se servidor faz parse do markdown dessas | |
15 | 16 | mensagens. |
16 | -- a revisao do teste não mostra as imagens que nao estejam ja em cache. | |
17 | 17 | |
18 | 18 | ## TODO |
19 | 19 | |
20 | +- pagina de login semelhante ao aprendizations | |
20 | 21 | - QuestionTextArea falta reportar nos comments os vários erros que podem ocorrer |
21 | 22 | (timeout, etc) |
22 | 23 | - pergunta com varias partes. |
... | ... | @@ -62,6 +63,12 @@ |
62 | 63 | - se ocorrer um erro na correcçao avisar aluno para contactar o professor. |
63 | 64 | - abrir o teste numa janela maximizada e que nao permite que o aluno a |
64 | 65 | redimensione/mova? modo kiosk? |
65 | -- detectar scroll e enviar posição para servidor (analise de scroll para detectar copianço?) | |
66 | +- detectar scroll e enviar posição para servidor (analise de scroll para | |
67 | + detectar copianço?) | |
66 | 68 | - criar perguntas de outros tipos, e.g. associação, ordenação. |
67 | -- stress tests. use https://locust.io | |
69 | +- stress tests. use [locust](https://locust.io) | |
70 | + | |
71 | +## FIXED | |
72 | + | |
73 | +- textarea vem inicializado com a resposta de outros alunos!!! | |
74 | +- App has no attribute get_grades_csv | ... | ... |
package.json
perguntations/app.py
... | ... | @@ -372,7 +372,7 @@ class App(): |
372 | 372 | } |
373 | 373 | |
374 | 374 | # ------------------------------------------------------------------------ |
375 | - def get_test_csv(self): | |
375 | + def get_grades_csv(self): | |
376 | 376 | '''generates a CSV with the grades of the test currently running''' |
377 | 377 | test_ref = self._testfactory['ref'] |
378 | 378 | with Session(self._engine, future=True) as session: | ... | ... |
perguntations/questions.py
... | ... | @@ -604,16 +604,14 @@ def question_from(qdict: QDict) -> Question: |
604 | 604 | try: |
605 | 605 | qclass = types[qdict['type']] |
606 | 606 | except KeyError: |
607 | - logger.error('Invalid type "%s" in "%s"', | |
608 | - qdict['type'], qdict['ref']) | |
607 | + logger.error('Invalid type "%s" in "%s"', qdict['type'], qdict['ref']) | |
609 | 608 | raise |
610 | 609 | |
611 | 610 | # Create an instance of Question() of appropriate type |
612 | 611 | try: |
613 | - qinstance = qclass(QDict(qdict)) | |
612 | + qinstance = qclass(qdict.copy()) | |
614 | 613 | except QuestionException: |
615 | - logger.error('Error generating "%s" in %s/%s', | |
616 | - qdict['ref'], qdict['path'], qdict['filename']) | |
614 | + logger.error('Generating "%s" in %s', qdict['ref'], qdict['filename']) | |
617 | 615 | raise |
618 | 616 | |
619 | 617 | return qinstance |
... | ... | @@ -625,11 +623,10 @@ class QFactory(): |
625 | 623 | QFactory is a class that can generate question instances, e.g. by shuffling |
626 | 624 | options, running a script to generate the question, etc. |
627 | 625 | |
628 | - To generate an instance of a question we use the method generate(). | |
626 | + To generate an instance of a question we use the method gen_async(). | |
629 | 627 | It returns a question instance of the correct class. |
630 | - There is also an asynchronous version called gen_async(). This version is | |
631 | - synchronous for all question types (radio, checkbox, etc) except for | |
632 | - generator types which run asynchronously. | |
628 | + The method is async but it only awaits on generator questions. The others | |
629 | + are run until completion. | |
633 | 630 | |
634 | 631 | Example: |
635 | 632 | |
... | ... | @@ -640,16 +637,13 @@ class QFactory(): |
640 | 637 | 'options': ['a', 'b'] |
641 | 638 | }) |
642 | 639 | |
643 | - # generate synchronously | |
644 | - question = qfactory.generate() | |
645 | - | |
646 | 640 | # generate asynchronously |
647 | 641 | question = await qfactory.gen_async() |
648 | 642 | |
649 | 643 | # answer one question and correct it |
650 | - question['answer'] = 42 # set answer | |
651 | - question.correct() # correct answer | |
652 | - grade = question['grade'] # get grade | |
644 | + question.set_answer(42) # set answer | |
645 | + question.correct() # correct answer | |
646 | + grade = question['grade'] # get grade | |
653 | 647 | ''' |
654 | 648 | |
655 | 649 | def __init__(self, qdict: QDict = QDict({})) -> None: | ... | ... |
perguntations/templates/admin.html
... | ... | @@ -21,8 +21,6 @@ |
21 | 21 | |
22 | 22 | <!-- Scripts --> |
23 | 23 | <script src="/static/jquery/jquery.min.js"></script> |
24 | - <!-- <script defer src="/static/popper.js/popper.min.js"></script> --> | |
25 | - <!-- <script defer src="/static/fontawesome-free/js/all.min.js"></script> --> | |
26 | 24 | <script defer src="/static/bootstrap/js/bootstrap.bundle.min.js"></script> |
27 | 25 | <script defer src="/static/datatables/js/jquery.dataTables.min.js"></script> |
28 | 26 | <script defer src="/static/underscore/underscore-min.js"></script> |
... | ... | @@ -40,27 +38,34 @@ |
40 | 38 | <span class="navbar-toggler-icon"></span> |
41 | 39 | </button> |
42 | 40 | <div class="collapse navbar-collapse" id="navbarNavDropdown"> |
43 | - <!-- left --> | |
44 | - <span class="navbar-text mr-auto"></span> | |
45 | - | |
46 | 41 | <!-- center --> |
47 | - <span class="navbar-text mr-auto"><span id="clock"> --:-- </span></span> | |
42 | + <span class="navbar-text mx-auto" id="clock"> --:-- </span> | |
48 | 43 | |
49 | 44 | <!-- right --> |
50 | 45 | <ul class="navbar-nav"> |
51 | 46 | <li class="nav-item dropdown"> |
47 | + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownNotas" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | |
48 | + Notas | |
49 | + </a> | |
50 | + <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownNotas"> | |
51 | + <li><a class="dropdown-item" href="/adminwebservice?cmd=testcsv">Obter CSV notas finais...</a></li> | |
52 | + <li><a class="dropdown-item" href="/adminwebservice?cmd=questionscsv">Obter CSV detalhado...</a></li> | |
53 | + </ul> | |
54 | + </li> | |
55 | + <li class="nav-item dropdown"> | |
52 | 56 | <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownAluno" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
53 | - Acções | |
57 | + Alunos | |
54 | 58 | </a> |
55 | - <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownAluno"> | |
59 | + <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownAluno"> | |
56 | 60 | <li><a class="dropdown-item" href="#" id="novo_aluno" data-bs-toggle="modal" data-bs-target="#novo_aluno_modal">Novo aluno...</a></li> |
57 | 61 | <li><a class="dropdown-item" href="#" id="reset_password_menu" data-bs-toggle="modal" data-bs-target="#reset_password_modal">Limpar password...</a></li> |
58 | 62 | <li><a class="dropdown-item" href="#" id="allow_all">Autorizar todos</a></li> |
59 | 63 | <li><a class="dropdown-item" href="#" id="deny_all">Desautorizar todos</a></li> |
60 | - <li><hr class="dropdown-divider"></hr></li> | |
61 | - <li><a class="dropdown-item" href="/logout">Sair</a></li> | |
62 | 64 | </ul> |
63 | 65 | </li> |
66 | + <li class="nav-item"> | |
67 | + <a class="nav-link" href="/logout">Sair</a> | |
68 | + </li> | |
64 | 69 | </ul> |
65 | 70 | </div> |
66 | 71 | </div> |
... | ... | @@ -71,28 +76,25 @@ |
71 | 76 | <div class="bg-light p-3"> |
72 | 77 | <h3 id="title"></h3> |
73 | 78 | <ul> |
74 | - <li>Referência: <code id="ref">--</code><br></li> | |
75 | - <li>Ficheiro de configuração: <code id="filename">--</code><br></li> | |
76 | - <li>Directório com os testes entregues: <code id="answers_dir">--</code><br></li> | |
77 | - <li>Base de dados: <code id="database">--</code><br></li> | |
79 | + <li>Referência: <code id="ref">--</code></li> | |
80 | + <li>Ficheiro de configuração: <code id="filename">--</code></li> | |
81 | + <li>Directório com os testes entregues: <code id="answers_dir">--</code></li> | |
82 | + <li>Base de dados: <code id="database">--</code></li> | |
78 | 83 | </ul> |
79 | - <p> | |
80 | - <a href="/adminwebservice?cmd=testcsv" class="btn btn-primary">Obter CSV das notas</a> | |
81 | - <a href="/adminwebservice?cmd=questionscsv" class="btn btn-primary">Obter CSV detalhado</a> | |
82 | - </p> | |
83 | 84 | </div> |
84 | 85 | <br> |
85 | - <table class="table table-sm table-striped" style="width:100%" id="students_table"> | |
86 | - <thead class="thead thead-light"> | |
86 | + <table class="table table-borderless table-striped" id="students_table"> | |
87 | + <thead> | |
87 | 88 | <tr> |
88 | - <th>#</th> | |
89 | - <th>Autoriz.</th> | |
90 | - <th>Número</th> | |
91 | - <th>Nome</th> | |
92 | - <th>Estado</th> | |
93 | - <th>Nota</th> | |
89 | + <th scope="col">#</th> | |
90 | + <th scope="col">Autoriz.</th> | |
91 | + <th scope="col">Número</th> | |
92 | + <th scope="col">Nome</th> | |
93 | + <th scope="col">Estado</th> | |
94 | + <th scope="col">Nota</th> | |
94 | 95 | </tr> |
95 | 96 | </thead> |
97 | + <tbody> </tbody> | |
96 | 98 | </table> |
97 | 99 | |
98 | 100 | </div> <!-- container --> | ... | ... |
perguntations/templates/grade.html
1 | -<!DOCTYPE html> | |
1 | +<!doctype html> | |
2 | 2 | <html lang="pt-PT"> |
3 | 3 | <head> |
4 | - <title>Teste</title> | |
4 | + <title>Prova de avaliação</title> | |
5 | 5 | <meta charset="utf-8"> |
6 | 6 | <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
7 | 7 | <link rel="icon" href="/static/favicon.ico"> |
... | ... | @@ -35,9 +35,7 @@ |
35 | 35 | <div class="container"> |
36 | 36 | <div class="bg-light p-3"> |
37 | 37 | {% if t['state'] == 'CORRECTED' %} |
38 | - <h1> | |
39 | - Resultado: <strong>{{ f'{round(t["grade"], 3)}' }}</strong> valores | |
40 | - </h1> | |
38 | + <h1>Resultado: <strong>{{ f'{round(t["grade"], 3)}' }}</strong> valores</h1> | |
41 | 39 | {% elif t['state'] == 'SUBMITTED' %} |
42 | 40 | <h3>A prova foi submetida com sucesso. Vai ser corrigida mais tarde.</h3> |
43 | 41 | {% elif t['state'] == 'QUIT' %} | ... | ... |
perguntations/templates/review-question-checkbox.html
... | ... | @@ -10,45 +10,35 @@ |
10 | 10 | {% if q['answer'] is not None and str(n) in q['answer'] %} |
11 | 11 | <div class="p-2"> |
12 | 12 | <i class="bi bi-check-square"></i> |
13 | - <!-- <i class="far fa-check-square" aria-hidden="true"></i> --> | |
14 | 13 | </div> |
15 | 14 | <div class="p-2"> |
16 | 15 | {{ md(opt) }} |
17 | 16 | </div> |
18 | - <div class="ml-auto p-2"> | |
19 | - {% if q['correct'][n] > 0 %} | |
20 | - <div class="text-right text-success"> | |
21 | - <i class="bi bi-check-lg"></i> | |
22 | - <!-- <i class="fas fa-check" aria-hidden="true"></i> --> | |
23 | - </div> | |
24 | - {% else %} | |
25 | - <div class="text-right text-danger"> | |
26 | - <i class="bi bi-x-lg"></i> | |
27 | - <!-- <i class="fas fa-times" aria-hidden="true"></i> --> | |
28 | - </div> | |
29 | - {% end %} | |
17 | + <div class="ms-auto p-2"> | |
18 | + <div class="text-end"> | |
19 | + {% if q['correct'][n] > 0 %} | |
20 | + <i class="bi bi-check-lg text-success"></i> | |
21 | + {% else %} | |
22 | + <i class="bi bi-x-lg text-danger"></i> | |
23 | + {% end %} | |
24 | + </div> | |
30 | 25 | </div> |
31 | 26 | |
32 | 27 | {% else %} |
33 | 28 | <div class="p-2"> |
34 | 29 | <i class="bi bi-square"></i> |
35 | - <!-- <i class="far fa-square" aria-hidden="true"></i> --> | |
36 | 30 | </div> |
37 | 31 | <div class="p-2"> |
38 | 32 | {{ md(opt) }} |
39 | 33 | </div> |
40 | - <div class="ml-auto p-2"> | |
41 | - {% if q['correct'][n] > 0 %} | |
42 | - <div class="text-right text-danger"> | |
43 | - <i class="bi bi-x-lg"></i> | |
44 | - <!-- <i class="fas fa-times" aria-hidden="true"></i> --> | |
45 | - </div> | |
46 | - {% else %} | |
47 | - <div class="text-right text-success"> | |
48 | - <i class="bi bi-check-lg"></i> | |
49 | - <!-- <i class="fas fa-check" aria-hidden="true"></i> --> | |
50 | - </div> | |
51 | - {% end %} | |
34 | + <div class="ms-auto p-2"> | |
35 | + <div class="text-end"> | |
36 | + {% if q['correct'][n] > 0 %} | |
37 | + <i class="bi bi-x-lg text-danger"></i> | |
38 | + {% else %} | |
39 | + <i class="bi bi-check-lg text-success"></i> | |
40 | + {% end %} | |
41 | + </div> | |
52 | 42 | </div> |
53 | 43 | {% end %} |
54 | 44 | </div> | ... | ... |
perguntations/templates/review-question-radio.html
... | ... | @@ -10,39 +10,28 @@ |
10 | 10 | {% if q['answer'] is not None and str(n)==q['answer'] %} |
11 | 11 | <div class="p-2"> |
12 | 12 | <i class="bi bi-record-circle"></i> |
13 | - <!-- <i class="fas fa-dot-circle" aria-hidden="true"></i> --> | |
14 | 13 | </div> |
15 | 14 | <div class="p-2"> |
16 | 15 | {{ md(opt) }} |
17 | 16 | </div> |
18 | - <div class="ml-auto p-2"> | |
17 | + <div class="ms-auto p-2"> | |
19 | 18 | {% if q['correct'][n] > 0 %} |
20 | - <div class="text-right text-success"> | |
21 | - <i class="bi bi-check-lg"></i> | |
22 | - <!-- <i class="fas fa-check" aria-hidden="true"></i> --> | |
23 | - </div> | |
19 | + <i class="bi bi-check-lg text-success"></i> | |
24 | 20 | {% else %} |
25 | - <div class="text-right text-danger"> | |
26 | - <i class="bi bi-x-lg"></i> | |
27 | - <!-- <i class="fas fa-times" aria-hidden="true"></i> --> | |
28 | - </div> | |
21 | + <i class="bi bi-x-lg text-danger"></i> | |
29 | 22 | {% end %} |
30 | 23 | </div> |
31 | 24 | |
32 | 25 | {% else %} |
33 | 26 | <div class="p-2"> |
34 | 27 | <i class="bi bi-circle"></i> |
35 | - <!-- <i class="far fa-circle" aria-hidden="true"></i> --> | |
36 | 28 | </div> |
37 | 29 | <div class="p-2"> |
38 | 30 | {{ md(opt) }} |
39 | 31 | </div> |
40 | - <div class="ml-auto p-2"> | |
32 | + <div class="ms-auto p-2"> | |
41 | 33 | {% if q['correct'][n] > 0 %} |
42 | - <div class="text-right text-info"> | |
43 | - <i class="bi bi-arrow-left"></i> | |
44 | - <!-- <i class="fas fa-dot-circle" aria-hidden="true"></i> --> | |
45 | - </div> | |
34 | + <i class="bi bi-arrow-left text-info"></i> | |
46 | 35 | {% end %} |
47 | 36 | </div> |
48 | 37 | {% end %} | ... | ... |
perguntations/templates/review-question.html
... | ... | @@ -6,10 +6,9 @@ |
6 | 6 | <div class="card border-dark mb-3"> |
7 | 7 | <h5 class="card-header text-white bg-dark"> |
8 | 8 | {{ q['number'] }}. {{ q['title'] }} |
9 | - <div class="float-right"> | |
10 | - <small>Classificar </small> | |
11 | - <i class="bi bi-check-square"></i> | |
12 | - <!-- <i class="far fa-check-square" aria-hidden="true"></i> --> | |
9 | + <div class="float-end"> | |
10 | + <small>Classificada </small> | |
11 | + <i class="bi bi-toggle2-on"></i> | |
13 | 12 | </div> |
14 | 13 | </h5> <!-- card-header --> |
15 | 14 | |
... | ... | @@ -38,14 +37,12 @@ |
38 | 37 | {% if q['grade'] > 0.999 %} |
39 | 38 | <h1 class="text-success"><i class="bi bi-hand-thumbs-up"></i></h1> |
40 | 39 | <p class="text-success"> |
41 | - <!-- <i class="far fa-thumbs-up fa-3x" aria-hidden="true"></i> --> | |
42 | 40 | {{ round(q['grade'] * q['points'], 2) }} pontos |
43 | 41 | </p> |
44 | 42 | <p class="text-success">{{ md(q['comments']) }}</p> |
45 | 43 | {% elif q['grade'] >= 0.5 %} |
46 | 44 | <h1 class="text-warning"><i class="bi bi-exclamation-triangle"></i></h1> |
47 | 45 | <p class="text-warning"> |
48 | - <!-- <i class="fas fa-exclamation-triangle fa-3x" aria-hidden="true"></i> --> | |
49 | 46 | {{ round(q['grade'] * q['points'], 2) }} pontos |
50 | 47 | </p> |
51 | 48 | <p class="text-warning">{{ md(q['comments']) }}</p> |
... | ... | @@ -56,7 +53,6 @@ |
56 | 53 | {% else %} |
57 | 54 | <h1 class="text-danger"><i class="bi bi-hand-thumbs-down"></i></h1> |
58 | 55 | <p class="text-danger"> |
59 | - <!-- <i class="far fa-thumbs-down fa-3x" aria-hidden="true"></i> --> | |
60 | 56 | {{ round(q['grade'] * q['points'], 2) }} pontos |
61 | 57 | </p> |
62 | 58 | <p class="text-danger">{{ md(q['comments']) }}</p> |
... | ... | @@ -80,10 +76,9 @@ |
80 | 76 | <div class="card border-secondary mb-3"> |
81 | 77 | <h5 class="card-header text-white bg-secondary"> |
82 | 78 | {{ q['number'] }}. {{ q['title'] }} |
83 | - <div class="float-right"> | |
84 | - <small>Classificar </small> | |
85 | - <i class="bi bi-square"></i> | |
86 | - <!-- <i class="far fa-square" aria-hidden="true"></i> --> | |
79 | + <div class="float-end"> | |
80 | + <small>Não classificada</small> | |
81 | + <i class="bi bi-toggle2-off"></i> | |
87 | 82 | </div> |
88 | 83 | </h5> <!-- card-header --> |
89 | 84 | |
... | ... | @@ -102,16 +97,10 @@ |
102 | 97 | </div> <!-- card-body --> |
103 | 98 | |
104 | 99 | <div class="card-footer"> |
105 | - <h1 class="text-secondary"><i class="bi bi-dash-circle"></i></h1> | |
106 | - <p class="text-secondary"> | |
107 | - Não respondeu | |
108 | - <!-- <i class="fas fa-ban fa-3x" aria-hidden="true"></i> --> | |
109 | - {{ md(q['comments']) }} | |
110 | - {% if q['solution'] %} | |
111 | - <hr> | |
112 | - {{ md('**Solução:** \n\n' + q['solution']) }} | |
113 | - {% end %} | |
114 | - </p> | |
100 | + {{ md(q['comments']) }} | |
101 | + {% if q['solution'] %} | |
102 | + {{ md('**Solução:** \n\n' + q['solution']) }} | |
103 | + {% end %} | |
115 | 104 | |
116 | 105 | {% if debug %} |
117 | 106 | <hr> | ... | ... |
perguntations/templates/review.html
... | ... | @@ -22,14 +22,12 @@ |
22 | 22 | |
23 | 23 | <!-- Styles --> |
24 | 24 | <link rel="stylesheet" type="text/css" href="/static/bootstrap/css/bootstrap.min.css"> |
25 | + <link rel="stylesheet" type="text/css" href="/static/bootstrap-icons/font/bootstrap-icons.css"> | |
25 | 26 | <link rel="stylesheet" type="text/css" href="/static/css/github.css"> <!-- syntax highlight --> |
26 | 27 | <link rel="stylesheet" type="text/css" href="/static/css/test.css"> |
27 | - <link rel="stylesheet" type="text/css" href="/static/bootstrap-icons/font/bootstrap-icons.css"> | |
28 | 28 | |
29 | 29 | <!-- Scripts --> |
30 | 30 | <script src="/static/jquery/jquery.min.js"></script> |
31 | - <!-- <script defer src="/static/popper.js/popper.min.js"></script> --> | |
32 | - <!-- <script defer src="/static/fontawesome-free/js/all.min.js"></script> --> | |
33 | 31 | <script defer src="/static/bootstrap/js/bootstrap.bundle.min.js"></script> |
34 | 32 | </head> |
35 | 33 | <!-- ===================================================================== --> |
... | ... | @@ -58,10 +56,8 @@ |
58 | 56 | <ul class="nav navbar-nav"> |
59 | 57 | <li class="nav-item"> |
60 | 58 | <span class="navbar-text"> |
61 | - <!-- <i class="fas fa-user" aria-hidden="true"></i> --> | |
62 | 59 | <span id="name">{{ escape(name) }}</span> |
63 | 60 | (<span id="number">{{ escape(uid) }}</span>) |
64 | - <!-- <span class="caret"></span> --> | |
65 | 61 | </span> |
66 | 62 | </li> |
67 | 63 | </ul> |
... | ... | @@ -73,46 +69,47 @@ |
73 | 69 | <div class="container"> |
74 | 70 | <div class="bg-light p-3"> |
75 | 71 | <h1 class="display-5">{{ t['title'] }}</h1> |
76 | - <h5> | |
77 | - <div class="row"> | |
78 | - <label for="duracao" class="col-sm-2">Duração:</label> | |
79 | - <div class="col-sm-10" id="duracao"> | |
80 | - {{ str(t.get('duration', 0)) + ' minutos' if t.get('duration', 0) > 0 else '+'+chr(8734) }} | |
81 | - </div> | |
82 | - </div> | |
83 | - </h5> | |
84 | 72 | <hr> |
85 | - | |
86 | - <h5> | |
87 | - <div class="row"> | |
88 | - <label for="inicio" class="col-sm-2">Início:</label> | |
89 | - <div class="col-sm-10" id="inicio">{{t['start_time'][:19]}}</div> | |
73 | + <div class="row"> | |
74 | + <label for="nome" class="col-sm-2">Nome:</label> | |
75 | + <div class="col-sm-9" id="nome">{{ escape(name) }}</div> | |
76 | + </div> | |
77 | + <div class="row"> | |
78 | + <label for="numero" class="col-sm-2">Número:</label> | |
79 | + <div class="col-sm-9" id="numero">{{ escape(uid) }}</div> | |
80 | + </div> | |
81 | + <div class="row"> | |
82 | + <label for="duracao" class="col-sm-2">Duração:</label> | |
83 | + <div class="col-sm-10" id="duracao"> | |
84 | + {{ str(t.get('duration', 0)) + ' minutos' if t.get('duration', 0) > 0 else '+'+chr(8734) }} | |
90 | 85 | </div> |
91 | - <div class="row"> | |
92 | - <label for="fim" class="col-sm-2">Fim:</label> | |
93 | - <div class="col-sm-10" id="fim">{{t['finish_time'][:19]}}</div> | |
86 | + </div> | |
87 | + <div class="row"> | |
88 | + <label for="inicio" class="col-sm-2">Início:</label> | |
89 | + <div class="col-sm-10" id="inicio">{{t['start_time'][:19]}}</div> | |
90 | + </div> | |
91 | + <div class="row"> | |
92 | + <label for="fim" class="col-sm-2">Fim:</label> | |
93 | + <div class="col-sm-10" id="fim">{{t['finish_time'][:19]}}</div> | |
94 | + </div> | |
95 | + <div class="row"> | |
96 | + <label for="nota" class="col-sm-2">Nota:</label> | |
97 | + <div class="col-sm-10" id="nota"> | |
98 | + {% if t['state'] == 'CORRECTED' %} | |
99 | + <strong>{{ round(t['grade'], 2) }}</strong> valores | |
100 | + {% elif t['state'] == 'SUBMITTED' %} | |
101 | + (não corrigido) | |
102 | + {% elif t['state'] == 'QUIT' %} | |
103 | + (DESISTIU) | |
104 | + {% end %} | |
94 | 105 | </div> |
95 | - </h5> | |
96 | - <h3> | |
106 | + </div> | |
107 | + {% if t['comment'] != '' %} | |
97 | 108 | <div class="row"> |
98 | - <label for="nota" class="col-sm-2">Nota:</label> | |
99 | - <div class="col-sm-10" id="nota"> | |
100 | - {% if t['state'] == 'CORRECTED' %} | |
101 | - <span class="badge badge-primary">{{ round(t['grade'], 2) }}</span> valores | |
102 | - {% elif t['state'] == 'SUBMITTED' %} | |
103 | - (não corrigido) | |
104 | - {% elif t['state'] == 'QUIT' %} | |
105 | - (DESISTIU) | |
106 | - {% end %} | |
107 | - </div> | |
109 | + <label for="comentario" class="col-sm-2">Comentário:</label> | |
110 | + <div class="col-sm-10" id="comentario">{{ t['comment'] }}</div> | |
108 | 111 | </div> |
109 | - {% if t['comment'] != '' %} | |
110 | - <div class="row"> | |
111 | - <label for="comentario" class="col-sm-2">Comentário:</label> | |
112 | - <div class="col-sm-10" id="comentario">{{ t['comment'] }}</div> | |
113 | - </div> | |
114 | - {% end %} | |
115 | - </h3> | |
112 | + {% end %} | |
116 | 113 | </div> |
117 | 114 | |
118 | 115 | {% for i, q in enumerate(t['questions']) %} | ... | ... |
perguntations/templates/test.html
1 | -<!DOCTYPE html> | |
1 | +<!doctype html> | |
2 | 2 | <html lang="pt-PT"> |
3 | 3 | <head> |
4 | - <title>Teste</title> | |
5 | - <meta charset="UTF-8"> | |
6 | - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
4 | + <title>Prova de avalliação</title> | |
5 | + <meta charset="utf-8"> | |
6 | + <meta name="viewport" content="width=device-width, initial-scale=1"> | |
7 | 7 | <link rel="icon" href="/static/favicon.ico"> |
8 | 8 | |
9 | +<!-- Styles --> | |
10 | + <link rel="stylesheet" type="text/css" href="/static/bootstrap/css/bootstrap.min.css"> | |
11 | + <link rel="stylesheet" type="text/css" href="/static/codemirror/lib/codemirror.css"> | |
12 | + <link rel="stylesheet" type="text/css" href="/static/codemirror/theme/darcula.css"> | |
13 | + <link rel="stylesheet" type="text/css" href="/static/css/github.css"> <!-- syntax highlight --> | |
14 | + <link rel="stylesheet" type="text/css" href="/static/css/test.css"> | |
15 | + | |
9 | 16 | <!-- MathJax3 --> |
10 | 17 | <script> |
11 | 18 | MathJax = { |
... | ... | @@ -21,21 +28,12 @@ |
21 | 28 | |
22 | 29 | <!-- Scripts --> |
23 | 30 | <script src="/static/jquery/jquery.min.js"></script> |
24 | - <!-- <script defer src="/static/popper.js/popper.min.js"></script> --> | |
25 | - <!-- <script defer src="/static/fontawesome-free/js/all.min.js"></script> --> | |
26 | 31 | <script defer src="/static/bootstrap/js/bootstrap.bundle.min.js"></script> |
27 | 32 | <script defer src="/static/underscore/underscore-min.js"></script> |
28 | 33 | <script src="/static/codemirror/lib/codemirror.js"></script> |
29 | 34 | <script src="/static/codemirror/addon/selection/active-line.js"></script> |
30 | 35 | <script src="/static/codemirror/addon/edit/matchbrackets.js"></script> |
31 | 36 | |
32 | -<!-- Styles --> | |
33 | - <link rel="stylesheet" type="text/css" href="/static/bootstrap/css/bootstrap.min.css"> | |
34 | - <link rel="stylesheet" type="text/css" href="/static/codemirror/lib/codemirror.css"> | |
35 | - <link rel="stylesheet" type="text/css" href="/static/codemirror/theme/darcula.css"> | |
36 | - <link rel="stylesheet" type="text/css" href="/static/css/github.css"> <!-- syntax highlight --> | |
37 | - <link rel="stylesheet" type="text/css" href="/static/css/test.css"> | |
38 | - | |
39 | 37 | <!-- My scripts --> |
40 | 38 | <script defer src="/static/js/question_disabler.js"></script> |
41 | 39 | <script defer src="/static/js/prevent_enter_submit.js"></script> |
... | ... | @@ -73,10 +71,8 @@ |
73 | 71 | <ul class="nav navbar-nav"> |
74 | 72 | <li class="nav-item"> |
75 | 73 | <span class="navbar-text"> |
76 | - <!-- <i class="fas fa-user" aria-hidden="true"></i> --> | |
77 | 74 | <span id="name">{{ escape(name) }}</span> |
78 | 75 | (<span id="number">{{ escape(uid) }}</span>) |
79 | - <!-- <span class="caret"></span> --> | |
80 | 76 | </span> |
81 | 77 | </li> |
82 | 78 | </ul> |
... | ... | @@ -136,17 +132,19 @@ |
136 | 132 | <h4 class="modal-title">Deseja submeter o teste?</h4> |
137 | 133 | </div> |
138 | 134 | <div class="modal-body"> |
139 | - O teste será enviado para classificação e já não poderá voltar atrás. | |
140 | - Antes de submeter, verifique se respondeu a todas as questões. | |
141 | - Desactive as perguntas que não pretende classificar para evitar | |
142 | - penalizações. | |
135 | + <ul> | |
136 | + <li> O teste será enviado para classificação e já não poderá voltar atrás.</li> | |
137 | + <li> Antes de submeter, verifique se respondeu a todas as questões.</li> | |
138 | + <li> Para evitar penalizações, esconda as perguntas que não | |
139 | + respondeu desactivando a checkbox "classificar".</li> | |
140 | + </ul> | |
143 | 141 | </div> |
144 | 142 | <div class="modal-footer"> |
145 | 143 | <button form="test" type="submit" class="btn btn-success btn-lg"> |
146 | 144 | Sim, submeter... |
147 | 145 | </button> |
148 | 146 | <button type="button" class="btn btn-danger btn-lg" data-bs-dismiss="modal"> |
149 | - Oops, NÃO!!! | |
147 | + Ainda NÃO!!! | |
150 | 148 | </button> |
151 | 149 | </div> |
152 | 150 | </div> |
... | ... | @@ -157,7 +155,6 @@ |
157 | 155 | $("textarea").each(function(i, ta) { |
158 | 156 | CodeMirror.fromTextArea(ta, { |
159 | 157 | lineNumbers: true, |
160 | - // theme: "darcula", | |
161 | 158 | viewportMargin: Infinity, |
162 | 159 | matchBrackets: true, |
163 | 160 | styleActiveLine: true, | ... | ... |
perguntations/testfactory.py