Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support subclasses #568

Merged
merged 24 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8d4c82a
Compute model subclass hierarchy to app-db and use in data-browser
heralden Aug 19, 2020
093ac8a
QB: Fix constraining to a subclass
heralden Aug 19, 2020
fa84adf
QB: Remove uncamel to fix incorrect class names
heralden Aug 19, 2020
d3a376d
QB: Handle and display errors when fetching preview
heralden Aug 19, 2020
1beae43
QB: Make subclass constraining much more robust
heralden Aug 20, 2020
a61dcf8
QB: Fix alphabetical order in Model Browser and Query Editor
heralden Aug 21, 2020
9425c40
QB: Sort and add separator to subclass dropdown
heralden Aug 21, 2020
2f8b3d8
Support subclasses by passing type-constraints to imcljs
heralden Aug 26, 2020
0d67b03
QB: Parse type-constraints from qb:menu for model-browser
heralden Aug 26, 2020
d6affdd
Do not remove empty classes from model
heralden Aug 26, 2020
4eaf4fc
QB: Use displayName instead of model class in Query Editor
heralden Sep 29, 2020
cf91723
QB: Fix subclass constraint not getting added for descendant attribute
heralden Sep 29, 2020
971e7b9
QB: Make subclass constraints even more robust
heralden Sep 29, 2020
3c30706
QB: Fix various bugs related to subclasses
heralden Oct 1, 2020
52e88ce
QB: Improve usability of model browser button group
heralden Oct 2, 2020
9cb3a96
Fix viz initialisation failing for queries with sortOrder
heralden Oct 5, 2020
e910fb9
Make tools active for subclasses of a supported class
heralden Oct 6, 2020
b95b76d
Autoformat code
heralden Oct 6, 2020
960134a
Templates: Fix fetching possible values with type constraint
heralden Oct 6, 2020
6ea57ea
Re-enable templates-for-entity in report page
heralden Oct 6, 2020
501ed6a
QB: Align model browser Summary button
heralden Oct 6, 2020
d6f0cfe
Update Tool API docs with info on subclasses
heralden Oct 6, 2020
e020907
Remove invalid suitable-entities test
heralden Oct 7, 2020
a9f3326
Try to fix weird unit test error
heralden Oct 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/tools/tool-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ If your tool *accepts* `ids` and takes multiple *classes*, (see [config.json](/d
}
```

Subclasses (descendant of a class in the model hierarchy) might also be passed to your tool if it's descendant of one of your tool's *classes*. When this happens, the key will still be its superclass which you specified in *classes*, while the subclass name can be accessed under `class`. If you want your tool to work with subclasses, you'll need to make sure that any queries you build based on imEntity sets the `from` key to this `class` (`imEntity.Gene.class` in this example).

```json
{
"Gene": {
"class": "ORF",
"format": "id",
"value": 5
}
}
```

It is up to you which class you want to use in your tool, and you can even use multiple.

Currently, it is not possible to receive multiple classes on the report page with *accepts* `id`. However, the Tool API allows for this should it be an option in the future.
Expand Down Expand Up @@ -146,7 +158,7 @@ This file provides bluegenes-specific config info. Some further config info is d

Plurality (i.e. id vs ids) will help to determine which context a tool can appear (report page, list analysis page)

**classes** default to `*` if this tool isn't class / objectType specific. otherwise your tool might be specific to a certain class, e.g. a gene displayer.
**classes** default to `*` if this tool isn't class / objectType specific. Otherwise your tool might be specific to a certain class, e.g. a gene displayer. Note that a subclass of a class you specify here may be passed via *imEntity* (see its section above for more details).

**columnMapping** is an important way to specify (or override) which columns should be passed to the tool by BlueGenes. As an example, for a gene tool, you might want to pass a symbol, OR a primaryIdentifier, or even secondaryIdentifier - and this might change depending in the InterMine that is fuelling the BlueGenes. Set a default likely value here, and in the future individual bluegenes administrators can override it if needed.

Expand Down
21 changes: 19 additions & 2 deletions less/components/querybuilder.less
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,11 @@ div.column-container {
font-weight: 500;
color: @query-browser-class;
white-space: nowrap;

.label {
padding: @spacer*0.1 @spacer*0.6;
margin-left: @spacer/2;
}
}

.qb-type {
Expand Down Expand Up @@ -591,6 +596,10 @@ div.column-container {
.qb-class {
font-weight: 500;
color: @model-browser-class;

&.empty-class {
color: @body-foreground-color;
}
}

.light {
Expand Down Expand Up @@ -643,8 +652,16 @@ div.column-container {
z-index: 11;
}

.model-button-group {
display: flex;
.model-button-container {
padding-left: 0.6em;

.model-button-group {
display: flex;

.icon {
fill: @body-foreground-color;
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@

; Intermine Assets
[org.intermine/im-tables "0.10.1"]
[org.intermine/imcljs "1.1.0"]
[org.intermine/imcljs "1.2.0"]
[org.intermine/bluegenes-tool-store "0.2.0"]]

:deploy-repositories {"clojars" {:sign-releases false}}
Expand Down
17 changes: 14 additions & 3 deletions src/cljs/bluegenes/components/icons.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@

[:symbol#icon-summary
{:viewBox "0 0 16 16"}
[:title "summary"]
[:path.path1
{:d
"M13.5 0h-12c-0.825 0-1.5 0.675-1.5 1.5v13c0 0.825 0.675 1.5 1.5 1.5h12c0.825 0 1.5-0.675 1.5-1.5v-13c0-0.825-0.675-1.5-1.5-1.5zM13 14h-11v-12h11v12zM4 7h7v1h-7zM4 9h7v1h-7zM4 11h7v1h-7zM4 5h7v1h-7z"}]]
Expand Down Expand Up @@ -539,7 +538,6 @@

[:symbol#icon-bin
{:viewBox "0 0 16 16"}
[:title "bin"]
[:path
{:d
"M2 5v10c0 0.55 0.45 1 1 1h9c0.55 0 1-0.45 1-1v-10h-11zM5 14h-1v-7h1v7zM7 14h-1v-7h1v7zM9 14h-1v-7h1v7zM11 14h-1v-7h1v7z"}]
Expand Down Expand Up @@ -804,4 +802,17 @@
{:d
"M77.7675 47.9216H94.1618C94.3911 47.9216 94.6182 47.9677 94.83 48.0571C95.0419 48.1465 95.2344 48.2776 95.3965 48.4429C95.5587 48.6082 95.6873 48.8044 95.7751 49.0203C95.8628 49.2363 95.908 49.4677 95.908 49.7014V50.4089C95.908 50.8843 95.7227 51.3404 95.3928 51.6766C95.0629 52.0128 94.6155 52.2017 94.149 52.2017H77.7547C77.2853 52.2017 76.8351 52.0116 76.5032 51.6733C76.1713 51.335 75.9848 50.8761 75.9848 50.3977V49.7387C75.9848 49.2567 76.1726 48.7946 76.5069 48.4538C76.8413 48.1131 77.2947 47.9216 77.7675 47.9216Z"}]
[:path
{:d "M88.3843 52.1886V62.8165H92.5829V52.1886H88.3843Z"}]]]])
{:d "M88.3843 52.1886V62.8165H92.5829V52.1886H88.3843Z"}]]

[:symbol#icon-enlarge2
{:viewBox "0 0 32 32"}
[:path
{:d
"M32 0v13l-5-5-6 6-3-3 6-6-5-5zM14 21l-6 6 5 5h-13v-13l5 5 6-6z"}]]

[:symbol#icon-shrink
{:viewBox "0 0 32 32"}
[:path {:d "M18 14h13l-5-5 6-6-3-3-6 6-5-5z"}]
[:path {:d "M18 18v13l5-5 6 6 3-3-6-6 5-5z"}]
[:path {:d "M14 18h-13l5 5-6 6 3 3 6-6 5 5z"}]
[:path {:d "M14 14v-13l-5 5-6-6-3 3 6 6-5 5z"}]]]])
18 changes: 7 additions & 11 deletions src/cljs/bluegenes/components/idresolver/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@
::fx/http {:uri "/api/ids/parse"
:method :post
:multipart-params
(cond->
(map (fn [f] [(oget f :name) f])
js-Files)
; After creating multipart params for files,
; create one for text
; if there's a value
(cond-> (map (fn [f] [(oget f :name) f]) js-Files)
; After creating multipart params for files,
; create one for text
; if there's a value
(not (string/blank? text))
(conj ["text" text])
; And also a param for the case sensitive option
Expand Down Expand Up @@ -118,11 +116,9 @@
::update-type
(fn [db [_ model value]]
;; why are we disabling organism?
(let [disable-organism? (when
(not= value "_")
(not (contains?
(path/relationships model value)
:organism)))
(let [disable-organism? (when (not= value "_")
(not (contains? (path/relationships model value)
:organism)))
mine-details (get-in db [:mines (get db :current-mine)])]
(if disable-organism?
(update-in db [:idresolver :stage :options]
Expand Down
2 changes: 1 addition & 1 deletion src/cljs/bluegenes/components/lighttable.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

([results options]
(if (:title options) ;; we could tweak this further to make a nice passed-in title, too
[:div
[:div ; if you complete this component, you should assoc :type-constraints to current-model (see path/walk docstring)
[:h4 (:displayName (first (path/walk @current-model (:class results))))]
[table results]]
[table results])))))
Expand Down
5 changes: 2 additions & 3 deletions src/cljs/bluegenes/components/tools/effects.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,10 @@
;; In the latter case, it will merely call the tool's main function.
(reg-fx
:load-suitable-tools
(fn [{:keys [tools service entities]}]
(fn [{:keys [tools service hier entities]}]
(doseq [tool tools]
;; `entity` is nil if tool is not suitable to be displayed.
(when-let [entity (suitable-entities
(get-in service [:model :classes]) entities (:config tool))]
(when-let [entity (suitable-entities (get-in service [:model :classes]) hier entities (:config tool))]
(if-let [tool-id (get-in tool [:names :cljs])]
(do (fetch-script! tool tool-id :service service :entity entity)
(fetch-styles! tool tool-id))
Expand Down
5 changes: 4 additions & 1 deletion src/cljs/bluegenes/components/tools/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
::load-tools
(fn [{db :db} [_]]
(let [tools (get-in db [:tools :installed])
service (get-in db [:mines (:current-mine db) :service])
mine (get-in db [:mines (:current-mine db)])
hier (get mine :model-hier)
service (get mine :service)
entities (get-in db [:tools :entities])]
(cond
;; Tools aren't ready yet.
Expand All @@ -76,4 +78,5 @@
:success? true}
:load-suitable-tools {:tools tools
:service service
:hier hier
:entities entities}}))))
5 changes: 3 additions & 2 deletions src/cljs/bluegenes/components/tools/subs.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@
:<- [::installed-tools]
:<- [::entities]
:<- [:model]
(fn [[tools entities model]]
(filter #(suitable-entities model entities (:config %)) tools)))
:<- [:current-model-hier]
(fn [[tools entities model hier]]
(filter #(suitable-entities model hier entities (:config %)) tools)))
5 changes: 3 additions & 2 deletions src/cljs/bluegenes/components/ui/constraint.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,15 @@
(defn one-of? [value col] (some? (some #{value} col)))
(def not-one-of? (complement one-of?))

(defn constraint-operator []
(defn constraint-operator
"Creates a dropdown for a query constraint.
:model The intermine model to use
:path The path of the constraint
:op The operator of the constraint
:on-change A function to call with the new operator
:lists (Optional) if provided, automatically disable list constraints
if there are no lists of that type"
[]
(fn [& {:keys [model path op on-change on-blur lists disabled value]}]
(let [path-class (im-path/class model path)
any-lists-with-class? (some? (some (fn [list] (= path-class (keyword (:type list)))) lists))
Expand Down Expand Up @@ -292,7 +293,7 @@
(create-class
{:component-did-mount (fn []
(when (nil? @pv)
(dispatch [:cache/fetch-possible-values path])))
(dispatch [:cache/fetch-possible-values path model false])))
:reagent-render (fn [& {:keys [lists model path value op code on-change
on-select-list on-change-operator on-remove
on-blur label? possible-values typeahead? hide-code?
Expand Down
11 changes: 4 additions & 7 deletions src/cljs/bluegenes/components/ui/results_preview.cljs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
(ns bluegenes.components.ui.results_preview
(:require [imcljs.path :as im-path]
[oops.core :refer [oget]]
[reagent.core :as reagent :refer [create-class]]
[bluegenes.components.loader :refer [loader]]
(:require [bluegenes.components.loader :refer [loader]]
[clojure.string :refer [split join]]))

(defn table-header []
Expand All @@ -16,9 +13,10 @@
[:td
(str (:value d))]) row))))

(defn preview-table []
(defn preview-table
"Creates a dropdown for a query constraint.
:query-results The intermine model to use"
[]
(fn [& {:keys [query-results loading? hide-count?]}]
(if loading?
[loader]
Expand All @@ -29,8 +27,7 @@
[table-header h])
(:columnHeaders query-results)))]
[:tbody
(if
(< (:iTotalRecords query-results) 1)
(if (< (:iTotalRecords query-results) 1)
[:tr
[:td {:col-span (count (:columnHeaders query-results))}
[:h4 "Query returned no results"]]]
Expand Down
6 changes: 4 additions & 2 deletions src/cljs/bluegenes/components/viz/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
:viz/run-queries
(fn [{db :db} [_]]
(let [entities (get-in db [:tools :entities])
model (get-in db [:mines (:current-mine db) :service :model :classes])]
current-mine (get-in db [:mines (:current-mine db)])
model (get-in current-mine [:service :model :classes])
hier (get current-mine :model-hier)]
{:dispatch-n (map (fn [{:keys [config query key]}]
(when-let [entity (suitable-entities model entities config)]
(when-let [entity (suitable-entities model hier entities config)]
[:viz/run-query key (query entity)]))
all-viz)})))

Expand Down
24 changes: 18 additions & 6 deletions src/cljs/bluegenes/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
[imcljs.path :as im-path]
[clojure.string :refer [join split]]
[cljs.core.async :refer [put! chan <! >! timeout close!]]
[imcljs.fetch :as fetch]
[cljs-bean.core :refer [->clj]]
[bluegenes.utils :refer [read-registry-mine]]))

Expand Down Expand Up @@ -218,17 +217,30 @@
(go
(dispatch [:cache/store-possible-values mine-kw summary-path (<! sum-chan)])))))

(defn get-active-type-constraints [db]
(->> (case (:active-panel db)
:templates-panel (get-in db [:components :template-chooser :selected-template :where])
nil)
(filterv :type)))

(reg-event-fx
:cache/fetch-possible-values
(fn [{db :db} [_ path]]
(let [mine (get-in db [:mines (get db :current-mine)])
(fn [{db :db} [_ path model ?type-constraints]]
(let [type-constraints (if (false? ?type-constraints)
(get-active-type-constraints db)
?type-constraints)
model (assoc model :type-constraints type-constraints)
mine (get-in db [:mines (get db :current-mine)])
split-path (split path ".")
existing-value (get-in db [:mines (get db :current-mine) :possible-values split-path])]

(if (and (nil? existing-value) (not (im-path/class? (get-in mine [:service :model]) path)))
(if (and (nil? existing-value) (not (im-path/class? model path)))
{:cache/fetch-possible-values-fx {:service (get mine :service)
:query {:from (first split-path)
:select [path]}
:query (merge
{:from (first split-path)
:select [path]}
(when (seq type-constraints)
{:where type-constraints}))
:mine-kw (get mine :id)
:summary-path path}}
{:dispatch [:cache/store-possible-values (get mine :id) path false]}))))
Expand Down
26 changes: 19 additions & 7 deletions src/cljs/bluegenes/events/boot.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
:token token}}
{:id :default
:name nil
:service {:root "https://alpha.flymine.org/alpha"
:service {:root "https://www.flymine.org/flymine"
:token token}})))

(defn wait-for-registry?
Expand Down Expand Up @@ -341,15 +341,27 @@
(keys (filter (comp #(contains? % preferred-tag)
set :tags second) (:classes model))))

(defn extends-hierarchy
"Subclasses in the model are specified via the extends key. We build a
first-class hierarchy type using this so we can query whether a class
is a subclass for another class."
[model-classes]
(reduce (fn [h [child {:keys [extends]}]]
(reduce #(derive %1 child %2) h (map keyword extends)))
(make-hierarchy)
model-classes))

(reg-event-db
:assets/success-fetch-model
(fn [db [_ mine-kw model]]
(let [model' (update model :classes
#(into {} (filter (comp pos? :count val) %)))]
(-> db
(assoc-in [:mines mine-kw :service :model] model')
(assoc-in [:mines mine-kw :default-object-types]
(sort (preferred-fields model')))))))
;; We used to remove empty classes (zero count) from the model here, but
;; this turned out to be a very bad idea! This is because the model is used
;; to parse paths into classes, which means it has to be complete. This also
;; applies to the model hierarchy.
(-> db
(assoc-in [:mines mine-kw :service :model] model)
(assoc-in [:mines mine-kw :default-object-types] (sort (preferred-fields model)))
(assoc-in [:mines mine-kw :model-hier] (extends-hierarchy (:classes model))))))

(reg-event-fx
:assets/fetch-model
Expand Down
Loading