Commit 359dadcb authored by Bruno Burke's avatar Bruno Burke 😁

Merge branch 'master' into 15-evaluation-voting-components

parents 629c1446 c89b24e9
Pipeline #47862 passed with stages
in 2 minutes
Bruno Burke <burke@fh-muenster.de> Bruno Burke <bb756626@fb11-nx-main.fh-muenster.de>
Bruno Burke <burke@fh-muenster.de> Bruno Burke <bb756626@fh-muenster.de>
Bruno Burke <burke@fh-muenster.de> Bruno Burke <brunoburke@googlemail.com>
Bruno Burke <burke@fh-muenster.de> Bruno Burke <burke@protonmail.ch>
Joachim Schunk <schunk@fh-muenster.de> schunk <schunk@linux.fritz.box>
Joachim Schunk <schunk@fh-muenster.de> schunk <schunk@localhost.localdomain>
Joachim Schunk <schunk@fh-muenster.de> schunk <schunk@solydxk>
(defproject lernmeister.components "1.0.0.1-evaluation"
(defproject lernmeister.components "1.0.0.7-evaluation"
:repositories [["internal" {:url "https://lernmeister.fh-muenster.de:8082/artifactory/libs-release"
:username "anonymous"}]]
:dependencies [[org.clojure/clojure "1.10.0"]
......
......@@ -52,13 +52,13 @@
[:.prefix {:grid-column "1 / 2"
:grid-row "2 / 4"}]
[:.unit {:grid-column "2 / 3"
:grid-row "2 / 4"}]
:grid-row "2 / 4"}]
[:.exponent {:align-self "end"
:grid-column "3 / 4"
:grid-row "1 / 3"}]
[:.action {:align-self "end"
:grid-column "4 / 5"
:grid-row "1 / 3"}]]
:grid-column "4 / 5"
:grid-row "1 / 3"}]]
[:.no-border {:border-bottom "none !important"}]
[:div.col.unit-chip {:border "1px solid grey"
:border-radius "2px"}
......@@ -171,17 +171,14 @@
[:&.right-icon {:padding disc-phys-val-icon-padding
:border-top-right-radius disc-phys-val-r :border-bottom-right-radius disc-phys-val-r
:border-top-style "solid" :border-right-style "solid" :border-bottom-style "solid"}]]]
[:div.var-config {:display "flex" :align-items "center"}
[:div.symbols {:margin-left "10px"}]]
[:div.toastbox
{:position :fixed
:margin-right "1rem"
:margin-top "1rem"
:right "0px"
:top "0px"
:z-index "1000"
}
]
:z-index "1000"}]
[:div.toast
{:opacity 0
:position :relative
......@@ -189,8 +186,7 @@
:margin-top "1rem"
:z-index "1000"
"-webkit-transition" "opacity 1000ms linear"
:transition "opacity 1000ms linear"
}
:transition "opacity 1000ms linear"}
[:&.active
{:opacity 1}]]
[:div.var-select {:cursor "pointer"}
......@@ -218,9 +214,8 @@
:top "0"
:pointer-events "none"
;;:visibility "hidden"
:background-color "#323232"
}
]
:background-color "#323232"}]
[:div.card
{:margin ".5rem 0 1rem 0"}]
[:div.collapsible-component
......@@ -228,8 +223,7 @@
[:div.card
{:margin "0px"}]]
[:div.vspace
{:height "1.6em"}]
)
{:height "1.6em"}])
;;; TODO ul / li innerhalb von ce-section mit list-style-type: initial
......
(ns lernmeister.components.exercise-types.calculation.build
(:require [lernmeister.components.exercise-types.build :as e-build]
[lernmeister.components.exercise-types.calculation.migrate
:refer
[exercise-scheme-version migrate-exercise]]
[lernmeister.components.helper :refer [migrate-if-necessary]]))
(defn build [exercise]
(if-let [migrated-exercise (migrate-if-necessary exercise :exercise-scheme "calculation"
exercise-scheme-version migrate-exercise)]
migrated-exercise
(-> exercise
(select-keys [:title :id])
(assoc :debug-info "unmigrateable"))))
(defmethod e-build/build :calculation [exercise & _]
(build exercise))
(ns lernmeister.components.exercise-types.calculation.check
(:require [clojure.string :as string]
(:require [lernmeister.components.exercise-types.calculation.migrate-answer
:refer
[answer-scheme-version migrate-answer]]
[lernmeister.components.exercise-types.check :as e-check]
[clojure.tools.reader.edn :as edn]
[lernmeister.components.helper :refer [math-abs]]))
(def prefix-mapping {"Y" 24
"Z" 21
"E" 18
"P" 15
"T" 12
"G" 9
"M" 6
"k" 3
"h" 2
"d" -1
"c" -2
"m" -3
"µ" -6
"n" -9
"p" -12
"f" -15
"a" -18
"z" -21
"y" -24
"" 0
nil 0})
(def si-unit-mapping {"m" [{:factor 1 :oom 0 :unit "m" :expo 1}]
"g" [{:factor 1 :oom -3 :unit "kg" :expo 1}]
"s" [{:factor 1 :oom 0 :unit "s" :expo 1}]
"A" [{:factor 1 :oom 0 :unit "A" :expo 1}]
"K" [{:factor 1 :oom 0 :unit "K" :expo 1}]
"mol" [{:factor 1 :oom 0 :unit "mol" :expo 1}]
"cd" [{:factor 1 :oom 0 :unit "cd" :expo 1}]
"rad" []
"sr" []
"Hz" [{:factor 1 :oom 0 :unit "s" :expo -1}]
"N" [{:factor 1 :oom 0 :unit "m" :expo 1}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -2}]
"Pa" [{:factor 1 :oom 0 :unit "m" :expo -1}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -2}]
"J" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -2}]
"W" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -3}]
"C" [{:factor 1 :oom 0 :unit "s" :expo 1}
{:factor 1 :oom 0 :unit "A" :expo 1}]
"V" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -3}
{:factor 1 :oom 0 :unit "A" :expo -1}]
"F" [{:factor 1 :oom 0 :unit "m" :expo -2}
{:factor 1 :oom 0 :unit "kg" :expo -1}
{:factor 1 :oom 0 :unit "s" :expo 4}
{:factor 1 :oom 0 :unit "A" :expo 2}]
"Ω" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -3}
{:factor 1 :oom 0 :unit "A" :expo -2}]
"S" [{:factor 1 :oom 0 :unit "m" :expo -2}
{:factor 1 :oom 0 :unit "kg" :expo -1}
{:factor 1 :oom 0 :unit "s" :expo 3}
{:factor 1 :oom 0 :unit "A" :expo 2}]
"Wb" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -2}
{:factor 1 :oom 0 :unit "A" :expo -2}]
"T" [{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -2}
{:factor 1 :oom 0 :unit "A" :expo -1}]
"H" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "kg" :expo 1}
{:factor 1 :oom 0 :unit "s" :expo -2}
{:factor 1 :oom 0 :unit "A" :expo -2}]
"lm" [{:factor 1 :oom 0 :unit "cd" :expo 1}]
"lx" [{:factor 1 :oom 0 :unit "m" :expo -2}
{:factor 1 :oom 0 :unit "cd" :expo 1}]
"Bq" [{:factor 1 :oom 0 :unit "s" :expo -1}]
"Gy" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "s" :expo -2}]
"Sv" [{:factor 1 :oom 0 :unit "m" :expo 2}
{:factor 1 :oom 0 :unit "s" :expo -2}]
"kat" [{:factor 1 :oom 0 :unit "s" :expo -1}
{:factor 1 :oom 0 :unit "mol" :expo 1}]
"h" [{:factor 3.6 :oom 3 :unit "s" :expo 1}]
"d" [{:factor 8.64 :oom 4 :unit "s" :expo 1}]
"l" [{:factor 1 :oom -1 :unit "m" :expo 3}]})
(def additional-unit-mappings {"B" [{:factor 1 :oom 1 :unit "dB" :expo 1}]
"°" [{:factor 1 :oom 0 :unit "°" :expo 1}]})
(def base-unit-set #{"m" "kg" "s" "A" "K" "mol" "cd" "dB" "°"})
(def unit-replace-map {"kg" {:unit "g" :prefix "k"}
"dB" {:unit "B" :prefix "d"}})
(def unit-replace-set (set (keys unit-replace-map)))
(def base-unit-map (reduce (fn [res-map base-unit] (assoc res-map (keyword base-unit) 0)) {} base-unit-set))
(def unit-mapping (merge si-unit-mapping additional-unit-mappings))
(def prefix-set (set (keys prefix-mapping)))
(def unit-set (set (keys unit-mapping)))
(defn not-empty-or-minus [number]
((every-pred #((complement empty?) %) #(not= "-" %)) number))
(defn get-number-part [{:keys [number order-of-magnitude]} with-oom target result]
(when (not-empty-or-minus number)
(let [parsed-number (edn/read-string (string/replace number "," "."))]
(if with-oom
(when (not-empty-or-minus order-of-magnitude)
(let [parsed-oom (edn/read-string order-of-magnitude)]
(assoc result target {:number parsed-number :oom parsed-oom})))
(assoc result target {:number parsed-number :oom 0})))))
(defn reduce-to-base-units [outer-multiplier result base-unit]
(let [multiplier (* outer-multiplier (:expo base-unit))
unit-key (keyword (:unit base-unit))
unit-factor (:factor base-unit)
unit-oom (:oom base-unit)]
(-> result
(update unit-key + multiplier)
(update :factor * (Math/pow unit-factor multiplier))
(update :oom + (* multiplier unit-oom)))))
(defn reduce-unit-mapping [result-map unit]
(let [parsed-expo (edn/read-string (:expo unit))
prefix-oom (get prefix-mapping (:prefix unit))
calculated-oom (* parsed-expo prefix-oom)]
(let [reduce-fn (fn [result base-unit]
((partial reduce-to-base-units parsed-expo) result base-unit))]
(reduce reduce-fn
(update result-map :oom + calculated-oom)
(get unit-mapping (:unit unit))))))
(defn reduce-units [units]
(let [result-map (merge base-unit-map {:factor 1 :oom 0})]
(reduce reduce-unit-mapping
result-map
units)))
(defn get-units-part [units with-units target result]
(if with-units
(let [reduced-units (reduce-units units)
base-units (select-keys reduced-units
(reduce (fn [res base-unit] (conj res (keyword base-unit))) [] base-unit-set))
units-factor (:factor reduced-units)
units-oom (:oom reduced-units)]
(update result target merge {:base-units base-units :units-factor units-factor :units-oom units-oom}))
result))
(defn compare-base-units [with-units result]
(if with-units
(let [calc-units (get-in result [:calc :base-units])
ans-units (get-in result [:ans :base-units])]
(when (= calc-units ans-units)
result))
[lernmeister.components.exercise-types.multistep-calculation.check
:refer
[check-phys-vals tolerable-error?]]
[lernmeister.components.helper :refer [migrate-if-necessary]]))
(defn get-calculation-ids [exercise]
(reduce (fn [res item] (conj res (:id item)))
#{} (get-in exercise [:core :calculations])))
(defn no-negative-points [result]
(if (neg? (:points result))
(assoc result :points 0)
result))
(defn compare-numbers [with-oom with-units error-factor result]
(let [get-combined-number (fn [oom-key]
(let [number (get-in result [oom-key :number])]
(if with-units
(let [units-factor (get-in result [oom-key :units-factor])]
(* number units-factor))
number)))
get-combined-oom (fn [oom-key]
(let [units-oom (get-in result [oom-key :units-oom] 0)]
(if with-oom
(let [oom (get-in result [oom-key :oom])]
(+ oom units-oom))
units-oom)))]
(let [calc-number (get-combined-number :calc)
ans-number (get-combined-number :ans)
calc-oom (get-combined-oom :calc)
ans-oom (get-combined-oom :ans)
oom-difference (- calc-oom ans-oom)
calc-multiplier (Math/pow 10 oom-difference)]
(when (<= (math-abs (- ans-number (* calc-multiplier calc-number)))
(math-abs (* error-factor ans-number)))
true))))
(defn check-answer [cur-calc cur-ans & {:keys [with-oom with-units]}]
(let [error-factor (/ (:error-relative cur-calc 1) 100)] ;;is it okay to keep this as ratio-datatype?
(some->> {}
(get-number-part cur-calc with-oom :calc)
(get-number-part cur-ans with-oom :ans)
(get-units-part (:units cur-calc) with-units :calc)
(get-units-part (:units cur-ans) with-units :ans)
(compare-base-units with-units)
(compare-numbers with-oom with-units error-factor))))
(defn check-calculation [id ans-obj ex-obj res-map with-oom with-units]
(let [phys-val-check-res (check-phys-vals ex-obj ans-obj :with-oom with-oom :with-units with-units)
is-correct? (tolerable-error? (:calculation-error phys-val-check-res)
(or (:error-relative ex-obj) 1))]
(assoc res-map id {:correct is-correct?
:points (if is-correct? (:correct-points ex-obj) (:incorrect-points ex-obj))
:points-max (:correct-points ex-obj)})))
(defn calculation-check [exercise answer callback]
(let [no-negative-points (fn [result]
(if (neg? (:points result))
(assoc result :points 0)
result))]
(let [core (:core exercise)
calculations (:calculations core)
with-oom (:with-oom core)
with-units (:with-units core)]
(callback)
(->>
(map
(fn [calculation]
(let [cur-correct-points (:correct-points calculation)
cur-incorrect-points (:incorrect-points calculation)
cur-id (:id calculation)
cur-answer (get answer cur-id)]
(if (and
cur-answer
(check-answer calculation cur-answer :with-oom with-oom :with-units with-units))
{:points cur-correct-points :points-max cur-correct-points :correct-id cur-id}
{:points cur-incorrect-points :points-max cur-correct-points})))
calculations)
(reduce
(fn [result cur-result]
(let [update-correct-calculations (fn [result]
(if (contains? cur-result :correct-id)
(update result :correct-calculations conj (:correct-id cur-result))
result))]
(->
result
(update :points + (:points cur-result))
(update :points-max + (:points-max cur-result))
update-correct-calculations)))
{:points 0 :points-max 0 :correct-calculations #{}})
no-negative-points))))
(let [answer-calculations (:calculations answer)
calculations (get-in exercise [:core :calculations])
with-oom (get-in exercise [:core :with-oom])
with-units (get-in exercise [:core :with-units])
res-map (reduce (fn [r-map calculation]
(let [id (:id calculation)
answer (get answer-calculations id)]
(check-calculation id answer calculation r-map with-oom with-units)))
{} calculations)
update-correct-calcs (fn [result id calculation-check]
(if (:correct calculation-check)
(update result :correct-calculations conj id)
result))]
(callback)
(->> res-map
(reduce-kv (fn [result k v] (-> result
(update :points + (:points v))
(update :points-max + (:points-max v))
(update-correct-calcs k v)))
{:points 0 :points-max 0 :correct-calculations #{}})
no-negative-points)))
(defmethod e-check/check-answer :calculation [exercise answer callback]
(calculation-check exercise answer callback))
(let [calculation-ids (get-calculation-ids exercise)
migrated-answer (migrate-if-necessary answer :answer-scheme "calculation"
answer-scheme-version (partial migrate-answer calculation-ids))]
(calculation-check exercise migrated-answer callback)))
......@@ -2,12 +2,14 @@
#?(:clj
(:require [lernmeister.components.exercise-types.calculation.spec :as spec]
[lernmeister.components.exercise-types.calculation.check :as check]
[lernmeister.components.exercise-types.calculation.prepare :as prepare])
[lernmeister.components.exercise-types.calculation.prepare :as prepare]
[lernmeister.components.exercise-types.calculation.build :as build])
:cljs
(:require [reagent.core :as reagent]
[lernmeister.components.content-elements.core :as content-manager]
[lernmeister.components.exercise-types.calculation.spec :as spec]
[lernmeister.components.exercise-types.calculation.prepare :as prepare]
[lernmeister.components.exercise-types.calculation.build :as build]
[lernmeister.components.exercise-types.calculation.check :as check]
[lernmeister.components.exercise-types.calculation.views.edit :as edit-view]
[lernmeister.components.exercise-types.calculation.views.show :as show-view])))
......@@ -17,7 +19,7 @@
:edit {:settings #?(:cljs edit-view/settings :clj nil)
:additional-forms #?(:cljs edit-view/additional-forms :clj nil)}
:prepare prepare/prepare
:build build/build
:renderer #?(:cljs show-view/exercise-renderer :clj nil)
:checker check/calculation-check
:spec ::spec/calculation-question})
(ns lernmeister.components.exercise-types.calculation.migrate
(:require [lernmeister.components.exercise-types.multistep-calculation.migrate
:refer
[convert-number-to-object
modify-exercise-field-vec
set-current-scheme]]))
(def exercise-scheme-version "2020-09-07")
(defn modify-calculations [exercise]
(partial modify-exercise-field-vec exercise [:core :calculations]))
(defn set-missing-error-relative [exercise]
((modify-calculations exercise) #(assoc % :error-relative (or (:error-relative %) 1))))
(defn convert-numbers [exercise]
((modify-calculations exercise) convert-number-to-object))
(defn normalize-units [exercise]
(if (get-in exercise [:core :with-units])
((modify-calculations exercise) #(assoc % :units (or (:units %) [])))
((modify-calculations exercise) #(dissoc % :units))))
(defn remove-unneeded-ooms [exercise]
(if-not (get-in exercise [:core :with-oom])
((modify-calculations exercise) #(dissoc % :order-of-magnitude))
exercise))
(defn remove-unneeded-error-keys [exercise]
((modify-calculations exercise) #(dissoc % :error)))
(defn migrate-exercise [exercise]
(->> exercise
remove-unneeded-error-keys
remove-unneeded-ooms
normalize-units
convert-numbers
set-missing-error-relative
(set-current-scheme "calculation" exercise-scheme-version)))
(ns lernmeister.components.exercise-types.calculation.migrate-answer
(:require [clojure.set :refer [difference]]
[lernmeister.components.exercise-types.multistep-calculation.migrate
:refer
[convert-number-to-object migrate-object]]))
(def answer-scheme-version "2020-09-07")
(defn set-current-scheme [answer]
(assoc answer :answer-scheme {:type "calculation" :version answer-scheme-version}))
(defn convert-calculation-numbers [answer]
(update answer :calculations (fn [calcs] (migrate-object convert-number-to-object calcs))))
(defn add-missing-calculation-objects [calculation-ids answer]
(let [calculations (:calculations answer)
missing-ids (difference calculation-ids (set (keys calculations)))]
(update answer :calculations (fn [calcs] (reduce (fn [res k] (assoc res k {:number nil}))
calcs missing-ids)))))
(defn wrap-answer-calculations [answer]
(if-not (contains? answer :calculations)
(let [wrapped-answer {}]
(assoc wrapped-answer :calculations answer))
answer))
(defn migrate-answer [calculation-ids answer]
(->> answer
wrap-answer-calculations
(add-missing-calculation-objects calculation-ids)
convert-calculation-numbers
set-current-scheme))
......@@ -2,8 +2,11 @@
(:require [lernmeister.components.exercise-types.prepare :as e-prepare]))
(defn select-exercise-keys [exercise]
(update-in exercise [:core]
#(select-keys % [:with-oom :with-units :type :task-description :calculations])))
(select-keys exercise [:title :id :type :core :exercise-scheme]))
(defn select-core-keys [exercise]
(update exercise :core
#(select-keys % [:with-oom :with-units :type :task-description :calculations :unit-list])))
(defn shuffle-calculations [exercise]
(if (get-in exercise [:core :shuffled])
......@@ -12,7 +15,7 @@
exercise))
(defn prep-calc [calculation]
(select-keys calculation [:id :name :number :order-of-magnitude :units]))
(select-keys calculation [:id :name]))
(defn prep-calcs [exercise]
(let [preped-calcs (into [] (map prep-calc (get-in exercise [:core :calculations])))]
......@@ -23,8 +26,8 @@
exercise
prep-calcs
shuffle-calculations
select-core-keys
select-exercise-keys))
(defmethod e-prepare/prepare :calculation [exercise & [options]]
(prepare exercise))
(defmethod e-prepare/prepare :calculation [exercise-build & _]
(prepare exercise-build))
(ns lernmeister.components.exercise-types.calculation.spec
(:require #?(:cljs [cljs.spec.alpha :as s]
:clj [clojure.spec.alpha :as s])
[lernmeister.components.exercise-types.calculation.check :refer [prefix-set unit-set]]))
[lernmeister.components.exercise-types.multistep-calculation.check :refer [prefix-set unit-set]]))
(s/def :calculation/name (s/and string? #(not (empty? %))))
(s/def :calculation/number (s/and string? #(re-matches #"^\-?((((0\,)|([1-9]\d*\,?))\d*)|0)$" %)))
......@@ -27,4 +27,3 @@
:exercise/task-description :exercise/shuffled]))
(s/def ::calculation-question (s/keys :req-un [:calculation-question/core]))
(ns lernmeister.components.exercise-types.multistep-calculation.build
(:require [lernmeister.components.exercise-types.multistep-calculation.check
:refer [mathjs-number-from-number-obj mathjs-str-to-number-obj mathjs-eval-and-format
number-obj]]
[lernmeister.components.exercise-types.build :as e-build]))
(:require [lernmeister.components.exercise-types.build :as e-build]
[lernmeister.components.exercise-types.multistep-calculation.check