sisl / D3Trees.jl
Showing 2 of 8 files from the diff.

@@ -20,11 +20,11 @@
Loading
20 20
struct D3TreeServer
21 21
    server::HTTP.Servers.Server
22 22
    router::HTTP.Router
23 -
    tree_data::Dict{String, D3Tree}
23 +
    tree_data::Dict{String,D3Tree}
24 24
end
25 25
HTTP.close(d3s::D3TreeServer) = close(d3s.server)
26 26
27 -
const SERVERS = Dict{Int64, D3TreeServer}()
27 +
const SERVERS = Dict{Int64,D3TreeServer}()
28 28
29 29
30 30
@@ -46,7 +46,7 @@
Loading
46 46
    # start new server on given port
47 47
    router = HTTP.Router(cors404, cors405)
48 48
    server = HTTP.serve!(router |> cors_middleware |> logging_middleware, HOST, port)
49 -
    tree_data = Dict{String, D3Tree}()
49 +
    tree_data = Dict{String,D3Tree}()
50 50
    d3s = D3TreeServer(server, router, tree_data)
51 51
    SERVERS[port] = d3s
52 52
    return d3s
@@ -59,7 +59,7 @@
Loading
59 59
Shutdown D3Trees server (all servers if port is not specified) and remove associated tree data.
60 60
"""
61 61
function shutdown_server!(port)
62 -
    d3s=SERVERS[port]
62 +
    d3s = SERVERS[port]
63 63
    isopen(d3s.server) ? close(d3s) : nothing
64 64
    @assert istaskdone(d3s.server.task)
65 65
    delete!(SERVERS, port)
@@ -72,7 +72,7 @@
Loading
72 72
"""
73 73
function logging_middleware(handler)
74 74
    # Middleware functions return *Handler* functions
75 -
    return function(req::HTTP.Request)
75 +
    return function (req::HTTP.Request)
76 76
        @debug "Incoming server request:\n$req"
77 77
        res = handler(req)
78 78
        @debug "Server reponds:\n$res"
@@ -90,16 +90,16 @@
Loading
90 90
correct service function.
91 91
"""
92 92
function cors_middleware(handler)
93 -
    return function(req::HTTP.Request)
93 +
    return function (req::HTTP.Request)
94 94
        if HTTP.hasheader(req, "OPTIONS")
95 95
            return HTTP.Response(200, CORS_OPT_HEADERS)
96 -
        else 
96 +
        else
97 97
            return handler(req)
98 98
        end
99 99
    end
100 100
end
101 101
102 -
function process_node_expand_request(tree_data::Dict{String, D3Tree}, div_id::String, subtree_root_id::Integer, depth::Integer)
102 +
function process_node_expand_request(tree_data::Dict{String,D3Tree}, div_id::String, subtree_root_id::Integer, depth::Integer)
103 103
    if haskey(tree_data, div_id)
104 104
        tree = tree_data[div_id]
105 105
        try
@@ -117,7 +117,7 @@
Loading
117 117
    end
118 118
end
119 119
120 -
function handle_subtree_request(req::HTTP.Request, tree_data::Dict{String, D3Tree}, lazy_subtree_depth::Integer)
120 +
function handle_subtree_request(req::HTTP.Request, tree_data::Dict{String,D3Tree}, lazy_subtree_depth::Integer)
121 121
    tree_div = HTTP.getparams(req)["treediv"]
122 122
    node_id = parse(Int, HTTP.getparams(req)["nodeid"])
123 123
    @debug "Request for tree $tree_div - Node: $node_id\n$req"
@@ -138,46 +138,48 @@
Loading
138 138
    # unexpanded_ind = [keys(t.unexpanded_children)...][1]
139 139
140 140
    n = DryRunTree()
141 -
    t = D3Tree(n, max_expand_depth=0)
142 -
    unexpanded_ind=1
143 -
    
141 +
    t = D3Tree(n, lazy_expand_after_depth=0)
142 +
    unexpanded_ind = 1
143 +
144 144
    div_id = "treevisDryRun"
145 -
    tree_data = Dict(div_id=>t)
145 +
    tree_data = Dict(div_id => t)
146 146
    HTTP.register!(SERVERS[port].router, "GET", "/api/d3trees/v1/dryrun/{treediv}/{nodeid}", req -> handle_subtree_request(req, tree_data, 1))
147 147
    HTTP.get("http://localhost:$(port)/api/d3trees/v1/dryrun/$div_id/$unexpanded_ind")
148 148
end
149 149
150 150
151 151
const DEFAULT_LAZY_SUBTREE_DEPTH = 2
152 -
const DEFAULT_PORT =16370
152 +
const DEFAULT_PORT = 16370
153 153
const API_PATH = "api/d3trees/v1/tree"
154 154
155 155
"""
156 156
    Start serving tree from existing or new server on some port. Returns port for the server.
