Commit fccce89b authored by Joachim Schunk's avatar Joachim Schunk
Browse files

Merge branch 'master' into 28-create-new-multistep-calculation-exercise-type

parents e6e2480f 8bbc5b27
Pipeline #107165 passed with stages
in 2 minutes and 29 seconds
(defproject wwsoftware/leukipp.components "1.3.3"
(defproject wwsoftware/leukipp.components "1.3.10"
:repositories [["internal" {:url "https://leukipp.fh-muenster.de:9082/releases"
:username "anonymous"}]]
:dependencies [[org.clojure/clojure "1.10.3"]
......
......@@ -245,10 +245,18 @@
[:&.inactive
{:opacity 0.15}]]]
[:.exercise-editor.exercise-multistep-calculation
[:.modal-card {:overflow :revert}
[:.dropdown.is-active
[:.dropdown-menu
{:position :inherit}]]]])
[:.modal-card {:overflow :revert}
[:.dropdown.is-active
[:.dropdown-menu
{:position :inherit}]]]]
[:.unit-tests
[:.unit-test {:padding "0.5rem"}]
[:.correct-test
{:border-left "1rem solid green"}]
[:.false-test
{:border-left "1rem solid red"}]
[:.error-test
{:border-left "1rem solid black"}]])
;;; TODO ul / li innerhalb von ce-section mit list-style-type: initial
......
......@@ -57,28 +57,30 @@
(:contents main-file))]
(assoc-in answer [:files (:id main-file) :code]
(build-unit-code unit-test contents language))))
;;;FIXME use :contents in answer file instead of :code...
(defn get-unit-title [unit-test]
(when (:show-title? unit-test)
(:title unit-test)))
(defn unit-test [exercise unit-test answer language]
(let [testcode (build-unit-test exercise unit-test answer language)]
(println "UNIT TEST")
(println testcode)
(try
(if (is-correct? ((get-run-code-fn) exercise testcode
language))
{:id (:id unit-test)
:correct true
:points (:correct-points unit-test)}
{:id (:id unit-test)
:correct false
:points (:incorrect-points unit-test)})
(catch Exception ex
{:id (:id unit-test)
:correct false
:points 0
:error true
:exception ex}))))
(let [testcode (build-unit-test exercise unit-test answer language)
title (get-unit-title unit-test)]
(merge {:id (:id unit-test)
:title title}
(try
(if (is-correct? ((get-run-code-fn) exercise testcode
language))
{:correct true
:points (:correct-points unit-test)}
{:correct false
:points (:incorrect-points unit-test)})
(catch Exception ex
{:correct false
:points 0
:error true
:exception ex})))))
(defn check [exercise answer callback]
(callback)
......
......@@ -9,7 +9,8 @@
(defn prepare-frontend [exercise]
(-> exercise
filter-hidden-files))
#_filter-hidden-files ;; files will be send to the frontend but will not be listed.
))
(defn prepare-backend [exercise]
exercise)
......
......@@ -11,7 +11,8 @@
:key :python3
:rendering #{:server}
:file-extension ".py"
:options {:allows-modules true}}
:options {:allows-modules true
:allows-console-input true}}
{:title "Common Lisp"
:key :common-lisp
:file-extension ".cl"
......@@ -63,6 +64,7 @@
:programming-question/correct-points
:programming-question/incorrect-points]))
(s/def :programming-question/unit-tests (s/coll-of :programming-question/unit-test :kind vector? :min-count 1 :distinct true))
(s/def :programming-question/allow-console-input boolean?)
(s/def :programming-question/language (set (map :key programming-languages)))
(s/def :programming-question/code-template (s/and string? (comp not clojure.string/blank?)))
(s/def :programming-question/type #{:programming})
......@@ -70,6 +72,7 @@
:programming-question/unit-tests
:programming-question/type
:programming-question/files]
:opt-un [:programming-question/code-template]))
:opt-un [:programming-question/code-template
:programming-question/allow-console-input]))
(s/def ::programming-question (s/keys :req-un [:programming-question/core]))
......@@ -112,7 +112,7 @@
:background-color (if error
"#FF7777"
"#EEEEFF")
:white-space :pre-line
:white-space :pre-wrap
:padding "10px"
:font-size "0.9em"
:display :inline-block}}
......
......@@ -107,7 +107,8 @@
feedback (:feedback options)
on-change (or (:on-change options) identity)
check-fn (:check-fn options)
reset-fn (:reset-fn options)]
reset-fn (:reset-fn options)
remove-feedback-fn (:remove-feedback-fn options)]
(if-not exercise
[:div [:p.has-background-danger "No valid exercise found..."]
[:p "Element: " (str element)]
......@@ -143,12 +144,16 @@
^{:key (str "feedback-ce" (:id ce))}
[ce-core/show ce feedback-options]))})
feedback)}]])
(when (or check-fn reset-fn)
(when (or check-fn remove-feedback-fn reset-fn)
[:div.level.my-1
(when reset-fn
[:div.level-item
[:a.button {:on-click (:reset-fn options)}
"Aufgabe zurücksetzen"]])
(when remove-feedback-fn
[:div.level-item
[:a.button {:on-click remove-feedback-fn}
"Feedback entfernen"]])
(when (and check-fn (not result))
[:div.level-item
[:a.button {:on-click (:check-fn options)}
......
......@@ -7,33 +7,35 @@
[reagent.core :as reagent]))
(defn init-tinymce [id editor element]
(-> (ocall js/tinymce "init"
(clj->js {:selector (str "#" id)
:plugins "table lists link codesample paste"
:codesample_global_prismjs true
:codesample_languages [{:text "C" :value "c"}
{:text "C++" :value "cpp"}
{:text "Clojure" :value "clojure"}
{:text "Java" :value "java"}
{:text "JavaScript" :value "javascript"}
{:text "Konsole/Bash" :value "bash"}
{:text "Python" :value "python"}
{:text "R" :value "r"}]
:paste_as_text "true"
:paste_enable_default_filters "false"
:paste_retain_style_properties "width"
:paste_word_valid_elements "b,strong,i,em,u,s,del,sub,sup,h1,h2,h3,h4,table,tr,td,ul,li"
:toolbar "undo redo | formatselect | bold italic strikethrough forecolor backcolor permanentpen formatpainter | link codesample | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat | addcomment"
:default_link_target "_blank"
:height "500"}))
(ocall "then" (comp (partial reset! editor) first))
(ocall "then" (fn [e]
(ocall e "setContent" (:value @element ""))
e))
(ocall "then" (fn [e]
(.on e "Change"
#(let [content (ocall (oget % "target") "getContent")]
(swap! element assoc :value content)))))))
(if (exists? js/tinymce)
(-> (js-invoke js/tinymce "init"
(clj->js {:selector (str "#" id)
:plugins "table lists link codesample paste"
:codesample_global_prismjs true
:codesample_languages [{:text "C" :value "c"}
{:text "C++" :value "cpp"}
{:text "Clojure" :value "clojure"}
{:text "Java" :value "java"}
{:text "JavaScript" :value "javascript"}
{:text "Konsole/Bash" :value "bash"}
{:text "Python" :value "python"}
{:text "R" :value "r"}]
:paste_as_text "true"
:paste_enable_default_filters "false"
:paste_retain_style_properties "width"
:paste_word_valid_elements "b,strong,i,em,u,s,del,sub,sup,h1,h2,h3,h4,table,tr,td,ul,li"
:toolbar "undo redo | formatselect | bold italic strikethrough forecolor backcolor permanentpen formatpainter | link codesample | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat | addcomment"
:default_link_target "_blank"
:height "500"}))
(js-invoke "then" (comp (partial reset! editor) first))
(js-invoke "then" (fn [e]
(js-invoke e "setContent" (:value @element ""))
e))
(js-invoke "then" (fn [e]
(.on e "Change"
#(let [content (js-invoke (oget % "target") "getContent")]
(swap! element assoc :value content))))))
(throw (js/Error. "TinyMCE not found."))))
(defmethod ce-core/edit :section [element options]
(let [editor (reagent/atom nil)
......@@ -50,10 +52,11 @@
(init-tinymce id editor element)
(reset! error nil)
(catch js/Error e
(js/console.debug e)
(reset! error (str "Fehler beim Laden des Texteditor: " e)))))))
:reagent-render
(fn [element options]
[:<>
[:div {:key (str "editor-" id "-" @error)}
(if @error
[:div.notification.is-warning
[:p (str @error)]]
......
......@@ -10,7 +10,8 @@
[leukipp.components.exercise-types.programming.views.edit.unit-tests :as unit-tests]
[leukipp.components.exercise-types.programming.spec :refer [serverside-languages programming-languages]]
[reagent.core :as reagent]
[leukipp.components.jshelper :refer [get-unique-id]]))
[leukipp.components.jshelper :refer [get-unique-id]]
[clj-helper.vector :as vhelper]))
(defn settings [exercise-core]
......@@ -26,15 +27,22 @@
(swap! exercise-core assoc :unit-tests [])))
:reagent-render
(fn [exercise-core]
(let [{:keys [id question code-template language]} @exercise-core]
(let [{:keys [id question code-template language allow-console-input]} @exercise-core
planguage (vhelper/get-by programming-languages :key language)]
[:div
[:div.row
[:div.col.s12
[:div.columns.is-multiline
[:div.column.is-12
[ui/field {:label "Programmiersprachen"}
[ui/select
{:value (or language "")
:options (sort-by :title serverside-languages)
:on-change #(swap! exercise-core assoc :language %)}]]]]]))})))
:on-change #(swap! exercise-core assoc :language %)}]]]
(when (true? (get-in planguage [:options :allows-console-input]))
[:div.column.is-12
[ui/field {:label "Konsoleneingabe"}
[ui/checkbox {:checked? (true? allow-console-input)
:on-change #(swap! exercise-core assoc :allow-console-input (not allow-console-input))
:label "Eingabefeld für Konsoleneingabe"}]]])]]))})))
(defn additional-forms [exercise-core]
......
......@@ -41,7 +41,7 @@
{:component-did-mount (fn [])
:reagent-render
(fn [test index delete-fn]
(let [{:keys [title testcode incorrect-points correct-points]} @test]
(let [{:keys [title show-title? testcode incorrect-points correct-points]} @test]
[ui/card
{:title (str "Unit-Test " title)
:footer-items [[:a.button.footer-item
......@@ -50,10 +50,17 @@
[:span "Test entfernen"]]]}
;;; TODO collapsible with open as start-state?
[:div.new-question.z-depth-3
[ui/field {:label "Titel"}
[ui/input-text
{:value title
:on-change #(swap! test assoc :title %)}]]
[ui/horizontal-field {}
[ui/field {:label "Titel"}
[ui/input-text
{:value title
:on-change #(swap! test assoc :title %)}]]
[ui/field {:label "Einstellungen"}
[ui/checkbox {:checked? show-title?
:on-change #(swap! test assoc :show-title? (not show-title?))
:label [:acronym {:title "In der Aufgabenauswertung wird dem Benutzer der Titel und das Ergebnis (Richtig/Falsch) dieses Unit-Tests angezeigt."}
"Titel sichtbar"]}]]]
[:div.columns.is-multiline
[:div.column.is-half
[ui/field {:label "Punkte (korrekt)"}
......
......@@ -22,39 +22,22 @@
clojure.string/upper-case
(clojure.string/ends-with? "FALSE")))))
(defn test-result [tr idx unit-test]
(let [testname (str (:title unit-test))]
(fn [tr]
(console.debug (clj->js tr))
(if (:isError tr)
[:div.card.card-danger.mb-3.text-center
[:div.card-block
[:blockquote.card-blockquote
[:h4 testname]
[:strong (str "Test fehlgeschlagen")]
[:p (str (:stderr tr))]
]
]
]
(if-not (is-correct tr)
[:div.card.card-warning.mb-3.text-center
[:div.card-block
[:blockquote.card-blockquote
[:h4 testname]
[:strong (str "Test fehlgeschlagen")]
]
]
]
[:div.card.card-success.mb-3.text-center
[:div.card-block
[:blockquote.card-blockquote
[:h4 testname]
[:strong (str "Test erfolgreich")]
]
]
])
))
))
(defn test-result [unit-test]
(let [testname (str (:title unit-test))
class (cond
(:error unit-test) "error-test"
(:correct unit-test) "correct-test"
:else "false-test")
explanation (cond
(:error unit-test) "Test konnte fehlerbedingt nicht abgeschlossen werden."
(:correct unit-test) "Test erfolgreich"
:else "Test fehlgeschlagen")]
[:div.unit-test.has-background-light {:class class}
[:div.card-panel
[:acronym {:title explanation}
testname]]]))
(defn code-editor [{:keys [code language read-only on-change cm-editor-id]}]
......@@ -135,6 +118,7 @@
:on-change #(on-change (assoc-in answer [:files :main :code] %))
:cm-editor-id (str id-prefix "-main")}]}])
(for [f (:files (:core exercise))
:when (not (:hidden f))
:let [fid (:id f (gensym "f"))
read-only (or disabled
(:read-only f))]]
......@@ -153,7 +137,19 @@
:read-only read-only
:on-change #(when-not read-only
(on-change (assoc-in answer [:files fid :code] %)))
:cm-editor-id (str id-prefix "-" fid)}]]}))}]))
:cm-editor-id (str id-prefix "-" fid)}]]})
(when (true? (get-in exercise [:core :allow-console-input]))
[{:id (str id-prefix "-console-input")
:title [:<>
[ui/icon {:style :solid
:id :fa-terminal}]
[:em "Konsoleneingabe"]]
:component [code-editor
{:code (get-in answer [:stdin :code])
:language "shell"
:read-only (true? disabled)
:on-change #(on-change (assoc-in answer [:stdin :code] %))
:cm-editor-id (str id-prefix "-console-input")}]}]))}]))
(defn update-answer-files [answer result]
(reduce (fn [answer [file-id contents]]
......@@ -179,7 +175,7 @@
:reagent-render
(fn [exercise {:keys [result answer on-change] :as data}]
(let [answer (migration/migrate-answer answer exercise)
test-results (get result :test-results)
test-results (get result :unit-tests)
coderesult (:code-result @status)
code-runner-pending (:code-runner-pending @status)
......@@ -226,12 +222,14 @@
(< 0 (:points result) (:points-max result))) [common/neutral-tick]
(and (:points result) (<= (:points result) 0)) [common/wrong-tick]
:else "")
(if (pos? (count code-testrunner-pending))
[:div.progress.blue.lighten-5
[:div.indeterminate.blue]]
(for [tr test-results]
^{:key (str eid (first tr))}
[test-result (second tr) (first tr) (get-in core [:unit-tests (first tr)])]))])]
[:div.unit-tests
(if (pos? (count code-testrunner-pending))
[:div.progress.blue.lighten-5
[:div.indeterminate.blue]]
(for [tr test-results
:when (:title tr)]
^{:key (str eid (:id tr))}
[test-result tr]))]])]
(when-not result
[:footer.card-footer
(if code-runner-pending
......
......@@ -4,7 +4,8 @@
[leukipp.components.i18n.core :refer [i18n-get]]
[oops.core :refer [oset!]]
[reagent.core :as reagent]
[reagent.dom :as rdom]))
[reagent.dom :as rdom]
[goog.object :as gobj]))
(defn chart [{:keys [answers answers-count]}]
......@@ -35,13 +36,11 @@
(fn [comp]
(let [answers (:answers (reagent/props comp))
answers-count (:answers-count (reagent/props comp))]
(oset! @chart-instance "data.labels"
(clj->js (map :label answers)))
(oset! @chart-instance "data.datasets.0.data"
(clj->js (map :count answers)))
(oset! @chart-instance "options.scales.x.0.ticks.max"
(clj->js answers-count))
(.update @chart-instance)))
(oset! @chart-instance "data.labels"
(clj->js (map :label answers)))
(oset! @chart-instance "data.datasets.0.data"
(clj->js (map :count answers))))
(.update @chart-instance))
:component-will-unmount
(fn []
(when @chart-instance
......
......@@ -43,8 +43,6 @@
(i18n-get (:text a))) answers)))
(oset! @chart-instance "data.datasets.0.data"
(clj->js (map :count answers)))
(oset! @chart-instance "options.scales.x.0.ticks.max"
(clj->js answers-count))
(.update @chart-instance)))
:component-will-unmount
(fn []
......
......@@ -26,15 +26,15 @@
#_(add-option!
:programming-exercise {:code-runner-fn
(fn [& {:keys [params handler]}]
(POST "http://localhost:3005/compile"
{:handler handler
:params params
:format :json
:response-format :json
:keywords? true
:headers {:X-API-KEY "...PyCodeExecution__3}dx"}}))})
:programming-exercise {:code-runner-fn
(fn [& {:keys [params handler]}]
(POST "http://localhost:3005/compile"
{:handler handler
:params params
:format :json
:response-format :json
:keywords? true
:headers {:X-API-KEY "...PyCodeExecution__3}dx"}}))})
(defmethod content-manager/receive-options :programming [element]
{:exercise element
......@@ -91,9 +91,9 @@
[:button.button {:on-click #(swap! switch not)} "Toggle result"]
[content-manager/show (migration/migrate-frontend exercise-data)
{;;{:points 3 :points-max 6 :correct true}
:answer @answer
:result (when @switch
{:failed 2 :passed 7 :points 2 :points-max 5})
:answer @answer
:result (when @switch
{:failed 2 :passed 7 :points 2 :points-max 5})
:on-change #(reset! answer %)}]])
answer
{:inspect-data true})
......@@ -104,16 +104,29 @@
[:button.button {:on-click #(swap! switch not)} "Toggle result"]
[exercise/render-exercise exercise-data
{;;{:points 3 :points-max 6 :correct true}
:answer @answer
:result (when @switch
{:failed 2 :passed 7 :points 2 :points-max 5})
:on-change #(reset! answer %)}]]))
:answer @answer
:result (when @switch
{:failed 2 :passed 7 :points 2 :points-max 5})
:on-change #(reset! answer %)}]]))
(defcard-rg prog-exercise-answered
(fn []
[:div
[exercise/render-exercise exercise-data
{;;{:points 3 :points-max 6 :correct true}
:answer (:code-template (:core exercise-data))
:result {:failed 2 :passed 7 :points 2 :points-max 5}
:on-change #(reset! answer %)}]]))
:answer (:code-template (:core exercise-data))
:result {:unit-tests [{:id :test1
:title "sichtbarer Titel, Test1"
:correct true
:points 3}
{:id :test2
:title nil ;;; show-title? false
:points -5
:correct false}
{:id :test2
:title nil ;;; show-title? false
:points 0
:error true
:exception "Division by Zero, exception ..."}]
:failed 2 :passed 7 :points 2 :points-max 5}
:on-change #(reset! answer %)}]]))
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment