From d4d066fca8a2a3141267a3642d5ca6806a3995b6 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Fri, 29 Nov 2019 14:30:21 +0100
Subject: [PATCH] Added rendering of asciimath in submissions

---
 core/models/submission_type.py                |  2 +
 frontend/package.json                         | 10 ++--
 frontend/public/index.html                    |  8 ++-
 .../submission_notes/SubmissionCorrection.vue |  7 ++-
 .../base/BaseAnnotatedSubmission.vue          |  5 +-
 .../submission_notes/base/SubmissionLine.vue  | 13 ++++-
 .../AnnotatedSubmissionTopToolbar.vue         | 50 ++++++++++++++++++-
 .../submission_type/SubmissionType.vue        | 11 ++--
 frontend/src/models.ts                        |  5 +-
 .../src/store/modules/submission-notes.ts     | 10 +++-
 10 files changed, 101 insertions(+), 20 deletions(-)

diff --git a/core/models/submission_type.py b/core/models/submission_type.py
index e33a1720..9cdb7982 100644
--- a/core/models/submission_type.py
+++ b/core/models/submission_type.py
@@ -37,6 +37,7 @@ class SubmissionType(models.Model):
     HASKELL = 'haskell'
     TEXT = 'plaintext'
     PYTHON = 'python'
+    MARKDOWN = 'markdown'
 
     LANGUAGE_CHOICES = (
         (C, 'C syntax highlighting'),
@@ -44,6 +45,7 @@ class SubmissionType(models.Model):
         (MIPS, 'Mips syntax highlighting'),
         (HASKELL, 'Haskell syntax highlighting'),
         (PYTHON, 'Python syntax highlighting'),
+        (MARKDOWN, 'Markdown syntax highlighting with asciimath rendering'),
         (TEXT, 'No syntax highlighting'),
     )
 
diff --git a/frontend/package.json b/frontend/package.json
index 0f0fdcd0..30f0dd1a 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -35,20 +35,20 @@
     "@types/mocha": "^5.2.5",
     "@types/nightwatch": "^0.9.8",
     "@types/sinon": "^7.0.2",
+    "@typescript-eslint/eslint-plugin": "^2.3.2",
+    "@typescript-eslint/parser": "^2.3.2",
     "@vue/cli-plugin-eslint": "^3.11.0",
     "@vue/cli-plugin-typescript": "^3.11.0",
     "@vue/cli-plugin-unit-mocha": "^3.11.0",
     "@vue/cli-service": "^3.11.0",
-    "@vue/test-utils": "^1.0.0-beta.29",
     "@vue/eslint-config-typescript": "^4.0.0",
-    "@typescript-eslint/parser": "^2.3.2",
-    "@typescript-eslint/eslint-plugin": "^2.3.2",
+    "@vue/test-utils": "^1.0.0-beta.29",
     "chai": "^4.2.0",
+    "eslint": "^5.16.0",
+    "eslint-plugin-vue": "^5.0.0",
     "mocha": "^5.2.0",
     "mock-local-storage": "^1.1.8",
     "sinon": "^7.2.2",
-    "eslint": "^5.16.0",
-    "eslint-plugin-vue": "^5.0.0",
     "typescript": "^3.4.3",
     "vue-template-compiler": "^2.5.16",
     "webpack": "^4.41.0"
diff --git a/frontend/public/index.html b/frontend/public/index.html
index 8008677d..f2f04e97 100644
--- a/frontend/public/index.html
+++ b/frontend/public/index.html
@@ -6,11 +6,14 @@
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <title>Grady</title>
 