157 157
"""
158 -
function serve_tree!(servers::Dict{<:Integer, D3TreeServer}, t::D3Tree, div::String)
158 +
function serve_tree!(servers::Dict{<:Integer,D3TreeServer}, t::D3Tree, div::String)
159 159
    port = get(t.options, :port, DEFAULT_PORT)
160 160
    lazy_subtree_depth = get(t.options, :lazy_subtree_depth, DEFAULT_LAZY_SUBTREE_DEPTH)
161 161
162 162
    # if server on this port is not yet running, start it.
163 -
    if !haskey(servers, port) 
163 +
    if !haskey(servers, port)
164 164
        d3server = reset_server!(port)
165 165
        # speedup first-click response in the visualization
166 166
        get(t.options, :dry_run_lazy_vizualization, t -> dry_run_server(port, t))(t)
167 167
    else
168 168
        d3server = servers[port]
169 169
    end
170 -
    
171 -
    d3server.tree_data[div] = t 
170 +
171 +
    d3server.tree_data[div] = t
172 172
    HTTP.register!(servers[port].router, "GET", "/$API_PATH/{treediv}/{nodeid}", req -> handle_subtree_request(req, d3server.tree_data, lazy_subtree_depth))
173 173
    return port
174 174
end
175 175
176 176
"""
177 177
    serror(error::Exception) -> String
178 +
    
178 179
Get error and stacktrace as String, e.g. for use in warning. 
179 180
Useful in Jypyter notebooks where error messages are not displayed correctly (https://github.com/JuliaLang/IJulia.jl/issues/1043)
180 -
Example:
181 +
182 +
# Example
181 183
```julia
182 184
julia> try
183 185
           a=b
@@ -205,6 +207,6 @@
Loading
205 207
"""
206 208
function serror(error::Exception)
207 209
    error_msg = sprint(showerror, error)
208 -
    st = sprint((io,v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
210 +
    st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
209 211
    return "$error_msg\n$st"
210 212
end

@@ -29,7 +29,7 @@
Loading
29 29
end
30 30
31 31
"""
32 -
    D3Tree(node; detect_repeat=true, max_expand_depth=typemax(Int), kwargs...)
32 +
    D3Tree(node; detect_repeat=true, lazy_expand_after_depth=typemax(Int), kwargs...)
33 33
34 34
Construct a tree to be displayed using D3 in a browser or ipython notebook with any object, `node`, that implements the AbstractTrees interface. 
35 35
@@ -41,9 +41,11 @@
Loading
41 41
D3Trees.link_style(node)
42 42
```
43 43
44 -
Allows for lazy loading of large trees through the `max_expand_depth` keyword argument, which sets the depth to which nodes are expanded. When visualizing a tree with unexpanded nodes, julia server is started to expand them on demand and serve them to the D3 visualizaion. 
45 -
    
46 -
The server does not have information about the lifetime of different visualizations so it might keep references to past visualizations, potentially holding up a lot of memory. To reset the server and remove references to old data, run `D3Trees.reset_server()` to reset everythingh manually.
44 +
Allows for lazy loading of large trees through the `lazy_expand_after_depth` keyword argument. Nodes beyonnd this depth are not intially expanded for visualization. 
45 +
Instead, they are cached and only expanded when requested by the visualization. The serving is done by the D3Trees HTTP server.
46 +
47 +
The server does not have information about the lifetime of different visualizations so it might keep references to past visualizations, 
48 +
potentially holding up a lot of memory. To reset the server and remove references to old data, run either `D3Trees.reset_server()` or `D3Trees.shutdown_server()`.
47 49
48 50
# Arguments
49 51
@@ -54,18 +56,21 @@
Loading
54 56
## Keyword
55 57
56 58
- `detect_repeat`: if true, uses a dictionary to detect whether a node has appeared previously
57 -
- `max_expand_depth::Integer`: default typemax(Int). Sets tree depth to at which `AbstractTrees.children(node)` will not be called anymore. Instead, nodes at this depth are cached and `children(node)` is called only when node is clicked in the D3 interactive visualization. Root has depth 0, so setting `max_expand_depth=1` expands only the root.
59 +
- `lazy_expand_after_depth::Integer`: default 20. Sets tree depth to at which `AbstractTrees.children(node)` will not be called anymore. Instead, nodes at this depth are cached and `children(node)` is called only when node is clicked in the D3 interactive visualization. Root has depth 0, so setting `lazy_expand_after_depth=1` expands only the root.
60 +
- `port::Integer` - (default: 16370) specify server port for D3Trees server that will serve subtrees for visualization. Shutdown server by `shutdown_server(port)`.
61 +
- `lazy_subtree_depth::Integer` - (default: 2) sets depth of subtrees fetched from D3Trees server
62 +
- `dry_run_lazy_vizualization::Function` - (default: t -> D3Trees.dry_run_server(port, t)) function that is ran once before visualization is started to speed up first fetch in the visualization. Provide custom function if your tree's children method takes a long time on first run.
58 63
- Also supports, the non-vector arguments of the vector-of-vectors `D3Tree` constructor, i.e. `title`, `init_expand`, `init_duration`, `svg_height`.
59 64
"""
60 -
function D3Tree(node; detect_repeat::Bool=true, max_expand_depth::Integer=typemax(Int), kwargs...)
65 +
function D3Tree(node; detect_repeat::Bool=true, lazy_expand_after_depth::Integer=20, kwargs...)
61 66
62 67
    t = D3Tree(Vector{Int}[]; kwargs...)
63 68
64 69
    if detect_repeat
65 70
        node_dict = Dict{Any,Int}()
66 -
        push_node!(t, node, max_expand_depth, node_dict)
71 +
        push_node!(t, node, lazy_expand_after_depth, node_dict)
67 72
    else
68 -
        push_node!(t, node, max_expand_depth)
73 +
        push_node!(t, node, lazy_expand_after_depth)
69 74
    end
70 75
    return t
71 76
end
@@ -95,9 +100,6 @@
Loading
95 100
- `init_expand::Integer` - levels to expand initially.
96 101
- `init_duration::Number` - duration of the initial animation in ms.
97 102
- `svg_height::Number` - height of the svg containing the tree in px.
98 -
- `port::Integer` - specify server port for D3Trees server that will serve subtrees for visualization. Shutdown server by `shutdown_server(port)`.
99 -
- `lazy_subtree_depth::Integer` - (default: 2) sets depth of subtrees fetched from D3Trees server
100 -
- `dry_run_lazy_vizualization::Function` - (default: t -> D3Trees.dry_run_server(port, t)) function that is ran once before visualization is started to speed up first fetch in the visualization. Provide custom function if your tree's children method takes a long time on first run.
101 103
"""
102 104
function D3Tree(children::AbstractVector{<:AbstractVector}; kwargs...)
103 105
    kwd = Dict(kwargs)
@@ -168,7 +170,7 @@
Loading
168 170
"""
169 171
DFS add node to the D3Tree structure
170 172
"""
171 -
function push_node!(t::D3Tree, node, max_expand_depth::Int, node_dict=nothing)
173 +
function push_node!(t::D3Tree, node, lazy_expand_after_depth::Int, node_dict=nothing)
172 174
    if !(node_dict === nothing) && haskey(node_dict, node)
173 175
        return node_dict[node]
174 176
    end
@@ -184,9 +186,9 @@
Loading
184 186
    push!(t.style, style(node))
185 187
    push!(t.link_style, link_style(node))
186 188
187 -
    if max_expand_depth > 0
189 +
    if lazy_expand_after_depth > 0
188 190
        for c in children(node)
189 -
            c_ind = push_node!(t, c, max_expand_depth - 1, node_dict)
191 +
            c_ind = push_node!(t, c, lazy_expand_after_depth - 1, node_dict)
190 192
            push!(t.children[ind], c_ind)
191 193
        end
192 194
    else
@@ -224,13 +226,13 @@
Loading
224 226
"""
225 227
Calculate missing children, for use with the D3Trees server for lazyly fetching data.
226 228
"""
227 -
function expand_node!(t::D3Tree, ind::Int, max_expand_depth::Int)
229 +
function expand_node!(t::D3Tree, ind::Int, lazy_expand_after_depth::Int)
228 230
    # TODO: missing handling of caching of repeated nodes
229 231
    @assert haskey(t.unexpanded_children, ind) "Node at index $ind is already expanded!"
230 232
    node = pop!(t.unexpanded_children, ind)
231 233
232 234
    # TODO: also pass other options from t?
233 -
    subtree = D3Tree(node; max_expand_depth=max_expand_depth)
235 +
    subtree = D3Tree(node; lazy_expand_after_depth=lazy_expand_after_depth)
234 236
    offset = length(t.children) - 1
235 237
236 238
    offset_subtree = D3OffsetSubtree(ind, subtree, offset)
Files Coverage
src 86.34%
Project Totals (5 files) 86.34%
codecov-umbrella
Build #2677357381 -
unittests
codecov-umbrella
Build #2677357381 -
unittests
codecov-umbrella
Build #2677357381 -
unittests
codecov-umbrella
Build #2677357381 -
unittests
codecov-umbrella
Build #2677357381 -
unittests
codecov-umbrella
Build #2677357381 -
unittests

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading