Commit bab5f36055ea9544520899e366629f54cdbe3f41

Authored by Miguel Barão
1 parent bfedb0e2
Exists in master and in 1 other branch dev

- jobe_server on the test.yaml applies as default JOBE server for all questions,…

… but can be overriden.
- comments are parsed by markdown when reviewing tests.
1 1
2 # BUGS 2 # BUGS
3 3
  4 +- em caso de timeout na submissão (e.g. JOBE ou script nao responde) a correcção não termina e o teste não é guardado.
  5 +- QuestionCode falta reportar nos comments os vários erros que podem ocorrer (timeout, etc)
4 - permitir remover alunos que estão online para poderem comecar de novo. 6 - permitir remover alunos que estão online para poderem comecar de novo.
5 -- grade gives internal server error 7 +- grade gives internal server error??
6 - reload do teste recomeça a contagem no inicio do tempo. 8 - reload do teste recomeça a contagem no inicio do tempo.
7 - em admin, quando scale_max não é 20, as cores das barras continuam a reflectir a escala 0,20. a tabela teste na DB não tem a escala desse teste. 9 - em admin, quando scale_max não é 20, as cores das barras continuam a reflectir a escala 0,20. a tabela teste na DB não tem a escala desse teste.
8 - em grade.html as barras estao normalizadas para os limites scale_min e max do teste actual e nao dos testes realizados no passado (tabela test devia guardar a escala). 10 - em grade.html as barras estao normalizadas para os limites scale_min e max do teste actual e nao dos testes realizados no passado (tabela test devia guardar a escala).
demo/demo.yaml
@@ -14,6 +14,9 @@ database: students.db @@ -14,6 +14,9 @@ database: students.db
14 # Directory where the submitted and corrected test are stored for later review. 14 # Directory where the submitted and corrected test are stored for later review.
15 answers_dir: ans 15 answers_dir: ans
16 16
  17 +# Server used to compile & execute code
  18 +jobe_server: 192.168.1.85
  19 +
17 # --- optional settings: ----------------------------------------------------- 20 # --- optional settings: -----------------------------------------------------
18 21
19 # Title of this test, e.g. course name, year or test number 22 # Title of this test, e.g. course name, year or test number
@@ -37,6 +40,7 @@ show_points: true @@ -37,6 +40,7 @@ show_points: true
37 # (default: no scaling, just use question points) 40 # (default: no scaling, just use question points)
38 scale: [0, 5] 41 scale: [0, 5]
39 42
  43 +
40 # ---------------------------------------------------------------------------- 44 # ----------------------------------------------------------------------------
41 # Base path applied to the questions files and all the scripts 45 # Base path applied to the questions files and all the scripts
42 # including question generators and correctors. 46 # including question generators and correctors.
demo/questions/questions-tutorial.yaml
@@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
26 show_points: true # mostra cotação das perguntas (default: true) 26 show_points: true # mostra cotação das perguntas (default: true)
27 scale: [0, 20] # limites inferior e superior da escala (default: [0,20]) 27 scale: [0, 20] # limites inferior e superior da escala (default: [0,20])
28 scale_points: true # normaliza cotações para a escala definida 28 scale_points: true # normaliza cotações para a escala definida
  29 + jobe_server: moodle-jobe.uevora.pt # server used to compile & execute code
29 debug: false # mostra informação de debug no browser 30 debug: false # mostra informação de debug no browser
30 31
31 # -------------------------------------------------------------------------- 32 # --------------------------------------------------------------------------
@@ -626,7 +627,6 @@ @@ -626,7 +627,6 @@
626 Escreva um programa em C que recebe uma string no standard input e 627 Escreva um programa em C que recebe uma string no standard input e
627 mostra a mensagem `hello ` seguida da string. 628 mostra a mensagem `hello ` seguida da string.
628 Por exemplo, se o input for `Maria`, o output deverá ser `hello Maria`. 629 Por exemplo, se o input for `Maria`, o output deverá ser `hello Maria`.
629 - server: 127.0.0.1 # replace by appropriate address  
630 language: c 630 language: c
631 correct: 631 correct:
632 - stdin: 'Maria' 632 - stdin: 'Maria'
@@ -641,6 +641,10 @@ @@ -641,6 +641,10 @@
641 Se um caso incluir `stdin`, este será enviado para o programa e o `stdout` 641 Se um caso incluir `stdin`, este será enviado para o programa e o `stdout`
642 obtido será comparado com o declarado. A pergunta é considerada correcta se 642 obtido será comparado com o declarado. A pergunta é considerada correcta se
643 todos os outputs coincidirem. 643 todos os outputs coincidirem.
  644 +
  645 + Por defeito é o usado o servidor JOBE declarado no teste. Para usar outro
  646 + diferente nesta pergunta usa-se a opção `server: 127.0.0.1` com o endereço
  647 + apropriado.
644 answer: | 648 answer: |
645 #include <stdio.h> 649 #include <stdio.h>
646 int main() { 650 int main() {
@@ -648,7 +652,7 @@ @@ -648,7 +652,7 @@
648 scanf("%s", name); 652 scanf("%s", name);
649 printf("hello %s", name); 653 printf("hello %s", name);
650 } 654 }
651 - server: 192.168.1.141 655 + # server: 192.168.1.85
652 language: c 656 language: c
653 correct: 657 correct:
654 - stdin: 'Maria' 658 - stdin: 'Maria'
perguntations/questions.py
@@ -591,11 +591,8 @@ class QuestionTextArea(Question): @@ -591,11 +591,8 @@ class QuestionTextArea(Question):
591 591
592 # ============================================================================ 592 # ============================================================================
593 class QuestionCode(Question): 593 class QuestionCode(Question):
594 - '''An instance of QuestionCode will always have the keys:  
595 - type (str)  
596 - text (str)  
597 - correct (str with script to run)  
598 - answer (None or an actual answer) 594 + '''
  595 + Submits answer to a JOBE server to compile and run against the test cases.