-    <script id="MathJax-script"  async src="https://grady.informatik.uni-goettingen.de/static/mathjax/es5/tex-mml-chtml.js"></script>
     <script>
       MathJax = {
+        loader: {load: ['input/asciimath', 'output/chtml', 'input/tex']},
+        asciimath: {
+          delimiters: [['$', '$']]
+        },
         options: {
-          skipHtmlTags: [            //  HTML tags that won't be searched for math
+          skipHtmlTags: [//  HTML tags that won't be searched for math
               'script', 'noscript', 'style', 'textarea', 'pre',
               'code', 'annotation', 'annotation-xml'
           ],
@@ -22,6 +25,7 @@
         }
       };
     </script>
+    <script id="MathJax-script"  async src="https://grady.informatik.uni-goettingen.de/static/mathjax/es5/startup.js"></script>
   </head>
   <body class="tex2jax_ignore">
     <noscript>
diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index ac298e83..c0213655 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -79,7 +79,7 @@ import FeedbackLabel from '@/components/feedback_labels/FeedbackLabel.vue'
 import { FeedbackLabels as Labels } from '@/store/modules/feedback-labels'
 import LabelSelector from '@/components/feedback_labels/LabelSelector.vue'
 import SubmissionLine from '@/components/submission_notes/base/SubmissionLine'
-import { SubmissionNotes } from '@/store/modules/submission-notes'
+import { SubmissionNotes, subNotesEventBus } from '@/store/modules/submission-notes'
 import { Authentication } from '@/store/modules/authentication'
 import { actions } from '@/store/actions'
 import { fetchFeedback } from '@/api'
@@ -148,6 +148,11 @@ export default {
     },
     submissionWithoutAssignment: function () {
       this.init()
+    },
+    submission: function (oldVal, newVal) {
+      if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
+        subNotesEventBus.$emit('submissionChanged')
+      }
     }
   },
   created () {
diff --git a/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue b/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue
index 9cb5de21..d6c48276 100644
--- a/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue
+++ b/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue
@@ -1,7 +1,10 @@
 <template>
   <div>
     <slot name="header" />
-    <table class="submission-table elevation-1">
+    <table
+      id="submission-table"
+      class="latex submission-table elevation-1"
+    >
       <slot name="table-content" />
     </table>
     <slot name="labels" />
diff --git a/frontend/src/components/submission_notes/base/SubmissionLine.vue b/frontend/src/components/submission_notes/base/SubmissionLine.vue
index 7cf21589..d2170684 100644
--- a/frontend/src/components/submission_notes/base/SubmissionLine.vue
+++ b/frontend/src/components/submission_notes/base/SubmissionLine.vue
@@ -16,13 +16,14 @@
     </td>
     <td class="code-cell-content pl-2">
       <!-- eslint-disable-next-line -->
-      <span class="code-line" v-html="code"/>
+      <span class="code-line" :key="key" v-html="code"/>
       <slot />
     </td>
   </div>
 </template>
 
 <script>
+import { subNotesEventBus } from '../../../store/modules/submission-notes'
 export default {
   name: 'SubmissionLine',
   props: {
@@ -43,11 +44,21 @@ export default {
       default: false,
     },
   },
+  data () {
+    return {
+      key: 0
+    }
+  },
   computed: {
     backgroundColor() {
       return this.hint ? 'background-color: #F44336;' : 'background-color: transparent;'
     }
   },
+  created () {
+    subNotesEventBus.$on('resetSubmission', () => {
+      this.key++
+    })
+  },
   methods: {
     toggleEditor () {
       this.$emit('toggleEditor')
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue
index 204b45ce..b1ad9618 100644
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue
+++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue
@@ -14,6 +14,20 @@
     </v-dialog>
     <span class="title">Submission of {{ ofStudent }}</span>
     <toggle-feedback-visibility-button />
+    <div v-if="isMarkdown">
+      <v-btn
+        v-if="!mathIsRendered"
+        @click="renderAsciiMath"
+      >
+        Render Math
+      </v-btn>
+      <v-btn
+        v-else
+        @click="resetSubmission"
+      >
+        Reset Math
+      </v-btn>
+    </div>
     <v-spacer />
     <v-tooltip
       v-if="sourceCodeAvailable"
@@ -45,9 +59,12 @@
 import CorrectionHelpCard from '@/components/submission_notes/CorrectionHelpCard'
 import { mapState } from 'vuex'
 import ToggleFeedbackVisibilityButton from '@/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton'
-import { SubmissionNotes } from '@/store/modules/submission-notes'
+import { SubmissionNotes, subNotesEventBus } from '@/store/modules/submission-notes'
 import {fetchSubmissionSourceCode} from '@/api.ts'
 import {saveAs} from 'file-saver'
+import store from '../../../store/store'
+import { SubmissionType } from '../../../models'
+import Vue from 'vue'
 
 export default {
   name: 'AnnotatedSubmissionTopToolbar',
@@ -63,15 +80,44 @@ export default {
   data () {
     return {
       helpDialog: false,
-      copyMessage: 'Copy to clipboard'
+      copyMessage: 'Copy to clipboard',
+      mathIsRendered: false
     }
   },
   computed: {
     sourceCodeAvailable () {
       return SubmissionNotes.state.submission.sourceCodeAvailable
+    },
+    isMarkdown () {
+      const typePk = SubmissionNotes.state.submission.type
+      const type = store.state.submissionTypes[typePk]
+      return type.programmingLanguage === SubmissionType.ProgrammingLanguageEnum.Markdown
+    }
+  },
+  created () {
+    subNotesEventBus.$on('submissionChanged', () => {
+      if (this.mathIsRendered) {
+        this.$nextTick(() => {
+          this.renderAsciiMath()
+        })
+      }
+    })
+  },
+  mounted () {
+    const lang = SubmissionNotes.submissionType.programmingLanguage
+    if (lang === SubmissionType.ProgrammingLanguageEnum.Markdown) {
+      this.renderAsciiMath()
     }
   },
   methods: {
+    renderAsciiMath () {
+      window.MathJax.typeset()
+      this.mathIsRendered = true
+    },
+    resetSubmission () {
+      this.mathIsRendered = false
+      subNotesEventBus.$emit('resetSubmission')
+    },
     async downloadSourceCode () {
       const data = await fetchSubmissionSourceCode(SubmissionNotes.state.submission.pk)
       saveAs(new Blob([data.sourceCode], {type: 'application/json'}), 'notebook.ipynb')
diff --git a/frontend/src/components/submission_type/SubmissionType.vue b/frontend/src/components/submission_type/SubmissionType.vue
index 138d1ae9..91ad3ade 100644
--- a/frontend/src/components/submission_type/SubmissionType.vue
+++ b/frontend/src/components/submission_type/SubmissionType.vue
@@ -137,10 +137,13 @@ export default class SubmissionType extends Vue {
   }
 
   @Watch('description')
-  onDescriptionChanged(val: string) {
-    this.$nextTick().then(() => {
-      window.MathJax.typeset()
-    })
+  onDescriptionChanged(newVal: string, oldVal: string) {
+    console.log(`desc rerender old  ${oldVal} new ${newVal}`)
+    if (oldVal !== newVal) {
+      this.$nextTick().then(() => {
+        window.MathJax.typeset()
+      })
+    }
   }
 
   mounted () {
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index 1982c228..6b7b44ea 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -640,8 +640,9 @@ export namespace SubmissionType {
      * @enum {string}
      */
     export enum ProgrammingLanguageEnum {
-        C = 'c' as any,
-        Java = 'java' as any
+        C = 'c',
+        Java = 'java',
+        Markdown = 'markdown'
     }
 }
 
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index 0870424b..78bc156b 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -1,13 +1,15 @@
 import Vue from 'vue'
 import * as hljs from 'highlight.js'
 import * as api from '@/api'
-import { Feedback, FeedbackComment, SubmissionNoType, CreateUpdateFeedback } from '@/models'
+import { Feedback, FeedbackComment, SubmissionNoType, CreateUpdateFeedback, SubmissionType } from '@/models'
 import { RootState } from '@/store/store'
 import { getStoreBuilder, BareActionContext } from 'vuex-typex'
 import { syntaxPostProcess } from '@/util/helpers'
 import { AxiosResponse } from 'axios'
 import { Assignments } from './assignments'
 
+export const subNotesEventBus = new Vue()
+
 export interface SubmissionNotesState {
   submission: SubmissionNoType
   ui: {
@@ -70,7 +72,11 @@ const submissionGetter = mb.read(function submission(state, getters) {
   const language = getters.submissionType
     ? getters.submissionType.programmingLanguage
     : 'c'
-  const highlighted = hljs.highlight(language, state.submission.text || '', true).value
+  let highlighted = state.submission.text || ''
+  if (language !== SubmissionType.ProgrammingLanguageEnum.Markdown) {
+    highlighted = hljs.highlight(language, highlighted, true).value
+  }
+  // const highlighted = state.submission.text || ''
   const postProcessed = syntaxPostProcess(highlighted)
   const splitted = postProcessed.split('\n').reduce((acc: { [k: number]: string }, cur, index) => {
     acc[index + 1] = cur
-- 
GitLab