Skip to content

Commit

Permalink
QB: Support importing of PathQuery XML
Browse files Browse the repository at this point in the history
  • Loading branch information
heralden committed Feb 6, 2020
1 parent c5d71c8 commit c984656
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 10 deletions.
1 change: 1 addition & 0 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
[cljsjs/google-analytics "2015.04.13-0"]
[day8.re-frame/test "0.1.5"]
[cljs-bean "1.4.0"]
[org.clojure/data.xml "0.2.0-alpha6"]

; Logging
[com.taoensso/timbre "4.10.0"]
Expand Down
3 changes: 2 additions & 1 deletion src/cljs/bluegenes/pages/querybuilder/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@
(comp enhance-constraint-logic read-logic-string))
(update :where #(or % []))
(dissoc :title :model)
;; Sterilizing *might* not be necessary.
;; Sterilizing *might* not be necessary since
;; it's coming straight from the InterMine.
(im-query/sterilize-query)))))
{} queries)))))
19 changes: 17 additions & 2 deletions src/cljs/bluegenes/pages/querybuilder/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
[imcljs.path :as im-path]
[imcljs.query :refer [->xml]]
[bluegenes.components.loader :refer [mini-loader loader]]
[bluegenes.components.ui.results_preview :refer [preview-table]]))
[bluegenes.components.ui.results_preview :refer [preview-table]]
[bluegenes.utils :refer [read-xml-query]]))

(defn one-of? [haystack needle] (some? (some #{needle} haystack)))

Expand Down Expand Up @@ -566,7 +567,21 @@
:active? (= @active-query query)
:any-renaming* any-renaming?]))]))))

(defn import-from-xml [])
(defn import-from-xml []
(let [query-input (reagent/atom "")]
(fn []
[:div
[:p "Paste your InterMine PathQuery XML here."]
[:textarea.form-control
{:rows 10
:autoFocus true
:value @query-input
:on-change #(reset! query-input (oget % :target :value))}]
[:button.btn.btn-raised
{:on-click #(when-let [query (not-empty @query-input)]
(reset! query-input "")
(dispatch [:qb/load-query (read-xml-query query)]))}
"Load query"]])))

(defn create-template [])

Expand Down
48 changes: 42 additions & 6 deletions src/cljs/bluegenes/utils.cljs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
(ns bluegenes.utils
(:require [clojure.string :refer [blank? join split capitalize split]]))
(:require [clojure.string :as string]
[clojure.data.xml :as xml]
[imcljs.query :as im-query]))

(defn uncamel
"Uncamel case a string. Example: thisIsAString -> This is a string"
[s]
(if-not (blank? s)
(if-not (string/blank? s)
(as-> s $
(split $ #"(?=[A-Z][^A-Z])")
(join " " $)
(capitalize $))
(string/split $ #"(?=[A-Z][^A-Z])")
(string/join " " $)
(string/capitalize $))
s))

(defn read-origin
"Read the origin class from a query, and infer it if it's missing."
[query]
(if-let [origin (:from query)]
origin
(first (split (first (:select query)) #"\."))))
(first (string/split (first (:select query)) #"\."))))

(defn kw->str
[kw]
Expand All @@ -26,3 +28,37 @@
(name kw))
(do (assert (string? kw) "This function takes only a keyword or string.")
kw)))

(defn read-xml-query
"Read an InterMine PathQuery in XML into an EDN Clojure map."
[xml-query]
(let [xml-map (xml/parse-str xml-query)
select (string/split (get-in xml-map [:attrs :view]) #" ")
from (second (re-find #"^(.*)\." (first select)))
constraintLogic (get-in xml-map [:attrs :constraintLogic])
orderBy (let [{:keys [sortOrder orderBy]} (:attrs xml-map)
pairs (partition 2 (string/split (or sortOrder orderBy) #" "))]
(mapv (fn [[path dir]]
{(keyword path) (string/upper-case dir)}) pairs))
joins (into []
(comp (filter (comp #{:join} :tag))
(filter (comp #{"OUTER"} :style :attrs))
(map (comp :path :attrs)))
(:content xml-map))
where (into []
(comp (filter (comp #{:constraint} :tag))
(map (fn [{:keys [attrs content]}]
(cond-> attrs
(not-empty content)
;; Handle ONE OF constraints.
(assoc :values
(->> content
(filter (comp #{:value} :tag))
(mapcat :content)))))))
(:content xml-map))]
{:from from
:select select
:orderBy orderBy
:constraintLogic constraintLogic
:joins joins
:where where}))
112 changes: 112 additions & 0 deletions test/cljs/bluegenes/utils_test.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
(ns bluegenes.utils-test
(:require [cljs.test :refer-macros [deftest is are testing]]
[bluegenes.utils :as utils]))

(deftest read-xml-query
(testing "Missing fields are correctly nulled"
(are [xml m] (= (utils/read-xml-query xml) m)
"<query name=\"\" model=\"genomic\" view=\"Gene.secondaryIdentifier Gene.symbol Gene.organism.name Gene.primaryIdentifier\" longDescription=\"\"></query>"
{:from "Gene",
:select
["Gene.secondaryIdentifier"
"Gene.symbol"
"Gene.organism.name"
"Gene.primaryIdentifier"],
:orderBy [],
:constraintLogic nil,
:joins [],
:where []}
"<query name=\"\" model=\"genomic\" view=\"Gene.secondaryIdentifier Gene.symbol Gene.organism.name Gene.primaryIdentifier\" longDescription=\"\" sortOrder=\"Gene.symbol desc\"></query>"
{:from "Gene",
:select
["Gene.secondaryIdentifier"
"Gene.symbol"
"Gene.organism.name"
"Gene.primaryIdentifier"],
:orderBy [{:Gene.symbol "DESC"}],
:constraintLogic nil,
:joins [],
:where []}
"<query name=\"\" model=\"genomic\" view=\"Gene.secondaryIdentifier Gene.symbol Gene.organism.name Gene.primaryIdentifier\" longDescription=\"\" sortOrder=\"Gene.symbol desc\">
<constraint path=\"Gene.symbol\" code=\"A\" op=\"CONTAINS\" value=\"ab\"/>
<constraint path=\"Gene\" code=\"B\" op=\"IN\" value=\"PL FlyAtlas_brain_top\"/>
</query>"
{:from "Gene",
:select
["Gene.secondaryIdentifier"
"Gene.symbol"
"Gene.organism.name"
"Gene.primaryIdentifier"],
:orderBy [{:Gene.symbol "DESC"}],
:constraintLogic nil,
:joins [],
:where
[{:path "Gene.symbol", :code "A", :op "CONTAINS", :value "ab"}
{:path "Gene", :code "B", :op "IN", :value "PL FlyAtlas_brain_top"}]}
"<query name=\"\" model=\"genomic\" view=\"Gene.secondaryIdentifier Gene.symbol Gene.organism.name Gene.primaryIdentifier\" longDescription=\"\" sortOrder=\"Gene.symbol desc\" constraintLogic=\"A or B\">
<constraint path=\"Gene.symbol\" code=\"A\" op=\"CONTAINS\" value=\"ab\"/>
<constraint path=\"Gene\" code=\"B\" op=\"IN\" value=\"PL FlyAtlas_brain_top\"/>
</query>"
{:from "Gene",
:select
["Gene.secondaryIdentifier"
"Gene.symbol"
"Gene.organism.name"
"Gene.primaryIdentifier"],
:orderBy [{:Gene.symbol "DESC"}],
:constraintLogic "A or B",
:joins [],
:where
[{:path "Gene.symbol", :code "A", :op "CONTAINS", :value "ab"}
{:path "Gene", :code "B", :op "IN", :value "PL FlyAtlas_brain_top"}]}))
(testing "Can handle ONE OF constraints"
(is (= (utils/read-xml-query "<query model=\"genomic\" view=\"Gene.primaryIdentifier Gene.secondaryIdentifier Gene.symbol Gene.name Gene.length Gene.organism.shortName\" constraintLogic=\"(A and B)\">)))
<constraint path=\"Gene.symbol\" op=\"ONE OF\" code=\"B\"><value>CDPK1</value><value>CK1</value><value>ENO</value><value>CDPK4</value><value>ABRA</value><value>ERD2</value><value>CRK2</value></constraint>))
</query>")
{:from "Gene"
:select
["Gene.primaryIdentifier"
"Gene.secondaryIdentifier"
"Gene.symbol"
"Gene.name"
"Gene.length"
"Gene.organism.shortName"],
:orderBy []
:constraintLogic "(A and B)",
:joins [],
:where
[{:path "Gene.symbol",
:values '("CDPK1" "CK1" "ENO" "CDPK4" "ABRA" "ERD2" "CRK2"),
:op "ONE OF",
:code "B"}]})))
(testing "Can handle OUTER JOIN"
(is (= (utils/read-xml-query "<query model=\"genomic\" view=\"Gene.symbol Gene.pathways.identifier\">))))
<join path=\"Gene.pathways\" style=\"OUTER\"/>)
</query>")
{:from "Gene",
:select ["Gene.symbol" "Gene.pathways.identifier"],
:orderBy [],
:constraintLogic nil,
:joins ["Gene.pathways"],
:where []})))
(testing "Can handle OUTER JOIN mixed with constraints"
(is (= (utils/read-xml-query "<query name=\"\" model=\"genomic\" view=\"Gene.secondaryIdentifier Gene.symbol Gene.organism.name Gene.primaryIdentifier Gene.proteins.primaryIdentifier Gene.proteins.primaryAccession Gene.proteins.organism.name\" longDescription=\"\" sortOrder=\"Gene.symbol desc\" constraintLogic=\"A and B\">))))
<constraint path=\"Gene.symbol\" code=\"A\" op=\"CONTAINS\" value=\"ab\"/>
<join path=\"Gene.proteins\" style=\"OUTER\"/>
<constraint path=\"Gene\" code=\"B\" op=\"IN\" value=\"PL FlyAtlas_brain_top\"/>)
</query>")
{:from "Gene",
:select
["Gene.secondaryIdentifier"
"Gene.symbol"
"Gene.organism.name"
"Gene.primaryIdentifier"
"Gene.proteins.primaryIdentifier"
"Gene.proteins.primaryAccession"
"Gene.proteins.organism.name"],
:orderBy [{:Gene.symbol "DESC"}],
:constraintLogic "A and B",
:joins ["Gene.proteins"],
:where
[{:path "Gene.symbol", :code "A", :op "CONTAINS", :value "ab"}
{:path "Gene", :code "B", :op "IN", :value "PL FlyAtlas_brain_top"}]}))))
2 changes: 1 addition & 1 deletion tests.edn
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#kaocha/v1
{:tests [{:id :unit-cljs
:type :kaocha.type/cljs
:cljs/repl-env cljs.repl.node/repl-env
:cljs/repl-env cljs.repl.browser/repl-env
:cljs/timeout 60000}]}

0 comments on commit c984656

Please sign in to comment.