599 ''' 596 '''
600 597
601 _outcomes = { 598 _outcomes = {
@@ -669,13 +666,15 @@ class QuestionCode(Question): @@ -669,13 +666,15 @@ class QuestionCode(Question):
669 return 666 return
670 logger.debug(self._outcomes[outcome]) 667 logger.debug(self._outcomes[outcome])
671 668
  669 +
  670 +
672 if result['cmpinfo']: # compiler errors and warnings 671 if result['cmpinfo']: # compiler errors and warnings
673 self['comments'] = f'Erros de compilação:\n{result["cmpinfo"]}' 672 self['comments'] = f'Erros de compilação:\n{result["cmpinfo"]}'
674 self['grade'] = 0.0 673 self['grade'] = 0.0
675 return 674 return
676 675
677 - if result['stdout'] != expected['stdout']:  
678 - self['comments'] = 'O output gerado é diferente do esperado.' 676 + if result['stdout'] != expected.get('stdout', ''):
  677 + self['comments'] = 'O output gerado é diferente do esperado.' # FIXME mostrar porque?
679 self['grade'] = 0.0 678 self['grade'] = 0.0
680 return 679 return
681 680
perguntations/templates/review-question.html
@@ -39,14 +39,14 @@ @@ -39,14 +39,14 @@
39 {{ round(q['grade'] * q['points'], 2) }} 39 {{ round(q['grade'] * q['points'], 2) }}
40 pontos 40 pontos
41 </p> 41 </p>
42 - <p class="text-success">{{ q['comments'] }}</p> 42 + <p class="text-success">{{ md(q['comments']) }}</p>
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="fas fa-exclamation-triangle fa-3x" aria-hidden="true"></i> 45 <i class="fas fa-exclamation-triangle fa-3x" aria-hidden="true"></i>
46 {{ round(q['grade'] * q['points'], 2) }} 46 {{ round(q['grade'] * q['points'], 2) }}
47 pontos 47 pontos
48 </p> 48 </p>
49 - <p class="text-warning">{{ q['comments'] }}</p> 49 + <p class="text-warning">{{ md(q['comments']) }}</p>
50 {% if q['solution'] %} 50 {% if q['solution'] %}
51 <hr> 51 <hr>
52 {{ md('**Solução:** \n\n' + q['solution']) }} 52 {{ md('**Solução:** \n\n' + q['solution']) }}
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 {{ round(q['grade'] * q['points'], 2) }} 57 {{ round(q['grade'] * q['points'], 2) }}
58 pontos 58 pontos
59 </p> 59 </p>
60 - <p class="text-danger"><pre>{{ q['comments'] }}</pre></p> 60 + <p class="text-danger">{{ md(q['comments']) }}</p>
61 {% if q['solution'] %} 61 {% if q['solution'] %}
62 <hr> 62 <hr>
63 {{ md('**Solução:** \n\n' + q['solution']) }} 63 {{ md('**Solução:** \n\n' + q['solution']) }}
@@ -101,7 +101,7 @@ @@ -101,7 +101,7 @@
101 <p class="text-secondary"> 101 <p class="text-secondary">
102 <i class="fas fa-ban fa-3x" aria-hidden="true"></i> 102 <i class="fas fa-ban fa-3x" aria-hidden="true"></i>
103 {{ round(q['grade'] * q['points'], 2) }} pontos<br> 103 {{ round(q['grade'] * q['points'], 2) }} pontos<br>
104 - {{ q['comments'] }} 104 + {{ md(q['comments']) }}
105 {% if q['solution'] %} 105 {% if q['solution'] %}
106 <hr> 106 <hr>
107 {{ md('**Solução:** \n\n' + q['solution']) }} 107 {{ md('**Solução:** \n\n' + q['solution']) }}
perguntations/testfactory.py
@@ -98,16 +98,23 @@ class TestFactory(dict): @@ -98,16 +98,23 @@ class TestFactory(dict):
98 98
99 # make factory only for the questions used in the test 99 # make factory only for the questions used in the test
100 if question['ref'] in qrefs: 100 if question['ref'] in qrefs:
101 - question.setdefault('type', 'information') 101 + # question.setdefault('type', 'information')
102 question.update({ 102 question.update({
103 'filename': filename, 103 'filename': filename,
104 'path': dirname, 104 'path': dirname,
105 'index': i # position in the file, 0 based 105 'index': i # position in the file, 0 based
106 }) 106 })
  107 + if question['type'] == 'code' and 'server' not in question:
  108 + try:
  109 + question['server'] = self['jobe_server']
  110 + except KeyError as exc:
  111 + msg = f'Missing JOBE server in "{question["ref"]}"'
  112 + raise TestFactoryException(msg)
107 113
108 self.question_factory[question['ref']] = QFactory(question) 114 self.question_factory[question['ref']] = QFactory(question)
109 115
110 # check if all the questions can be correctly generated 116 # check if all the questions can be correctly generated
  117 + # TODO and corrected
111 try: 118 try:
112 self.question_factory[question['ref']].generate() 119 self.question_factory[question['ref']].generate()
113 except Exception as exc: 120 except Exception as exc: