소스 검색

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 년 전
부모
커밋
4c0094e773
8개의 변경된 파일94개의 추가작업 그리고 19개의 파일을 삭제
  1. 21 1
      README.md
  2. 48 1
      css/ivprog-assessment.css
  3. 1 1
      i18n/pt/error.json
  4. 3 1
      i18n/pt/message.json
  5. 1 0
      img/empty.svg
  6. 3 13
      js/assessment/ivprogAssessment.js
  7. 16 2
      js/assessment/output_matching/assessment_result.js
  8. 1 0
      webpack.config.js

+ 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"
 }

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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')},