diff --git a/.gitignore b/.gitignore
index f7c09a04b..c64138c29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,9 @@ benchmark/tune.json
docs/Manifest.toml
types-old.jl
.vscode
+src/explorer/mimiexplorer-app/node_modules
+src/explorer/mimiexplorer-app/.pnp
+src/explorer/mimiexplorer-app/.pnp.js
+
+# testing
+src/explorer/mimiexplorer-app/coverage
diff --git a/Project.toml b/Project.toml
index b871b653f..99e7141ad 100644
--- a/Project.toml
+++ b/Project.toml
@@ -56,6 +56,7 @@ MetaGraphs = "0.6"
VegaLite = "1, 2"
TableTraits = "0.4.1, 1"
Classes = "1.2"
+Electron = "3.1"
IterTools = "1.3"
NamedArrays = "0.9"
IteratorInterfaceExtensions = "0.1.1, 1"
@@ -70,7 +71,6 @@ FileIO = "1"
julia = "1.4"
GlobalSensitivityAnalysis = "1.0"
DataFrames = "0.19.1, 0.20, 0.21"
-Electron = "1.1, 2.0, 3.0"
FilePaths = "0.8"
[targets]
diff --git a/docs/src/figs/explorer_model_example.png b/docs/src/figs/explorer_model_example.png
index 459146f9e..117a05309 100644
Binary files a/docs/src/figs/explorer_model_example.png and b/docs/src/figs/explorer_model_example.png differ
diff --git a/docs/src/howto/howto_2.md b/docs/src/howto/howto_2.md
index 54990946b..1f2738be8 100644
--- a/docs/src/howto/howto_2.md
+++ b/docs/src/howto/howto_2.md
@@ -30,11 +30,11 @@ Mimi provides support for plotting using [VegaLite](https://github.com/vega/vega
Plotting support is provided by the **Explorer UI**, rooted in `VegaLite`. The `explore` function allows the user to view and explore the variables and parameters of a model run. The explorer can be used in two primary ways.
-In order to invoke the explorer UI and explore all of the variables and parameters in a model, simply call the function `explore` with the model run as the required argument, and a window title as an optional keyword argument, as shown below. This will produce a new browser window containing a selectable list of parameters and variables, organized by component, each of which produces a graphic. The exception here being that if the parameter or variable is a single scalar value, the value will appear alongside the name in the left-hand list.
+In order to invoke the explorer UI and explore all of the variables and parameters in a model, simply call the function `explore` with the model run as the required argument as shown below. This will produce a new browser window containing a selectable list of parameters and variables, organized by component, each of which produces a graphic. The exception here being that if the parameter or variable is a single scalar value, the value will appear alongside the name in the left-hand list.
```julia
run(m)
-explore(m, title = "run1 results")
+explore(m)
```

diff --git a/docs/src/tutorials/tutorial_2.md b/docs/src/tutorials/tutorial_2.md
index 05dbca3eb..bd6895dc9 100644
--- a/docs/src/tutorials/tutorial_2.md
+++ b/docs/src/tutorials/tutorial_2.md
@@ -126,10 +126,10 @@ Mimi provides support for plotting using [VegaLite](https://github.com/vega/vega
If you wish to explore the results graphically, use the explorer UI. This functionality is described in more detail in the second how-to guide, How-to Guide 2: View and Explore Model Results. For now, however, you don't need this level of detail and can simply follow the steps below.
-To explore all variables and parameters of FUND in a dynamic UI app window, use the [`explore`](@ref) function called with the model as the required first argument, and the optional argument of the `title` The menu on the left hand side will list each element in a label formatted as `component: variable/parameter`.
+To explore all variables and parameters of FUND in a dynamic UI app window, use the [`explore`](@ref) function called with the model as the required first argument. The menu on the left hand side will list each element in a label formatted as `component: variable/parameter`.
```julia
-explore(m, title = "My Window")
+explore(m)
```
Alternatively, in order to view just one parameter or variable, call the function [`explore`](@ref) as below to return a plot object and automatically display the plot in a viewer, assuming [`explore`](@ref) is the last command executed. This call will return the type `VegaLite.VLSpec`, which you may interact with using the API described in the [VegaLite.jl](https://github.com/fredo-dedup/VegaLite.jl) documentation. For example, [VegaLite.jl](https://github.com/fredo-dedup/VegaLite.jl) plots can be saved as [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics), [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), [PDF](https://en.wikipedia.org/wiki/PDF) and [EPS](https://en.wikipedia.org/wiki/Encapsulated_PostScript) files. You may save a plot using the `save` function.
diff --git a/src/core/defs.jl b/src/core/defs.jl
index 595099a8f..bc7c9fa71 100644
--- a/src/core/defs.jl
+++ b/src/core/defs.jl
@@ -137,7 +137,8 @@ istype(T::DataType) = (pair -> pair.second isa T)
# Namespace filter functions return dicts of values for the given type.
# N.B. only composites hold other comps in the namespace.
-components(obj::AbstractCompositeComponentDef) = filter(istype(AbstractComponentDef), obj.namespace)
+components(model::Model, comp_name::Symbol) = components(compdef(model, comp_name))
+components(obj::AbstractComponentDef) = filter(istype(AbstractComponentDef), obj.namespace)
param_dict(obj::ComponentDef) = filter(istype(ParameterDef), obj.namespace)
param_dict(obj::AbstractCompositeComponentDef) = filter(istype(CompositeParameterDef), obj.namespace)
diff --git a/src/core/instances.jl b/src/core/instances.jl
index 3fc1dc326..6fab75bfd 100644
--- a/src/core/instances.jl
+++ b/src/core/instances.jl
@@ -213,6 +213,12 @@ function Base.getindex(mi::ModelInstance, key::AbstractString, datum::Symbol)
_get_datum(mi[key], datum)
end
+function Base.getindex(mi::ModelInstance, comp_path::ComponentPath, datum::Symbol)
+ _get_datum(mi[comp_path], datum)
+end
+
+@delegate Base.getindex(m::Model, comp_path::ComponentPath, datum::Symbol) => mi
+
function Base.getindex(obj::AbstractCompositeComponentInstance, comp_name::Symbol, datum::Symbol)
ci = obj[comp_name]
return _get_datum(ci, datum)
diff --git a/src/core/model.jl b/src/core/model.jl
index c0b9493e0..92df8f87a 100644
--- a/src/core/model.jl
+++ b/src/core/model.jl
@@ -262,7 +262,16 @@ datumdef(m::Model, comp_name::Symbol, item::Symbol) = datumdef(compdef(m.md, com
Return the dimension names for the variable or parameter `datum_name`
in the given component `comp_name` in model `m`.
"""
-dim_names(m::Model, comp_name::Symbol, datum_name::Symbol) = dim_names(compdef(m, comp_name), datum_name)
+function dim_names(m::Model, comp_name::Symbol, datum_name::Symbol)
+ # the line below would work if the comp_name is in the top level of components in m's component structure
+ # return dim_names(compdef(m, comp_name), datum_name)
+
+ paths = _get_all_paths(m)
+ comp_path = paths[comp_name]
+ comp_def = find_comp(m, comp_path)
+ return dim_names(comp_def, datum_name)
+end
+
dim_names(mm::MarginalModel, comp_name::Symbol, datum_name::Symbol) = dim_names(mm.base, comp_name, datum_name)
@delegate dimension(m::Model, dim_name::Symbol) => md
diff --git a/src/core/paths.jl b/src/core/paths.jl
index 88c7e6d79..a85eb95d8 100644
--- a/src/core/paths.jl
+++ b/src/core/paths.jl
@@ -151,6 +151,8 @@ end
find_comp(cr::AbstractComponentReference) = find_comp(parent(cr), pathof(cr))
+@delegate find_comp(m::Model, path::ComponentPath) => md
+
"""
Return the relative path of `descendant` if is within the path of composite `ancestor` or
or nothing otherwise.
@@ -194,3 +196,26 @@ gensym("ModelDef") names look like Symbol("##ModelDef#123")
function is_abspath(path::ComponentPath)
return ! isempty(path) && match(r"^##ModelDef#\d+$", string(path.names[1])) !== nothing
end
+
+# Returns a dictionary of the paths associated with all components, including composite components
+function _get_all_paths(m::Model)
+ all_paths = Dict{Symbol, ComponentPath}()
+ for comp in components(m) # iterate over top level ComponentInstances
+ _add_paths(m, comp, all_paths)
+ end
+ return all_paths
+end
+
+# a helper function to perform a preorder traversal of a given top-level component
+# in model m and add that path, and all sub-component paths, to the paths array
+function _add_paths(m::Model, comp::Union{CompositeComponentInstance, LeafComponentInstance}, paths::Dict{Symbol, ComponentPath})
+ if isa(comp, CompositeComponentInstance)
+ paths[comp.comp_name] = comp.comp_path
+ for subcomp in values(comp.comps_dict)
+ _add_paths(m, subcomp, paths)
+ end
+ else # LeafComponentInstance
+ paths[comp.comp_name] = comp.comp_path
+ end
+ return paths
+end
\ No newline at end of file
diff --git a/src/core/types/model.jl b/src/core/types/model.jl
index 7152a4fa5..279d3a390 100644
--- a/src/core/types/model.jl
+++ b/src/core/types/model.jl
@@ -51,6 +51,10 @@ function Base.getindex(mm::MarginalModel, comp_name::Symbol, name::Symbol)
return (mm.modified[comp_name, name] .- mm.base[comp_name, name]) ./ mm.delta
end
+function Base.getindex(mm::MarginalModel, comp_path::ComponentPath, name::Symbol)
+ return (mm.modified.mi[comp_path, name] .- mm.base.mi[comp_path, name]) ./ mm.delta
+end
+
# DEPRECATION - EVENTUALLY REMOVE (and go back to default getproperty behavior)
function Base.getproperty(base::MarginalModel, s::Symbol)
if (s == :marginal)
diff --git a/src/explorer/buildspecs.jl b/src/explorer/buildspecs.jl
index fa33be45e..eb5df501b 100644
--- a/src/explorer/buildspecs.jl
+++ b/src/explorer/buildspecs.jl
@@ -18,21 +18,24 @@ function _spec_for_item(m::Model, comp_name::Symbol, item_name::Symbol; interact
# Drop references to singleton dimensions
dims = tuple([dim for dim in dims if dim_count(m, dim) != 1]...)
end
-
+
# Control flow logic selects the correct plot type based on dimensions
# and dataframe fields
if length(dims) == 0
- value = m[comp_name, item_name]
+ paths = _get_all_paths(m)
+ comp_path = paths[comp_name];
+ value = m[comp_path, item_name] === nothing ? m[comp_name, item_name] : m[comp_path, item_name]
name = "$comp_name : $item_name = $value"
spec = createspec_singlevalue(name)
elseif length(dims) > 2
@warn("$comp_name.$item_name has > 2 indexed dimensions, not yet implemented in explorer")
- return nothing
+ name = "$comp_name : $item_name (CANNOT DISPLAY)"
+ spec = createspec_singlevalue(name)
else
name = "$comp_name : $item_name"
df = getdataframe(m, comp_name, item_name)
dffields = map(string, names(df)) # convert to string once before creating specs
-
+
# a 'time' field necessitates a line plot
if "time" in dffields
@@ -79,7 +82,8 @@ function _spec_for_sim_item(sim_inst::SimulationInstance, comp_name::Symbol, ite
spec = createspec_histogram(name, results, dffields; interactive = interactive)
elseif length(dims) > 2
@warn("$name has > 2 indexed dimensions, not yet implemented in explorer")
- return nothing
+ name = "$comp_name : $item_name (CANNOT DISPLAY)"
+ spec = createspec_singlevalue(name)
else
# check if there are too many dimensions to map and if so, error
@@ -103,36 +107,63 @@ function _spec_for_sim_item(sim_inst::SimulationInstance, comp_name::Symbol, ite
end
+function tree_view_values(model::Model)
+ all_subcomps = []
+ for comp_def in compdefs(model)
+ subcomp = tree_view_values(model, nameof(comp_def), comp_def)
+ push!(all_subcomps, subcomp)
+ end
+
+ # Return sorted list so that the UI list of items will be in lexicographic order
+ return sort(all_subcomps, by = x -> lowercase(x["name"]))
+end
+
+function tree_view_values(model::Model, comp_name::Symbol, comp_def::AbstractComponentDef)
+ sub_comp_item = _tree_view_node(comp_name)
+ for subcomp in compdefs(comp_def)
+ push!(sub_comp_item["children"], tree_view_values(model, nameof(subcomp), subcomp));
+ end
+ return sub_comp_item
+end
+
+function _tree_view_node(comp_name::Symbol)
+ return Dict("name" => "$comp_name", "children" => Dict[])
+end
+
# Create the list of variables and parameters
function menu_item_list(model::Model)
- all_menuitems = []
+ var_menuitems = []
+ par_menuitems = []
- for comp_name in map(nameof, compdefs(model))
- items = vcat(variable_names(model, comp_name), parameter_names(model, comp_name))
-
- for item_name in items
- menu_item = _menu_item(model, comp_name, item_name)
- if menu_item !== nothing
- push!(all_menuitems, menu_item)
- end
- end
+ for comp_def in compdefs(model)
+ all_subcomp_values = menu_item_list(model, nameof(comp_def), comp_def)
+ append!(var_menuitems, all_subcomp_values["vars"])
+ append!(par_menuitems, all_subcomp_values["pars"])
end
- # Return sorted list so that the UI list of items will be in alphabetical order
- return sort(all_menuitems, by = x -> lowercase(x["name"]))
+ # Return sorted list so that the UI list of items will be in lexicographic order
+ return Dict("vars" => sort(var_menuitems, by = x -> lowercase(x["name"])),"pars" => sort(par_menuitems, by = x -> lowercase(x["name"])))
+end
+
+# Create the list of variables and parameters
+function menu_item_list(m::Model, comp_name::Symbol, comp_def::AbstractComponentDef)
+ var_menu_items = map(var_name -> _menu_item(m, Symbol(comp_name), var_name), variable_names(comp_def));
+ par_menu_items = map(par_name -> _menu_item(m, Symbol(comp_name), par_name), parameter_names(comp_def));
+
+ # Return sorted list so that the UI list of items will be in lexicographic order
+ return Dict("vars" => sort(var_menu_items, by = x -> lowercase(x["name"])),"pars" => sort(par_menu_items, by = x -> lowercase(x["name"])))
end
function menu_item_list(sim_inst::SimulationInstance)
all_menuitems = []
for datum_key in sim_inst.sim_def.savelist
-
menu_item = _menu_item(sim_inst, datum_key)
if menu_item !== nothing
push!(all_menuitems, menu_item)
end
end
- # Return sorted list so that the UI list of items will be in alphabetical order
+ # Return sorted list so that the UI list of items will be in lexicographic order
return sort(all_menuitems, by = x -> lowercase(x["name"]))
end
@@ -144,11 +175,13 @@ function _menu_item(m::Model, comp_name::Symbol, item_name::Symbol)
end
if length(dims) == 0
- value = m[comp_name, item_name]
+ paths = _get_all_paths(m)
+ comp_path = paths[comp_name];
+ value = m[comp_path, item_name]
name = "$comp_name : $item_name = $value"
elseif length(dims) > 2
@warn("$comp_name.$item_name has > 2 indexed dimensions, not yet implemented in explorer")
- return nothing
+ name = "$comp_name : $item_name (CANNOT DISPLAY)"
else
name = "$comp_name : $item_name" # the name is needed for the list label
end
@@ -167,7 +200,7 @@ function _menu_item(sim_inst::SimulationInstance, datum_key::Tuple{Symbol, Symbo
if length(dims) > 2
@warn("$comp_name.$item_name has >2 graphing dims, not yet implemented in explorer")
- return nothing
+ name = "$comp_name : $item_name (CANNOT DISPLAY)"
else
name = "$comp_name : $item_name" # the name is needed for the list label
end
@@ -193,10 +226,10 @@ function createspec_lineplot_interactive(name, df, dffields)
"VLspec" => Dict(
"\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
"description" => "plot for a specific component variable pair",
- "title" => "$name (use bottom plot for interactive selection)",
"data"=> Dict("values" => datapart),
"vconcat" => [
Dict(
+ "title" => "$name",
# "transform" => [Dict("filter" => Dict("selection" => "brush"))],
"width" => _plot_width,
"height" => _plot_height,
@@ -215,6 +248,7 @@ function createspec_lineplot_interactive(name, df, dffields)
)
)
), Dict(
+ "title" => "INTERACTIVE PLOT",
"width" => _plot_width,
"height" => _slider_height,
"mark" => Dict("type" => "line", "point" => true),
@@ -280,10 +314,10 @@ function createspec_multilineplot_interactive(name, df, dffields, strmultidims)
"VLspec" => Dict(
"\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
"description" => "plot for a specific component variable pair",
- "title" => "$name (use bottom plot for interactive selection)",
"data" => Dict("values" => datapart),
"vconcat" => [
Dict(
+ "title" => "$name",
# "transform" => [Dict("filter" => Dict("selection" => "brush"))],
"mark" => Dict("type" => "line", "point" => true),
"encoding" => Dict(
@@ -305,6 +339,7 @@ function createspec_multilineplot_interactive(name, df, dffields, strmultidims)
"width" => _plot_width,
"height" => _plot_height
), Dict(
+ "title" => "INTERACTIVE PLOT",
"width" => _plot_width,
"height" => _slider_height,
"mark" => Dict("type" => "line", "point" => true),
@@ -467,10 +502,10 @@ function createspec_trumpet_interactive(name, df, dffields)
"VLspec" => Dict(
"\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
"description" => "plot for a specific component variable pair",
- "title" => "$name (use bottom plot for interactive selection)",
"data"=> Dict("values" => datapart),
"vconcat" => [
Dict(
+ "title" => "$name",
"width" => _plot_width,
"height" => _plot_height,
"encoding" => Dict(
@@ -514,6 +549,7 @@ function createspec_trumpet_interactive(name, df, dffields)
]
),
Dict(
+ "title" => "INTERACTIVE PLOT",
"width" => _plot_width,
"height" => _slider_height,
"encoding" => Dict(
diff --git a/src/explorer/explore.jl b/src/explorer/explore.jl
index 927f5a2b5..7ffa7486f 100644
--- a/src/explorer/explore.jl
+++ b/src/explorer/explore.jl
@@ -11,11 +11,11 @@ include("buildspecs.jl")
include("results.jl")
"""
- explore(m::Model; title = "Electron")
+ explore(m::Model)
-Produce a UI to explore the parameters and variables of Model `m` in a Window with title `title`.
+Produce a UI to explore the parameters and variables of Model `m` in an independent window.
"""
-function explore(m::Model; title = "Electron")
+function explore(m::Model)
if m.mi === nothing
error("A model must be run before it can be plotted")
@@ -26,33 +26,46 @@ function explore(m::Model; title = "Electron")
global app = Application()
end
- #load main html file
- mainpath = replace(joinpath(@__DIR__, "assets", "main.html"), "\\" => "/")
-
#window options
- windowopts = Dict("title" => title, "width" => 1000, "height" => 700)
- w = Window(app, URI(joinpath(@__PATH__, "assets", "main.html")), options = windowopts)
-
+ windowopts = Dict("width" => 1000, "height" => 1000)
+ w = Window(app, joinpath(@__PATH__, p"mimiexplorer-app/build/index.html"), options = windowopts)
+
#set async block to process messages
@async for msg in msgchannel(w)
+ if (msg["cmd"] == "display_spec")
+ spec = _spec_for_item(m, Symbol(msg["comp_name"]), Symbol(msg["item_name"]))
+ specJSON = JSON.json(spec)
+ run(w, "display($specJSON)")
+ end
+ if (msg["cmd"] == "update_data")
+ comp_name = msg["comp_name"];
+ paths = _get_all_paths(m)
+ comp_path = paths[Symbol(comp_name)];
+ comp_def = find_comp(m, comp_path);
+ menulist = menu_item_list(m, Symbol(comp_name), comp_def)
+ menulistJSON = JSON.json(menulist);
+ result = run(w, "setData($menulistJSON)");
+ end
+ end
- spec = _spec_for_item(m, Symbol(msg["comp_name"]), Symbol(msg["item_name"]))
- specJSON = JSON.json(spec)
+ # Electron.toggle_devtools(w)
- run(w, "display($specJSON)")
- end
+ #refresh tree view
+ subcomplist = tree_view_values(m)
+ subcomplistJSON = JSON.json(subcomplist)
- #refresh variable list
- menulist = menu_item_list(m)
- menulistJSON = JSON.json(menulist)
+ result = run(w, "setTreeChildren($subcomplistJSON)")
+
+ #refresh data view
+ datalist = menu_item_list(m)
+ datalistJSON = JSON.json(datalist)
+ result = run(w, "setData($datalistJSON)")
- result = run(w, "refresh($menulistJSON)")
-
return w
end
-function explore(mi::ModelInstance; title = "Electron")
+function explore(mi::ModelInstance)
m = Model(mi)
m.md.dirty = false # we need this to get explorer working, but it's a hack and should be temporary!
explore(m)
diff --git a/src/explorer/mimiexplorer-app/README.md b/src/explorer/mimiexplorer-app/README.md
new file mode 100644
index 000000000..74735dc66
--- /dev/null
+++ b/src/explorer/mimiexplorer-app/README.md
@@ -0,0 +1,44 @@
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
+
+The page will reload if you make edits.
+You will also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
+
+If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
+
+You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
diff --git a/src/explorer/mimiexplorer-app/build/asset-manifest.json b/src/explorer/mimiexplorer-app/build/asset-manifest.json
new file mode 100644
index 000000000..cc4534d7c
--- /dev/null
+++ b/src/explorer/mimiexplorer-app/build/asset-manifest.json
@@ -0,0 +1,22 @@
+{
+ "files": {
+ "main.css": "./static/css/main.cffa2cfa.chunk.css",
+ "main.js": "./static/js/main.173225f5.chunk.js",
+ "main.js.map": "./static/js/main.173225f5.chunk.js.map",
+ "runtime-main.js": "./static/js/runtime-main.a5a8ef9e.js",
+ "runtime-main.js.map": "./static/js/runtime-main.a5a8ef9e.js.map",
+ "static/js/2.c20c54c2.chunk.js": "./static/js/2.c20c54c2.chunk.js",
+ "static/js/2.c20c54c2.chunk.js.map": "./static/js/2.c20c54c2.chunk.js.map",
+ "index.html": "./index.html",
+ "precache-manifest.78211434cff98949dccf2ba16888ad6d.js": "./precache-manifest.78211434cff98949dccf2ba16888ad6d.js",
+ "service-worker.js": "./service-worker.js",
+ "static/css/main.cffa2cfa.chunk.css.map": "./static/css/main.cffa2cfa.chunk.css.map",
+ "static/js/2.c20c54c2.chunk.js.LICENSE.txt": "./static/js/2.c20c54c2.chunk.js.LICENSE.txt"
+ },
+ "entrypoints": [
+ "static/js/runtime-main.a5a8ef9e.js",
+ "static/js/2.c20c54c2.chunk.js",
+ "static/css/main.cffa2cfa.chunk.css",
+ "static/js/main.173225f5.chunk.js"
+ ]
+}
\ No newline at end of file
diff --git a/src/explorer/mimiexplorer-app/build/favicon.ico b/src/explorer/mimiexplorer-app/build/favicon.ico
new file mode 100644
index 000000000..bcd5dfd67
Binary files /dev/null and b/src/explorer/mimiexplorer-app/build/favicon.ico differ
diff --git a/src/explorer/mimiexplorer-app/build/index.html b/src/explorer/mimiexplorer-app/build/index.html
new file mode 100644
index 000000000..0a33cd38a
--- /dev/null
+++ b/src/explorer/mimiexplorer-app/build/index.html
@@ -0,0 +1 @@
+
>1)+d+e+k+E.slice(_);break;default:e=E+d+e+k}return u(e)}return g=void 0===g?6:/[gprs]/.test(y)?Math.max(1,Math.min(21,g)):Math.max(0,Math.min(20,g)),k.toString=function(){return e+""},k}return{format:f,formatPrefix:function(e,t){var n=f(((e=We(e)).type="f",e)),r=3*Math.max(-8,Math.min(8,Math.floor(Qe(t)/3))),i=Math.pow(10,-r),a=at[8+r/3];return function(e){return n(i*e)+a}}}};function ut(e){var t=Re(e.format),n=e.formatPrefix;return{format:t,formatPrefix:n,formatFloat:function(e){var n,r,i=We(e||",");if(null==i.precision){switch(i.precision=12,i.type){case"%":i.precision-=2;break;case"e":i.precision-=1}return n=t(i),r=t(".1f")(1)[1],function(e){var t,i,a=n(e),o=a.indexOf(r);if(o<0)return a;for(i=(t=function(e,t){var n,r=e.lastIndexOf("e");if(r>0)return r;for(r=e.length;--r>t;)if((n=e.charCodeAt(r))>=48&&n<=57)return r+1}(a,o)) g)))return e.save(),e.translate(h,p),h=i-h,p=a-p,y&&ms(l)&&!Af(e,l,u,c)?(e.restore(),null):(f=l.strokeForeground,(d=!1!==t.interactive)&&f&&l.stroke&&Cf(e,l,u,c)?(e.restore(),l):(!(s=sf(l,(function(e){return function(e,t,n){return(!1!==e.interactive||"group"===e.marktype)&&e.bounds&&e.bounds.contains(t,n)}(e,h,p)?o.pick(e,n,r,h,p):null})))&&d&&(l.fill||!f&&l.stroke)&&Af(e,l,u,c)&&(s=l),e.restore(),s||null))}))},isect:Zs,content:function(e,t,n){e("clip-path",t.clip?Os(n,t,t):null)},background:function(e,t){e("class","background"),e("aria-hidden",!0),Ef(e,t)},foreground:function(e,t){e("class","foreground"),e("aria-hidden",!0),t.strokeForeground?Ef(e,t):e("d","")}},Tf={version:"1.1",xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"};function Mf(e,t){var n=e.image;return(!n||e.url&&e.url!==n.url)&&(n={complete:!1,width:0,height:0},t.loadImage(e.url).then((function(t){e.image=t,e.image.url=e.url}))),n}function Ff(e,t){return null!=e.width?e.width:t&&t.width?!1!==e.aspect&&e.height?e.height*t.width/t.height:t.width:0}function Nf(e,t){return null!=e.height?e.height:t&&t.height?!1!==e.aspect&&e.width?e.width*t.height/t.width:t.height:0}function Rf(e,t){return"center"===e?t/2:"right"===e?t:0}function Pf(e,t){return"middle"===e?t/2:"bottom"===e?t:0}var zf={type:"image",tag:"image",nested:!1,attr:function(e,t,n){var r=Mf(t,n),i=Ff(t,r),a=Nf(t,r),o=(t.x||0)-Rf(t.align,i),u=(t.y||0)-Pf(t.baseline,a);e("href",!r.src&&r.toDataURL?r.toDataURL():r.src||"",Tf["xmlns:xlink"],"xlink:href"),e("transform",gf(o,u)),e("width",i),e("height",a),e("preserveAspectRatio",!1===t.aspect?"none":"xMidYMid")},bound:function(e,t){var n=t.image,r=Ff(t,n),i=Nf(t,n),a=(t.x||0)-Rf(t.align,r),o=(t.y||0)-Pf(t.baseline,i);return e.set(a,o,a+r,o+i)},draw:function(e,t,n){var r=this;lf(t,(function(t){if(!n||n.intersects(t.bounds)){var i,a,o,u,c=Mf(t,r),l=Ff(t,c),s=Nf(t,c),f=(t.x||0)-Rf(t.align,l),d=(t.y||0)-Pf(t.baseline,s);!1!==t.aspect&&(a=c.width/c.height,o=t.width/t.height,a===a&&o===o&&a!==o&&(o=0;)if(!1!==e[a].defined&&(n=e[a].x-t[0])*n+(r=e[a].y-t[1])*r1?t:t[0]:t;var t}function rd(e){var t=nd(e);return(Object(p.A)(t)?t.length-1:0)*td(e)}function id(e,t){var n=null==t?"":(t+"").trim();return e.limit>0&&n.length?function(e,t){var n=+e.limit,r=function(e){if(Gf.width===Kf){var t=od(e);return function(e){return Jf(e,t)}}var n=ed(e);return function(e){return Qf(e,n)}}(e);if(r(t) =u&&o[i]<=c&&(l<0&&(l=i),n=i);if(!(l<0))return u=e.invertExtent(o[l]),c=e.invertExtent(o[n]),[void 0===u[0]?u[1]:u[0],void 0===c[1]?c[0]:c[1]]}}(n):void 0),n.type=e,n};return r.metadata=Object(p.db)(Object(p.i)(n)),r}function nv(e,t,n){return arguments.length>1?(ev[e]=tv(e,t,n),this):rv(e)?ev[e]:void 0}function rv(e){return Object(p.v)(ev,e)}function iv(e,t){var n=ev[e];return n&&n.metadata[t]}function av(e){return iv(e,ih)}function ov(e){return iv(e,"discrete")}function uv(e){return iv(e,"discretizing")}function cv(e){return iv(e,th)}function lv(e){return iv(e,"interpolating")}function sv(e){return iv(e,"quantile")}nv("identity",(function e(t){var n;function r(e){return isNaN(e=+e)?n:e}return r.invert=r,r.domain=r.range=function(e){return arguments.length?(t=Array.from(e,yp),r):t.slice()},r.unknown=function(e){return arguments.length?(n=e,r):n},r.copy=function(){return e(t).unknown(n)},t=arguments.length?Array.from(t,yp):[0,1],Sp(r)})),nv("linear",(function e(){var t=Ep();return t.copy=function(){return jp(t,e())},lh.apply(t,arguments),Sp(t)}),ih),nv(th,(function e(){var t=Rp(_p()).domain([1,10]);return t.copy=function(){return jp(t,e()).base(t.base())},lh.apply(t,arguments),t}),[ih,th]),nv("pow",Bp,ih),nv("sqrt",(function(){return Bp.apply(null,arguments).exponent(.5)}),ih),nv("symlog",(function e(){var t=Wp(_p());return t.copy=function(){return jp(t,e()).constant(t.constant())},lh.apply(t,arguments)}),ih),nv(nh,(function(){return lh.apply(Vp(En,An,Rt,Ft,Dn,Mn,Nn,Pn,br).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}),[ih,"temporal"]),nv(rh,(function(){return lh.apply(Vp(Bn,qn,Xt,Wt,Hn,Vn,Nn,Pn,Or).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)}),[ih,"temporal"]),nv("sequential",Xp,[ih,"interpolating"]),nv("".concat("sequential","-").concat("linear"),Xp,[ih,"interpolating"]),nv("".concat("sequential","-").concat(th),(function e(){var t=Rp(Yp()).domain([1,10]);return t.copy=function(){return Gp(t,e()).base(t.base())},sh.apply(t,arguments)}),[ih,"interpolating",th]),nv("".concat("sequential","-").concat("pow"),Zp,[ih,"interpolating"]),nv("".concat("sequential","-").concat("sqrt"),(function(){return Zp.apply(null,arguments).exponent(.5)}),[ih,"interpolating"]),nv("".concat("sequential","-").concat("symlog"),(function e(){var t=Wp(Yp());return t.copy=function(){return Gp(t,e()).constant(t.constant())},sh.apply(t,arguments)}),[ih,"interpolating"]),nv("".concat("diverging","-").concat("linear"),(function e(){var t=Sp(Kp()(xp));return t.copy=function(){return Gp(t,e())},sh.apply(t,arguments)}),[ih,"interpolating"]),nv("".concat("diverging","-").concat(th),(function e(){var t=Rp(Kp()).domain([.1,1,10]);return t.copy=function(){return Gp(t,e()).base(t.base())},sh.apply(t,arguments)}),[ih,"interpolating",th]),nv("".concat("diverging","-").concat("pow"),Jp,[ih,"interpolating"]),nv("".concat("diverging","-").concat("sqrt"),(function(){return Jp.apply(null,arguments).exponent(.5)}),[ih,"interpolating"]),nv("".concat("diverging","-").concat("symlog"),(function e(){var t=Wp(Kp());return t.copy=function(){return Gp(t,e()).constant(t.constant())},sh.apply(t,arguments)}),[ih,"interpolating"]),nv("quantile",(function e(){var t,n=[],r=[],i=[];function a(){var e=0,t=Math.max(1,r.length);for(i=new Array(t-1);++e 0&&s>0?(Wy(h,p,v,g,l),Hy(h,p,g,v,s),Wy(h,p,v,g,l),Hy(h,p,g,v,s),Wy(h,p,v,g,l),Hy(h,p,g,v,s)):l>0?(Wy(h,p,v,g,l),Wy(h,p,g,v,l),Wy(h,p,v,g,l),m=g):s>0&&(Hy(h,p,v,g,s),Hy(h,p,g,v,s),Hy(h,p,v,g,s),m=g);for(var y=c?Math.pow(2,-2*o):1/my(m),b=0,x=h*p;b >>1;l[v] >1,y.y=u[1]*(s()+.5)>>1,y_(c,y,m,v),y.hasText&&d(f,y,h)&&(g.push(y),h?x_(h,y):h=[{x:y.x+y.x0,y:y.y+y.y0},{x:y.x+y.x1,y:y.y+y.y1}],y.x-=u[0]>>1,y.y-=u[1]>>1)}return g},f.words=function(e){return arguments.length?(l=e,f):l},f.size=function(e){return arguments.length?(u=[+e[0],+e[1]],f):u},f.font=function(e){return arguments.length?(t=w_(e),f):t},f.fontStyle=function(e){return arguments.length?(r=w_(e),f):r},f.fontWeight=function(e){return arguments.length?(i=w_(e),f):i},f.rotate=function(e){return arguments.length?(a=w_(e),f):a},f.text=function(t){return arguments.length?(e=w_(t),f):e},f.spiral=function(e){return arguments.length?(c=k_[e]||e,f):c},f.fontSize=function(e){return arguments.length?(n=w_(e),f):n},f.padding=function(e){return arguments.length?(o=w_(e),f):o},f.random=function(e){return arguments.length?(s=e,f):s},f};function y_(e,t,n,r){if(!t.sprite){var i=e.context,a=e.ratio;i.clearRect(0,0,2048/a,2048/a);var o,u,c,l,s,f=0,d=0,h=0,p=n.length;for(--r;++r >5<<5,c=~~Math.max(Math.abs(y+b),Math.abs(y-b))}else o=o+31>>5<<5;if(c>h&&(h=c),f+o>=2048&&(f=0,d+=h,h=0),d+c>=2048)break;i.translate((f+(o>>1))/a,(d+(c>>1))/a),t.rotate&&i.rotate(t.rotate*g_),i.fillText(t.text,0,0),t.padding&&(i.lineWidth=2*t.padding,i.strokeText(t.text,0,0)),i.restore(),t.width=o,t.height=c,t.xoff=f,t.yoff=d,t.x1=o>>1,t.y1=c>>1,t.x0=-t.x1,t.y0=-t.y1,t.hasText=!0,f+=o}for(var O=i.getImageData(0,0,2048/a,2048/a).data,w=[];--r>=0;)if((t=n[r]).hasText){for(u=(o=t.width)>>5,c=t.y1-t.y0,l=0;l=f){for(s=(l+s)/2;ao[x]-y?b:x,w=0,k=0,j=0,_=0,E=0,S=1/Math.abs(o[O]-y||1),A=b;A<=x;++A){var C=o[A],D=u[A],T=Ro(Math.abs(y-C)*S)*p[A],M=C*T;w+=T,k+=M,j+=D*T,_+=D*M,E+=C*M}var F=Oo(k/w,j/w,_/w,E/w),N=Object(So.a)(F,2),R=N[0],P=N[1];d[m]=R+P*y,h[m]=Math.abs(u[m]-d[m]),Po(o,m+1,g)}if(2===v)break;var z=Fo(h);if(Math.abs(z)<1e-12)break;for(var L,I,B=0;B=1?1e-12:(I=1-L*L)*I}return function(e,t,n,r){for(var i,a=e.length,o=[],u=0,c=0,l=[];u=e.length))for(;t>i&&e[a]-r<=r-e[i];)n[0]=++i,n[1]=a,++a}var zo=.1*Math.PI/180,Lo=function(e,t,n,r){n=n||25,r=Math.max(n,r||200);var i=function(t){return[t,e(t)]},a=t[0],o=t[1],u=o-a,c=u/r,l=[i(a)],s=[];if(n===r){for(var f=1;f0){for(n=[];--u>=0;)n.push(i=S(c(e))),a.push(i);o.add=o.add.length?o.materialize(o.ADD).add.concat(n):n}else r=a.slice(0,-u),o.rem=o.rem.length?o.materialize(o.REM).rem.concat(r):r,a=a.slice(-u);return o.source=this.value=a,o};var xu={value:"value",median:Fo,mean:function(e,t){var n=0,r=0;if(void 0===t){var i,a=Object(Fa.a)(e);try{for(a.s();!(i=a.n()).done;){var o=i.value;null!=o&&(o=+o)>=o&&(++n,r+=o)}}catch(f){a.e(f)}finally{a.f()}}else{var u,c=-1,l=Object(Fa.a)(e);try{for(l.s();!(u=l.n()).done;){var s=u.value;null!=(s=t(s,++c,e))&&(s=+s)>=s&&(++n,r+=s)}}catch(f){l.e(f)}finally{l.f()}}if(n)return r/n},min:za,max:Pa},Ou=[];function wu(e){va.call(this,[],e)}function ku(e){qo.call(this,e)}wu.Definition={type:"Impute",metadata:{changes:!0},params:[{name:"field",type:"field",required:!0},{name:"key",type:"field",required:!0},{name:"keyvals",array:!0},{name:"groupby",type:"field",array:!0},{name:"method",type:"enum",default:"value",values:["value","mean","median","max","min"]},{name:"value",default:0}]},Object(p.y)(wu,va).transform=function(e,t){var n,r,i,a,o,u,c,l,s,f,d=t.fork(t.ALL),h=function(e){var t,n=e.method||xu.value;if(null!=xu[n])return n===xu.value?(t=void 0!==e.value?e.value:0,function(){return t}):xu[n];Object(p.n)("Unrecognized imputation method: "+n)}(e),v=function(e){var t=e.field;return function(e){return e?t(e):NaN}}(e),g=Object(p.h)(e.field),m=Object(p.h)(e.key),y=(e.groupby||[]).map(p.h),b=function(e,t,n,r){var i,a,o,u,c,l,s,f,d=function(e){return e(f)},h=[],p=r?r.slice():[],v={},g={};for(p.forEach((function(e,t){v[e]=t+1})),u=0,s=e.length;ua&&(a=r[1]);return[i,a]}function Fu(e){I.call(this,null,Nu,e)}function Nu(e){return this.value&&!e.modified()?this.value:e.values.reduce((function(e,t){return e.concat(t)}),[])}function Ru(e){va.call(this,null,e)}function Pu(e){qo.call(this,e)}ju.transform=function(e,t){var n,r=this,i=e.modified();return r.value&&(i||t.modified(r._inputs,!0))?(n=r.value=i?r.init(e):{},t.visit(t.SOURCE,(function(e){r.add(e)}))):(n=r.value=r.value||this.init(e),t.visit(t.REM,(function(e){r.rem(e)})),t.visit(t.ADD,(function(e){r.add(e)}))),r.changes(),t.visit(t.SOURCE,(function(e){Object(p.o)(e,n[r.cellkey(e)].tuple)})),t.reflow(i).modifies(this._outputs)},ju.changes=function(){var e,t,n=this._adds,r=this._mods;for(e=0,t=this._alen;eu)for(l=1,f=i.length;l=s;--f)u.point(g[f],m[f]);u.lineEnd(),u.areaEnd()}v&&(g[l]=+e(d,l,c),m[l]=+n(d,l,c),u.point(t?+t(d,l,c):g[l],r?+r(d,l,c):m[l]))}if(h)return u=null,h+""||null}function l(){return ns().defined(i).curve(o).context(a)}return c.x=function(n){return arguments.length?(e="function"===typeof n?n:Vl(+n),t=null,c):e},c.x0=function(t){return arguments.length?(e="function"===typeof t?t:Vl(+t),c):e},c.x1=function(e){return arguments.length?(t=null==e?null:"function"===typeof e?e:Vl(+e),c):t},c.y=function(e){return arguments.length?(n="function"===typeof e?e:Vl(+e),r=null,c):n},c.y0=function(e){return arguments.length?(n="function"===typeof e?e:Vl(+e),c):n},c.y1=function(e){return arguments.length?(r=null==e?null:"function"===typeof e?e:Vl(+e),c):r},c.lineX0=c.lineY0=function(){return l().x(e).y(n)},c.lineY1=function(){return l().x(e).y(r)},c.lineX1=function(){return l().x(t).y(n)},c.defined=function(e){return arguments.length?(i="function"===typeof e?e:Vl(!!e),c):i},c.curve=function(e){return arguments.length?(o=e,null!=a&&(u=o(a)),c):o},c.context=function(e){return arguments.length?(null==e?a=u=null:u=o(a=e),c):a},c},is={draw:function(e,t){var n=Math.sqrt(t/Ic);e.moveTo(n,0),e.arc(0,0,n,0,Uc)}},as=(Math.sqrt(1/3),Math.sin(Ic/10)/Math.sin(7*Ic/10));Math.sin(Uc/10),Math.cos(Uc/10),Math.sqrt(3),Math.sqrt(3),Math.sqrt(12);function os(e,t){return null!=e?e:t}var us=function(e){return e.x||0},cs=function(e){return e.y||0},ls=function(e){return!(!1===e.defined)},ss=function(){var e=Yl,t=Gl,n=Vl(0),r=null,i=Xl,a=Zl,o=Ql,u=null;function c(){var c,l,s=+e.apply(this,arguments),f=+t.apply(this,arguments),d=i.apply(this,arguments)-Bc,h=a.apply(this,arguments)-Bc,p=Mc(h-d),v=h>d;if(u||(u=c=Pl()),f1e-12)if(p>Uc-1e-12)u.moveTo(f*Nc(d),f*zc(d)),u.arc(0,0,f,d,h,!v),s>1e-12&&(u.moveTo(s*Nc(h),s*zc(h)),u.arc(0,0,s,h,d,v));else{var g,m,y=d,b=h,x=d,O=h,w=p,k=p,j=o.apply(this,arguments)/2,_=j>1e-12&&(r?+r.apply(this,arguments):Lc(s*s+f*f)),E=Pc(Mc(f-s)/2,+n.apply(this,arguments)),S=E,A=E;if(_>1e-12){var C=Wc(_/s*zc(j)),D=Wc(_/f*zc(j));(w-=2*C)>1e-12?(x+=C*=v?1:-1,O-=C):(w=0,x=O=(d+h)/2),(k-=2*D)>1e-12?(y+=D*=v?1:-1,b-=D):(k=0,y=b=(d+h)/2)}var T=f*Nc(y),M=f*zc(y),F=s*Nc(O),N=s*zc(O);if(E>1e-12){var R,P=f*Nc(b),z=f*zc(b),L=s*Nc(x),I=s*zc(x);if(p=m)<<1|e>=g)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-l],p[p.length-1-l]=c)}else{var y=e-+this._x.call(null,v.data),b=t-+this._y.call(null,v.data),x=y*y+b*b;if(x1?(l.on(e,n),t):l.on(e)}}}(e),r=!1,i=n.stop,a=n.restart;return n.stopped=function(){return r},n.restart=function(){return r=!1,a()},n.stop=function(){return r=!0,i()},vk(n,t,!0).on("end",(function(){r=!0}))}(t.source,e),i.on("tick",(n=t.dataflow,r=this,function(){n.touch(r).run()})),e.static||(a=!0,i.tick()),t.modifies("index")),o||a||e.modified(fk)||t.changed()&&e.restart)if(i.alpha(Math.max(i.alpha(),e.alpha||1)).alphaDecay(1-Math.pow(i.alphaMin(),1/u)),e.static)for(i.stop();--u>=0;)i.tick();else if(i.stopped()&&i.restart(),!a)return t.StopPropagation;return this.finish(e,t)},pk.finish=function(e,t){for(var n,r=t.dataflow,i=this._argops,a=0,o=i.length;a1;)i-=2;for(var a=2;a4)for(var u=0;u1&&void 0!==arguments[1]?arguments[1]:2,n=null==e?e=new e_:void 0,r=this.points,i=0,a=r.length;i=h));)if(t.x=f+i,t.y=d+a,!(t.x+t.x0<0||t.y+t.y0<0||t.x+t.x1>u[0]||t.y+t.y1>u[1])&&(!n||!b_(t,e,u[0]))&&(!n||(l=n,(o=t).x+o.x1>l[0].x&&o.x+o.x0