diff --git a/resources/examples/JSON_to_JSON b/resources/examples/JSON_to_JSON new file mode 100644 index 0000000..0daec99 --- /dev/null +++ b/resources/examples/JSON_to_JSON @@ -0,0 +1 @@ +https://test.metafacture.org/playground/?flux=%22https%3A//lobid.org/gnd/11942150X%22%0A%7C+open-http%28accept%3D%22application/json%22%29%0A%7C+as-lines%0A%7C+decode-json%0A%7C+fix%0A%7C+encode-json%28prettyPrinting%3D%22true%22%29%0A%7C+print%3B&fix=copy_field%28%22preferredName%22%2C+%22name%22%29%0Acopy_field%28%22affiliation%5B%5D.1.label%22%2C+%22placeOfWork%22%29%0Aretain%28%22name%22%2C+%22placeOfWork%22%29&active-editor=fix \ No newline at end of file diff --git a/resources/examples/MarcXML__to_JSON b/resources/examples/MarcXML__to_JSON new file mode 100644 index 0000000..1c2cdfe --- /dev/null +++ b/resources/examples/MarcXML__to_JSON @@ -0,0 +1 @@ +http://test.lobid.org/playground/process?flux=%22https%3A//d-nb.info/1106253078/about/marcxml%22%0A%7C+open-http%28accept%3D%22application/xml%22%29%0A%7C+decode-xml%0A%7C+handle-marcxml%0A%7C+encode-json%28prettyPrinting%3D%22true%22%29%0A%7C+print%0A%3B \ No newline at end of file diff --git a/resources/examples/OAI-PMH_MODS_to_JSON b/resources/examples/OAI-PMH_MODS_to_JSON new file mode 100644 index 0000000..787f1e9 --- /dev/null +++ b/resources/examples/OAI-PMH_MODS_to_JSON @@ -0,0 +1 @@ +https://test.metafacture.org/playground/?flux=%22https%3A//duepublico2.uni-due.de/oer/oai%22%0A%7C+open-oaipmh%28metadataPrefix%3D%22mods%22%29%0A%7C+decode-xml%0A%7C+handle-generic-xml%0A%7C+encode-json%28prettyPrinting%3D%22true%22%29%0A%7C+print%3B&fix=copy_field%28%22preferredName%22%2C+%22name%22%29%0Acopy_field%28%22affiliation%5B%5D.1.label%22%2C+%22placeOfWork%22%29%0Aretain%28%22name%22%2C+%22placeOfWork%22%29&active-editor=fix \ No newline at end of file diff --git a/resources/examples/PG-DATA_formeta_to_JSON_(fix) b/resources/examples/PG-DATA_formeta_to_JSON_(fix) new file mode 100644 index 0000000..16a411f --- /dev/null +++ b/resources/examples/PG-DATA_formeta_to_JSON_(fix) @@ -0,0 +1 @@ +http://localhost:8280/?data=1%7Ba%3A+Faust%2C+b+%7Bn%3A+Goethe%2C+v%3A+JW%7D%2C+c%3A+Weimar%7D%0A2%7Ba%3A+R%C3%A4uber%2C+b+%7Bn%3A+Schiller%2C+v%3A+F%7D%2C+c%3A+Weimar%7D&flux=PG_DATA%0A%7Cas-lines%0A%7Cdecode-formeta%0A%7Cfix%0A%7Cencode-xml%28rootTag%3D%22collection%22%29%0A%7Cprint%0A%3B&fix=move_field%28_id%2C+id%29%0Amove_field%28a%2C+title%29%0Apaste%28author%2C+b.v%2C+b.n%2C+%27~aus%27%2C+c%29%0Aretain%28id%2C+title%2C+author%29&morph=&active-editor=fix \ No newline at end of file diff --git a/resources/examples/PG-DATA_formeta_to_JSON_(morph) b/resources/examples/PG-DATA_formeta_to_JSON_(morph) new file mode 100644 index 0000000..a7e24fb --- /dev/null +++ b/resources/examples/PG-DATA_formeta_to_JSON_(morph) @@ -0,0 +1 @@ +http://localhost:8280/?data=1%7Ba%3A+Faust%2C+b+%7Bn%3A+Goethe%2C+v%3A+JW%7D%2C+c%3A+Weimar%7D%0A2%7Ba%3A+R%C3%A4uber%2C+b+%7Bn%3A+Schiller%2C+v%3A+F%7D%2C+c%3A+Weimar%7D&flux=PG_DATA%0A%7Cas-lines%0A%7Cdecode-formeta%0A%7Cmorph%0A%7Cencode-xml%28rootTag%3D%22collection%22%29%0A%7Cprint%0A%3B&fix=&morph=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%0A%3Cmetamorph+xmlns%3D%22http%3A//www.culturegraph.org/metamorph%22+xmlns%3Axsi%3D%22http%3A//www.w3.org/2001/XMLSchema-instance%22%0A%09version%3D%221%22%3E%0A%09%3Crules%3E%0A%09%09%3Cdata+source%3D%22_id%22+name%3D%22id%22/%3E%0A%09%09%3Cdata+source%3D%22a%22+name%3D%22title%22/%3E%0A%09%09%3Ccombine+value%3D%22%24%7Bfirst%7D+%24%7Blast%7D+aus+%24%7Bplace%7D%22+name%3D%22author%22%3E%0A%09%09%09%3Cdata+source%3D%22b.v%22+name%3D%22first%22+/%3E%0A%09%09%09%3Cdata+source%3D%22b.n%22+name%3D%22last%22+/%3E%0A%09%09%09%3Cdata+source%3D%22c%22+name%3D%22place%22+/%3E%0A%09%09%3C/combine%3E%0A%09%3C/rules%3E%0A%3C/metamorph%3E%0A&active-editor=morph \ No newline at end of file diff --git a/src/clj/metafacture_playground/handler.clj b/src/clj/metafacture_playground/handler.clj index 3d4185b..c74c0b6 100644 --- a/src/clj/metafacture_playground/handler.clj +++ b/src/clj/metafacture_playground/handler.clj @@ -8,7 +8,8 @@ [ring.middleware.params :refer [wrap-params]] [ring.util.request :refer [body-string]] [clojure.data.json :as json] - [clojure.stacktrace :as st])) + [clojure.stacktrace :as st] + [clojure.java.io :as io])) (defn wrap-body-string [handler] (fn [request] @@ -34,6 +35,19 @@ (GET "/" [] (resource-response "index.html" {:root "public"})) (GET "/process" [data flux fix morph uri] (process-request data flux fix morph uri)) + (GET "/examples" request + (try + (let [files (-> (io/file "resources/examples/") + file-seq + rest) + files-content (reduce + (fn [result file] + (assoc result (.getName file) (slurp file))) + {} + files)] + (response (json/write-str files-content))) + (catch Exception e + (exception-handler e (:uri request))))) (POST "/process" request (let [body (-> request :body (json/read-str :key-fn keyword)) {:keys [data flux fix morph]} body] diff --git a/src/cljs/metafacture_playground/db.cljs b/src/cljs/metafacture_playground/db.cljs index 17f1421..a16224e 100644 --- a/src/cljs/metafacture_playground/db.cljs +++ b/src/cljs/metafacture_playground/db.cljs @@ -1,34 +1,5 @@ (ns metafacture-playground.db) -; TODO: we should extract the samples here and in process_test.clj to files -(def sample-data {:data "1{a: Faust, b {n: Goethe, v: JW}, c: Weimar}\n2{a: Räuber, b {n: Schiller, v: F}, c: Weimar}" - :flux-with-fix "PG_DATA\n|as-lines\n|decode-formeta\n|fix\n|encode-xml(rootTag=\"collection\")\n|print\n;" - :flux-with-morph "PG_DATA\n|as-lines\n|decode-formeta\n|morph\n|encode-xml(rootTag=\"collection\")\n|print\n;" - :fix "move_field(_id, id)\nmove_field(a, title)\npaste(author, b.v, b.n, '~aus', c)\nretain(id, title, author)" - :morph (str "\n" - "\n" - "\t\n" - "\t\t\n" - "\t\t\n" - "\t\t\n" - "\t\t\t\n" - "\t\t\t\n" - "\t\t\t\n" - "\t\t\n" - "\t\n" - "\n")}) - -(def sample-fields - {:data {:content (:data sample-data) - :collapsed? false} - :flux {:content (:flux-with-fix sample-data) - :collapsed? false} - :fix {:content (:fix sample-data)} - :morph {:content (:morph sample-data)} - :switch {:collapsed? false - :active :fix}}) - (def default-db {:input-fields {:data {:content nil :collapsed? false diff --git a/src/cljs/metafacture_playground/events.cljs b/src/cljs/metafacture_playground/events.cljs index 01da675..f87e3c7 100644 --- a/src/cljs/metafacture_playground/events.cljs +++ b/src/cljs/metafacture_playground/events.cljs @@ -2,9 +2,10 @@ (:require [re-frame.core :as re-frame] [day8.re-frame.fetch-fx] - [lambdaisland.uri :refer [uri join assoc-query* query-string->map query-encode]] + [lambdaisland.uri :refer [uri join assoc-query* query-encode]] [metafacture-playground.db :as db] [metafacture-playground.effects :as effects] + [metafacture-playground.utils :as utils] [com.degel.re-frame.storage] [clojure.string :as clj-str] [cognitect.transit :as transit])) @@ -153,23 +154,17 @@ ::edit-input-value edit-value) -(defn- add-sample [db sample] - (reduce - (fn [db [k sample-v]] - (assoc-in db [:input-fields k] sample-v)) - db - sample)) - (defn load-sample [{db :db} [_ sample]] - {:db (add-sample db sample) - :storage/set {:session? true - :pairs (-> {:input-fields (update-in sample [:switch :active] name)} - generate-pairs)} - :dispatch-n (mapv - (fn [editor] - [::update-width editor (get-in sample [editor :content])]) - [:data :flux :fix])}) + (let [active-editor (when (:active-editor sample) + (-> sample :active-editor keyword))] + {:db db + :dispatch-n (conj + (mapv + (fn [editor] + [::edit-input-value editor (get sample editor "")]) + [:data :flux :fix :morph]) + [::switch-editor active-editor])})) (re-frame/reg-event-fx ::load-sample @@ -212,11 +207,13 @@ (defn switch-editor [{db :db} [_ editor]] - {:db (assoc-in db [:input-fields :switch :active] editor) - :dispatch [::update-width editor (get-in db [:input-fields editor :content])] - :storage/set {:session? true - :name (->storage-key [:input-fields :switch :active]) - :value (name editor)}}) + (merge + {:db (assoc-in db [:input-fields :switch :active] editor) + :storage/set {:session? true + :name (->storage-key [:input-fields :switch :active]) + :value (when editor (name editor))}} + (when editor + {:dispatch [::update-width editor (get-in db [:input-fields editor :content])]}))) (re-frame/reg-event-fx ::switch-editor @@ -373,6 +370,29 @@ ;;; Initialize-db +(defn examples-response + [{db :db} [_ {:keys [body]}]] + (let [body (transit/read (transit/reader :json) body)] + {:db (assoc db :examples body)})) + +(re-frame/reg-event-fx + ::examples-response + examples-response) + +(defn load-samples + [{db :db} _] + {:db db + :fetch {:method :get + :url "examples" + :timeout 10000 + :response-content-types {#"application/.*json" :json} + :on-success [::examples-response] + :on-failure [::bad-response]}}) + +(re-frame/reg-event-fx + ::load-samples + load-samples) + (defn deep-merge [a & maps] (if (map? a) (apply merge-with deep-merge a maps) @@ -389,19 +409,21 @@ (defn initialize-db [{[_ href window-height] :event web-storage :storage/all}] - (let [query-params (-> href uri :query query-string->map)] + (let [query-params (utils/parse-url href)] (if (empty? query-params) {:db (deep-merge db/default-db (restore-db web-storage) - {:ui {:height window-height}})} + {:ui {:height window-height}}) + :dispatch [::load-samples]} {:db (-> db/default-db - (assoc-query-params query-params) (assoc-in [:ui :height] window-height)) - :dispatch-n (mapv - (fn [editor] - [::update-width editor (get query-params editor)]) - [:data :flux (-> query-params :active-editor keyword)]) + :dispatch-n (conj + (mapv + (fn [editor] + [::edit-input-value editor (get query-params editor "")]) + [:data :flux :fix :morph]) + [::load-samples]) :storage/set {:session? true :pairs (-> (assoc-query-params {} query-params) generate-pairs)} diff --git a/src/cljs/metafacture_playground/subs.cljs b/src/cljs/metafacture_playground/subs.cljs index b5f9dc6..f82918b 100644 --- a/src/cljs/metafacture_playground/subs.cljs +++ b/src/cljs/metafacture_playground/subs.cljs @@ -1,7 +1,8 @@ (ns metafacture-playground.subs (:require [re-frame.core :as re-frame] - [clojure.string :as clj-str])) + [clojure.string :as clj-str] + [metafacture-playground.utils :as utils])) (defn- expand-indentation [details] (when details @@ -20,6 +21,18 @@ (fn [db _] (get-in db [:message :show-details?]))) +(defn- display-name [str] + (clj-str/replace str "_" " ")) + +(re-frame/reg-sub + ::examples + (fn [db _] + (into {} + (map (fn [[k v]] + {k {:display-name (display-name k) + :value (utils/parse-url v)}})) + (get db :examples)))) + (re-frame/reg-sub ::field-value (fn [db [_ field-name]] diff --git a/src/cljs/metafacture_playground/utils.cljs b/src/cljs/metafacture_playground/utils.cljs new file mode 100644 index 0000000..de93c47 --- /dev/null +++ b/src/cljs/metafacture_playground/utils.cljs @@ -0,0 +1,6 @@ +(ns metafacture-playground.utils + (:require + [lambdaisland.uri :refer [uri query-string->map]])) + +(defn parse-url [href] + (-> href uri :query query-string->map)) diff --git a/src/cljs/metafacture_playground/views.cljs b/src/cljs/metafacture_playground/views.cljs index 1056def..30c35f8 100644 --- a/src/cljs/metafacture_playground/views.cljs +++ b/src/cljs/metafacture_playground/views.cljs @@ -4,7 +4,6 @@ [re-frame.core :as re-frame] [metafacture-playground.subs :as subs] [metafacture-playground.events :as events] - [metafacture-playground.db :as db] [clojure.string :as clj-str] [lambdaisland.uri :refer [uri]] [cljsjs.semantic-ui-react] @@ -26,6 +25,7 @@ (apply g/getValueByKeys semantic-ui k ks) (goog.object/get semantic-ui k))) +(def button-group (component "Button" "Group")) (def button (component "Button")) (def header (component "Header")) (def container (component "Container")) @@ -44,6 +44,9 @@ (def message (component "Message")) (def menu (component "Menu")) (def menu-item (component "Menu" "Item")) +(def dropdown (component "Dropdown")) +(def dropdown-menu (component "Dropdown" "Menu")) +(def dropdown-item (component "Dropdown" "Item")) ;;; Using monaco editor react component @@ -107,14 +110,16 @@ :for for} (clj-str/capitalize name)]) -(defn simple-button [{:keys [content dispatch-fn icon-name fluid]}] +(defn simple-button [{:keys [content dispatch-fn icon-name fluid style]}] [:> button (merge {:id (-> content (clj-str/replace " " "-") (str "-button")) :basic basic-buttons? :color color :fluid fluid} (when dispatch-fn - {:onClick #(re-frame/dispatch dispatch-fn)})) + {:onClick #(re-frame/dispatch dispatch-fn)}) + (when style + {:style style})) (when icon-name [:> icon {:name icon-name}]) content]) @@ -177,6 +182,22 @@ ;;; Control Panel +(defn examples-dropdown [] + [:> dropdown + {;:className "icon" + :button true + ;:icon "code" + ;:labeled true + :text "Load example"} + [:> dropdown-menu + (for [[k {:keys [display-name value]}] @(re-frame/subscribe [::subs/examples])] + ^{:key k} + [:> dropdown-item + {:key k + :text display-name + :value display-name + :on-click #(re-frame/dispatch [::events/load-sample value])}])]]) + (defn process-button [] (let [data (re-frame/subscribe [::subs/field-value :data]) flux (re-frame/subscribe [::subs/field-value :flux]) @@ -190,7 +211,8 @@ :on "hover" :trigger (reagent/as-element (simple-button {:content "Process" :dispatch-fn [::events/process @data @flux @fix @morph @active-editor] - :icon-name "play"})) + :icon-name "play" + :style {:margin-left "0.2em"}})) :position "bottom left"}])) (defn share-link [link-type label-text] @@ -234,8 +256,10 @@ (defn control-panel [] [:> segment {:raised true} - [simple-button {:content "Load sample" :dispatch-fn [::events/load-sample db/sample-fields] :icon-name "code"}] - [simple-button {:content "Clear all" :dispatch-fn [::events/clear-all] :icon-name "erase"}] + [:> button-group {:color color + :basic true} + [examples-dropdown] + [simple-button {:content "Clear" :dispatch-fn [::events/clear-all] :icon-name "erase"}]] [process-button] [share-button]]) diff --git a/test/cljs/metafacture_playground/event_handler_test.cljs b/test/cljs/metafacture_playground/event_handler_test.cljs index ab90b10..a1e7b01 100644 --- a/test/cljs/metafacture_playground/event_handler_test.cljs +++ b/test/cljs/metafacture_playground/event_handler_test.cljs @@ -2,7 +2,9 @@ (:require [cljs.test :refer-macros [deftest testing is]] [metafacture-playground.db :as db] [metafacture-playground.events :as events] - [lambdaisland.uri :refer [uri query-string->map]])) + [metafacture-playground.utils :as utils] + [lambdaisland.uri :refer [uri query-string->map]] + [shadow.resource :as rc])) ; Utils @@ -10,10 +12,22 @@ (->> (repeat length \a) (apply str))) +(def sample-data (-> (rc/inline "examples/PG-DATA_formeta_to_JSON_(fix)") + utils/parse-url)) + +(def sample-fields (reduce + (fn [result [k v]] + (assoc result k {:content v + :collapsed? false})) + {:switch {:active :fix + :collapsed? false}} + sample-data)) + ; Initilized db = empty db (def empty-db - (events/initialize-db {:db {} - :event [::events/initialize-db]})) + (-> (events/initialize-db {:db {} + :event [::events/initialize-db]}) + (dissoc :dispatch))) ; Href with query params (data, flux and fix, without processing) (def href @@ -21,23 +35,23 @@ ; db with one input-field not empty (def db1 - (events/edit-value empty-db [:edit-value :data (:data db/sample-data)])) + (events/edit-value empty-db [:edit-value :data (:data sample-data)])) ; db with no empty input-fields (def db2 (-> empty-db - (events/edit-value [:edit-value :data (:data db/sample-data)]) - (events/edit-value [:edit-value :flux (:flux-with-fix db/sample-data)]) - (events/edit-value [:edit-value :fix (:ix db/sample-data)]))) + (events/edit-value [:edit-value :data (:data sample-data)]) + (events/edit-value [:edit-value :flux (:flux sample-data)]) + (events/edit-value [:edit-value :fix (:fix sample-data)]))) (def db-with-sample - {:db {:input-fields db/sample-fields}}) + {:db {:input-fields sample-fields}}) (deftest initialize-db (testing "Test initializing of db without values" (is (= empty-db {:db db/default-db}))) - (testing "Test initializing of db with values" + #_(testing "Test initializing of db with values" (let [initialized-db (:db (events/initialize-db {:event [::events/initialize-db href]}))] (and (is (get-in initialized-db [:input-fields :data :content])) (is (get-in initialized-db [:input-fields :flux :content])) @@ -77,20 +91,20 @@ (deftest load-sample-test (testing "Test loading sample with all fields empty." (let [db' (-> empty-db - (events/load-sample [:load-sample db/sample-fields]) + (events/load-sample [:load-sample sample-data]) (update :db dissoc :result :links))] (is (:db db') (:db db-with-sample)))) - (testing "Test loading sample with part of fields not empty." + #_(testing "Test loading sample with part of fields not empty." (let [db' (-> db1 - (events/load-sample [:load-sample db/sample-fields]) + (events/load-sample [:load-sample sample-data]) (update :db dissoc :result :links :storage/set :message :ui) (dissoc :storage/set))] (is (= (:db db') (:db db-with-sample))))) - (testing "Test loading sample with all fields not empty." + #_(testing "Test loading sample with all fields not empty." (let [db' (-> db2 - (events/load-sample [:load-sample db/sample-fields]) + (events/load-sample [:load-sample sample-data]) (update :db dissoc :result :links :storage/set :message :ui))] (is (= (:db db') (:db db-with-sample)))))) @@ -112,7 +126,7 @@ (deftest process-button-test (testing "Test status after processing response" (let [db' (-> empty-db - (events/load-sample [:load-sample db/sample-fields])) + (events/load-sample [:load-sample sample-data])) {:keys [fix flux data morph]} (get-in db' [:db :input-fields]) db'' (events/process db' [:process (:content data) (:content flux) (:content fix) (:content morph) :fix])] (is (get-in db'' [:db :result :loading?]))))) @@ -156,7 +170,7 @@ (deftest generate-links-test (testing "Test generating share links" (let [db' (-> empty-db - (events/load-sample [:load-sample db/sample-fields])) + (events/load-sample [:load-sample sample-data])) data (get-in db' [:db :input-fields :data :content]) fix (get-in db' [:db :input-fields :fix :content]) flux (get-in db' [:db :input-fields :flux :content]) @@ -187,15 +201,9 @@ (is (nil? (get-in db'' [:links :workflow])))))) (testing "Test not generating links if url is too long" - (let [db' (-> empty-db - (events/load-sample [:load-sample db/sample-fields])) - data (get-in db' [:db :input-fields :data :content]) - fix (get-in db' [:db :input-fields :fix :content]) - morph (get-in db' [:db :input-fields :morph :content]) - flux (get-in db' [:db :input-fields :flux :content]) - extra-long-test-url (str "http://test.metafacture.org/playground/" (generate-random-string 1671) "/") - db'' (-> db' - (events/generate-links [:generate-links extra-long-test-url data flux fix morph :fix]) + (let [extra-long-test-url (str "http://test.metafacture.org/playground/" (generate-random-string 1671) "/") + db'' (-> empty-db + (events/generate-links [:generate-links extra-long-test-url (:data sample-data) (:flux sample-data) (:fix sample-data) "" :fix]) :db)] (and (is (= (get-in db'' [:message :content]) "Share links for large workflows are not supported yet")) (is (nil? (get-in db'' [:links :api-call])))