Browse Source

Update README with credits and build instruction

Implement empty output information with an svg and tooltip to tell the user when output is missing or exceeding the expected
Lucas Mendonça 4 years ago
parent
commit
4c0094e773

+ 21 - 1
README.md

@@ -1,5 +1,25 @@
 # iVProg
+
 Módulo interativo de aprendizagem para o ensino de lógica de programação desenvolvido pelo [LInE](https://usp.br/line)
 
 # Downloads
-As versão estáveis podem ser baixaas através do seguinte [link](http://200.144.254.107/release/ivprog). O projeto usa uma numeração de versão seguindo a formatação: YYYY-MM-DD_HH-MM-SS
+
+As versões estáveis podem ser baixadas através do seguinte [link](http://200.144.254.107/release/ivprog). O projeto usa uma numeração de versão seguindo a formatação: YYYY-MM-DD_HH-MM-SS.
+
+# Credits
+
+- "Empty" icon by amante de icono, from thenounproject.com
+
+# Desenvolvimento
+
+O projeto utiliza o npm como gerenciador de pacotes e ferramenta de construção. Para montar o programa a partir do código fonte é necessário ter instalado o Java (para o analisador léxico baseado em antlr4) e nodejs(^10.16.0) com npm(^6.9.0).
+Após clone este repositório execute os seguintes comandos a partir da pasta raiz:
+
+```
+npm install
+npm run build
+npm run start
+```
+
+Após a execução desses comandos, você poderá acessar o localhost na porta 8080 para acessar a sua versão local do iVProg.
+Existem também o comando _npm run watch_ para compilar os arquivos enquanto você faz modificações no código

+ 48 - 1
css/ivprog-assessment.css

@@ -18,7 +18,8 @@
 }
 body {
   font-family: 'TeXGyreHerosRegular';
-  background-color: #b9c7ca
+  background-color: #b9c7ca;
+  margin-left: 2rem;
 }
 .details-body > .details-header > h2 {margin-bottom: 0.5rem;}
 .details-body > .details-header > p {
@@ -69,3 +70,49 @@ p.assessment-failed-execution {padding-left: 1rem;}
 .assessment-input-unread {color: #d02929}
 .assessment-number-result-failed, .assessment-bool-result-failed {color: #d02929}
 .assessment-number-result, .assessment-bool-result, .assessment-string-result {color: #22a222}
+.assessment-popup {
+  position: relative;
+  display: inline-block;
+  cursor: pointer;
+}
+.assessment-popup .assessment-popuptext {
+  visibility: hidden;
+  width: 160px;
+  background-color: #555;
+  color: #fff;
+  text-align: center;
+  border-radius: 6px;
+  padding: 8px 0;
+  position: absolute;
+  z-index: 1;
+  bottom: 125%;
+  left: 50%;
+  margin-left: -80px;
+}
+.assessment-popup .assessment-popuptext::after {
+  content: "";
+  position: absolute;
+  top: 100%;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px;
+  border-style: solid;
+  border-color: #555 transparent transparent transparent;
+}
+.assessment-popup:hover .assessment-popuptext {
+  visibility: visible;
+  -webkit-animation: fadeIn 1s;
+  animation: fadeIn 1s;
+}
+@-webkit-keyframes fadeIn {
+  from {opacity: 0;} 
+  to {opacity: 1;}
+}
+@keyframes fadeIn {
+  from {opacity: 0;}
+  to {opacity:1 ;}
+}
+.assessment-empty-output {
+  height: 1.5rem;
+  width: 1.5rem;
+}

+ 1 - 1
i18n/pt/error.json

@@ -84,7 +84,7 @@
   "invalid_array_literal_column": "Esperava-se $0 colunas mas encontrou $1.",
   "exceeded_input_request": "A quantidade de leituras requisitadas execedeu a quantidade de entradas disponíveis.",
   "test_case_few_reads": "Caso de teste $0 falhou: ainda restam entradas!",
-  "test_case_failed": "<div class='assessment-div-detail' onClick='ivprogCore.openAssessmentDetail(event)' data-page=\"$4\"> <span>Caso de teste $0 falhou</span>: entrada(s): $1 | saída(s) esperada(s): $2 | saída(s): $3</div>",
+  "test_case_failed": "<div class='assessment-div-detail' onClick='ivprogCore.openAssessmentDetail(event)' data-page=\"$1\"> <span>Caso de teste $0 não executou com sucesso.</span></div>",
   "test_case_failed_exception": "<div class='assessment-div-detail' onClick='ivprogCore.openAssessmentDetail(event)' data-page=\"$2\"> <span>Caso de teste $0 falhou</span>: $1",
   "test_case_exception": "Ocorreu uma exceção no caso de teste $0: $1",
   "invalid_type_conversion": "O valor $0 não pode ser convertido para o tipo $1",

+ 3 - 1
i18n/pt/message.json

@@ -2,5 +2,7 @@
   "test_case_success": "<div class='assessment-div-detail' onClick='ivprogCore.openAssessmentDetail(event)' data-page=\"$1\"><span>Caso de teste $0</span>: OK</div>",
   "test_case_duration": "Levou $0ms",
   "test_suite_grade": "A sua solução alcançou $0% da nota.",
-  "awaiting_input_message": "O seu programa está em execução e aguardando uma entrada! Digite algo e pressione ENTER..."
+  "awaiting_input_message": "O seu programa está em execução e aguardando uma entrada! Digite algo e pressione ENTER...",
+  "assessment-empty-expected-tooltip": "A saída gerada foi além do esperado",
+  "assessment-empty-generated-tooltip": "O programa não gerou saídas suficientes"
 }

File diff suppressed because it is too large
+ 1 - 0
img/empty.svg


+ 3 - 13
js/assessment/ivprogAssessment.js

@@ -32,23 +32,13 @@ export class IVProgAssessment {
           grade += result.grade;
           if(result.grade == 1) {
             outerRef.writeToConsole(DOMConsole.INFO, StringTypes.MESSAGE,'test_case_success',
-              result.name+1, result.generateOutput());
+              result.name + 1, result.generateOutput());
           } else if (result.status == 1) {
             outerRef.writeToConsole(DOMConsole.ERR, StringTypes.ERROR,'test_case_failed_exception',
-              result.name+1,
-              result.error_msg,
-              result.generateOutput());
+              result.name + 1, result.error_msg, result.generateOutput());
           } else {
-            const inputs = result.inputs.map(input => input.value);
-            const outputs = result.results;
-            const expected_output = outputs.map(r => r.expected || '').filter( str => (''+str).length > 0);
-            const generated_output = outputs.map(r => r.generated || '').filter( str => (''+str).length > 0);
             outerRef.writeToConsole(DOMConsole.ERR, StringTypes.ERROR,'test_case_failed',
-              result.name+1,
-              inputs.join(LocalizedStrings.getUI('text_join_assessment_outputs')),
-              expected_output.join(LocalizedStrings.getUI('text_join_assessment_outputs')),
-              generated_output.join(LocalizedStrings.getUI('text_join_assessment_outputs')),
-              result.generateOutput());
+              result.name + 1, result.generateOutput());
           }
         }
         grade /= results.length;

+ 16 - 2
js/assessment/output_matching/assessment_result.js

@@ -55,6 +55,11 @@ export class OutputAssessmentResult {
             <td class=':class-result:'>$2</td></tr>`;
   }
 
+  static get EMPTY_OUTPUT_TEMPLATE () {
+    return `<div class='assessment-popup'><img class='assessment-empty-output' src='img/empty.svg'>
+      <span class='assessment-popuptext'>$0</span></div>`;
+  }
+
   static get FAILED_TEMPLATE () {
     return `<p class='assessment-failed-execution'><span class='assessment-failed-case'>✗</span>$0</p>`;
   }
@@ -165,8 +170,17 @@ export class OutputAssessmentResult {
     template = template.replace(":class-expected:", expected_class);
     template = template.replace(":class-generated:", generated_class);
     template = template.replace(":class-result:", result_class);
-    template = template.replace("$0", result.expected);
-    template = template.replace("$1", result.generated);
+    let expected_tmpl = result.expected;
+    let generated_tmpl = result.generated;
+    if(expected_tmpl == null) {
+      expected_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace('$0',
+        LocalizedStrings.getMessage('assessment-empty-expected-tooltip'));
+    } else if(generated_tmpl == null) {
+      generated_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace('$0',
+        LocalizedStrings.getMessage('assessment-empty-generated-tooltip'));
+    }
+    template = template.replace("$0", expected_tmpl);
+    template = template.replace("$1", generated_tmpl);
     const final_result = result.grade == 1 ? "✓" : "✗"
     template = template.replace("$2", final_result);
     return template

+ 1 - 0
webpack.config.js

@@ -65,6 +65,7 @@ module.exports = {
         {from:"css/fonts/", to:path.resolve(__dirname, 'build/css/fonts')},
         {from:'js/Sortable.js', to:path.resolve(__dirname, 'build/js')},
         {from: 'img/trash-icon.png', to:path.resolve(__dirname, 'build/img')},
+        {from: 'img/empty.svg', to:path.resolve(__dirname, 'build/img')},
         {from:'js/jquery.json-editor.min.js', to:path.resolve(__dirname, 'build/js')},
         {from:'node_modules/codemirror/lib/codemirror.css', to:path.resolve(__dirname, 'build/css')},
         {from:'node_modules/codemirror/addon/hint/show-hint.css', to:path.resolve(__dirname, 'build/css')},