diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 34b16a6..ebe4a66 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,6 +2,7 @@ name: CI
on:
push:
branches: [master]
+ tags: '*'
pull_request:
types: [opened, synchronize, reopened]
jobs:
diff --git a/Project.toml b/Project.toml
index 375b006..3c06ab2 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,6 +1,6 @@
name = "MetaGraphs"
uuid = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
-version = "0.7.2"
+version = "0.8.0"
[deps]
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
@@ -9,7 +9,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[compat]
Graphs = "1.4"
-JLD2 = "0.1.11, 0.2, 0.3, 0.4"
+JLD2 = "0.1.11, 0.2, 0.3, 0.4, 0.5"
julia = "1"
[extras]
diff --git a/README.md b/README.md
index b2a2a2f..9d8ce3e 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,26 @@
# MetaGraphs
-[![Build Status][build-img]][build-url]
-[![Codecov branch][codecov-img]][codecov-url]
-[](https://juliagraphs.org/MetaGraphs.jl/dev/)
+[](https://juliagraphs.org/MetaGraphs.jl/)
+[](https://juliagraphs.org/MetaGraphs.jl/dev/)
+[](https://github.com/JuliaGraphs/Metagraphs.jl/actions?query=workflow%3ACI)
+[](http://codecov.io/github/JuliaGraphs/Metagraphs.jl?branch=master)
-[MetaGraphs.jl](https://github.com/JuliaGraphs/MetaGraphs.jl) graphs with arbitrary metadata.
+A flexible package for graphs with arbitrary metadata.
-[build-img]: https://github.com/JuliaGraphs/Metagraphs.jl/workflows/CI/badge.svg?branch=master
-[build-url]: https://github.com/JuliaGraphs/Metagraphs.jl/actions?query=workflow%3ACI
-[codecov-img]: http://codecov.io/github/JuliaGraphs/Metagraphs.jl/coverage.svg?branch=master
-[codecov-url]: http://codecov.io/github/JuliaGraphs/Metagraphs.jl?branch=master
+> For a more performant option, check out [MetaGraphsNext.jl](https://github.com/JuliaGraphs/MetaGraphsNext.jl)
-## Documentation
-Full documentation is available at [GitHub Pages](https://juliagraphs.github.io/MetaGraphs.jl/latest).
-Documentation for methods is also available via the Julia REPL help system.
+## Getting started
-## Installation
Installation is straightforward: from the Julia `pkg` prompt,
```julia
-add MetaGraphs
+pkg> add MetaGraphs
```
-## Example Usage
+Full documentation is available [here](https://juliagraphs.org/MetaGraphs.jl/).
+Documentation for methods is also available via the Julia REPL help system.
+
+## Example use
+
```julia
julia> using Graphs, MetaGraphs
@@ -72,6 +71,10 @@ Dict{Symbol,Any} with 2 entries:
julia> get_prop(mg, 2, :name)
"John"
+# set a default value to return in case the property does not exist
+julia> get_prop(mg, 2, :nonexistent_prop, "default value")
+"default value"
+
# delete a specific property
julia> rem_prop!(mg, 1, :name)
Dict{Symbol,Any} with 1 entry:
diff --git a/src/MetaGraphs.jl b/src/MetaGraphs.jl
index 7eddade..06c3b3a 100644
--- a/src/MetaGraphs.jl
+++ b/src/MetaGraphs.jl
@@ -74,7 +74,7 @@ has_vertex(g::AbstractMetaGraph, x...) = has_vertex(g.graph, x...)
inneighbors(g::AbstractMetaGraph, v::Integer) = inneighbors(g.graph, v)
outneighbors(g::AbstractMetaGraph, v::Integer) = fadj(g.graph, v)
-issubset(g::T, h::T) where T <: AbstractMetaGraph = issubset(g.graph, h.graph)
+issubset(g::T, h::T) where {T<:AbstractMetaGraph} = issubset(g.graph, h.graph)
"""
add_edge!(g, u, v, s, val)
@@ -135,10 +135,10 @@ function rem_vertex!(g::AbstractMetaGraph, v::Integer)
lasteoutprops = Dict(n => props(g, lastv, n) for n in outneighbors(g, lastv))
lasteinprops = Dict(n => props(g, n, lastv) for n in inneighbors(g, lastv))
for ind in g.indices
- if haskey(props(g,lastv),ind)
+ if haskey(props(g, lastv), ind)
pop!(g.metaindex[ind], get_prop(g, lastv, ind))
end
- if haskey(props(g,v),ind)
+ if haskey(props(g, v), ind)
v != lastv && pop!(g.metaindex[ind], get_prop(g, v, ind))
end
end
@@ -191,7 +191,7 @@ function rem_vertex!(g::AbstractMetaGraph, v::Integer)
return true
end
-struct MetaWeights{T <: Integer,U <: Real} <: AbstractMatrix{U}
+struct MetaWeights{T<:Integer,U<:Real} <: AbstractMatrix{U}
n::T
weightfield::Symbol
defaultweight::U
@@ -203,7 +203,7 @@ show(io::IO, z::MIME"text/plain", x::MetaWeights) = show(io, x)
MetaWeights(g::AbstractMetaGraph) = MetaWeights{eltype(g),eltype(g.defaultweight)}(nv(g), g.weightfield, g.defaultweight, g.eprops, is_directed(g))
-function getindex(w::MetaWeights{T,U}, u::Integer, v::Integer)::U where T <: Integer where U <: Real
+function getindex(w::MetaWeights{T,U}, u::Integer, v::Integer)::U where {T<:Integer} where {U<:Real}
_e = Edge(u, v)
e = !w.directed && !Graphs.is_ordered(_e) ? reverse(_e) : _e
!haskey(w.eprops, e) && return w.defaultweight
@@ -253,20 +253,33 @@ props(g::AbstractMetaGraph, v::Integer) = get(PropDict, g.vprops, v)
props(g::AbstractMetaGraph, u::Integer, v::Integer) = props(g, Edge(u, v))
"""
- get_prop(g, prop)
- get_prop(g, v, prop)
- get_prop(g, e, prop)
- get_prop(g, s, d, prop)
+ get_prop(g, prop::Symbol)
+ get_prop(g, prop::Symbol, default)
+
+ get_prop(g, v, prop::Symbol)
+ get_prop(g, v, prop::Symbol, default)
+
+ get_prop(g, e, prop::Symbol)
+ get_prop(g, e, prop::Symbol, default)
+ get_prop(g, s, d, prop::Symbol)
+ get_prop(g, s, d, prop::Symbol, default)
Return the property `prop` defined for graph `g`, vertex `v`, or edge `e`
(optionally referenced by source vertex `s` and destination vertex `d`).
-If property is not defined, return an error.
+Use the version with `default`, to return a default value if the property is not defined. Otherwise, it will return an error.
"""
get_prop(g::AbstractMetaGraph, prop::Symbol) = props(g)[prop]
+get_prop(g::AbstractMetaGraph, prop::Symbol, default) = get(props(g), prop, default)
+
get_prop(g::AbstractMetaGraph, v::Integer, prop::Symbol) = props(g, v)[prop]
+get_prop(g::AbstractMetaGraph, v::Integer, prop::Symbol, default) = get(props(g, v), prop, default)
+
get_prop(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol) = props(g, e)[prop]
+get_prop(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol, default) = get(props(g, e), prop, default)
get_prop(g::AbstractMetaGraph, u::Integer, v::Integer, prop::Symbol) = get_prop(g, Edge(u, v), prop)
+get_prop(g::AbstractMetaGraph, u::Integer, v::Integer, prop::Symbol, default) = get_prop(g, Edge(u, v), prop, default)
+
"""
has_prop(g, prop)
@@ -300,7 +313,7 @@ end
function set_props!(g::AbstractMetaGraph, v::Integer, d::Dict)
if has_vertex(g, v)
- for (prop,val) in d
+ for (prop, val) in d
index_available(g, v, prop, val) || error("':$prop' index already contains $val")
end
if !_hasdict(g, v)
@@ -308,7 +321,7 @@ function set_props!(g::AbstractMetaGraph, v::Integer, d::Dict)
else
merge!(g.vprops[v], d)
end
- for prop in intersect(keys(d), g.indices)
+ for prop in intersect(keys(d), g.indices)
g.metaindex[prop][d[prop]] = v
end
return true
@@ -317,7 +330,7 @@ function set_props!(g::AbstractMetaGraph, v::Integer, d::Dict)
end
# set_props!(g::AbstractMetaGraph, e::SimpleEdge, d::Dict) is dependent on directedness.
-set_props!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, d::Dict) where T = set_props!(g, Edge(T(u), T(v)), d)
+set_props!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, d::Dict) where {T} = set_props!(g, Edge(T(u), T(v)), d)
"""
set_prop!(g, prop, val)
@@ -339,7 +352,7 @@ set_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val) = begin
end
set_prop!(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol, val) = set_props!(g, e, Dict(prop => val))
-set_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol, val) where T = set_prop!(g, Edge(T(u), T(v)), prop, val)
+set_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol, val) where {T} = set_prop!(g, Edge(T(u), T(v)), prop, val)
"""
rem_prop!(g, prop)
@@ -355,7 +368,7 @@ rem_prop!(g::AbstractMetaGraph, prop::Symbol) = delete!(g.gprops, prop)
rem_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol) = delete!(g.vprops[v], prop)
rem_prop!(g::AbstractMetaGraph, e::SimpleEdge, prop::Symbol) = delete!(g.eprops[e], prop)
-rem_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol) where T = rem_prop!(g, Edge(T(u), T(v)), prop)
+rem_prop!(g::AbstractMetaGraph{T}, u::Integer, v::Integer, prop::Symbol) where {T} = rem_prop!(g, Edge(T(u), T(v)), prop)
"""
default_index_value(v, prop, index_values; exclude=nothing)
@@ -416,7 +429,7 @@ function set_indexing_prop!(g::AbstractMetaGraph, v::Integer, prop::Symbol, val:
haskey(g.metaindex[prop], val) && error("':$prop' index already contains $val")
if !haskey(g.vprops, v)
- push!(g.vprops, v=>Dict{Symbol,Any}())
+ push!(g.vprops, v => Dict{Symbol,Any}())
end
if haskey(g.vprops[v], prop)
delete!(g.metaindex[prop], g.vprops[v][prop])
@@ -438,7 +451,7 @@ clear_props!(g::AbstractMetaGraph, v::Integer) = _hasdict(g, v) && delete!(g.vpr
clear_props!(g::AbstractMetaGraph, e::SimpleEdge) = _hasdict(g, e) && delete!(g.eprops, e)
clear_props!(g::AbstractMetaGraph) = g.gprops = PropDict()
-clear_props!(g::AbstractMetaGraph{T}, u::Integer, v::Integer) where T = clear_props!(g, Edge(T(u), T(v)))
+clear_props!(g::AbstractMetaGraph{T}, u::Integer, v::Integer) where {T} = clear_props!(g, Edge(T(u), T(v)))
"""
weightfield!(g, prop)
@@ -514,7 +527,7 @@ filter_vertices(g::AbstractMetaGraph, prop::Symbol) =
filter_vertices(g::AbstractMetaGraph, prop::Symbol, val) =
filter_vertices(g, (g, x) -> has_prop(g, x, prop) && get_prop(g, x, prop) == val)
-function _copy_props!(oldg::T, newg::T, vmap) where T <: AbstractMetaGraph
+function _copy_props!(oldg::T, newg::T, vmap) where {T<:AbstractMetaGraph}
for (newv, oldv) in enumerate(vmap)
p = props(oldg, oldv)
if !isempty(p)
@@ -540,21 +553,21 @@ function _copy_props!(oldg::T, newg::T, vmap) where T <: AbstractMetaGraph
return nothing
end
-function induced_subgraph(g::T, v::AbstractVector{U}) where T <: AbstractMetaGraph where U <: Integer
+function induced_subgraph(g::T, v::AbstractVector{U}) where {T<:AbstractMetaGraph} where {U<:Integer}
inducedgraph, vmap = induced_subgraph(g.graph, v)
newg = T(inducedgraph)
_copy_props!(g, newg, vmap)
return newg, vmap
end
-function induced_subgraph(g::T, v::AbstractVector{U}) where T <: AbstractMetaGraph where U <: SimpleEdge
+function induced_subgraph(g::T, v::AbstractVector{U}) where {T<:AbstractMetaGraph} where {U<:SimpleEdge}
inducedgraph, vmap = induced_subgraph(g.graph, v)
newg = T(inducedgraph)
_copy_props!(g, newg, vmap)
return newg, vmap
end
-induced_subgraph(g::T, filt::Iterators.Filter) where T <: AbstractMetaGraph =
+induced_subgraph(g::T, filt::Iterators.Filter) where {T<:AbstractMetaGraph} =
induced_subgraph(g, collect(filt))
# TODO - would be nice to be able to apply a function to properties. Not sure
@@ -563,7 +576,7 @@ induced_subgraph(g::T, filt::Iterators.Filter) where T <: AbstractMetaGraph =
==(x::AbstractMetaGraph, y::AbstractMetaGraph) = x.graph == y.graph
-copy(g::T) where T <: AbstractMetaGraph = deepcopy(g)
+copy(g::T) where {T<:AbstractMetaGraph} = deepcopy(g)
include("metadigraph.jl")
include("metagraph.jl")
diff --git a/src/persistence.jl b/src/persistence.jl
index 709c308..7f25788 100644
--- a/src/persistence.jl
+++ b/src/persistence.jl
@@ -16,6 +16,26 @@ end
loadgraph(fn::AbstractString, gname::String, ::MGFormat) = loadmg(fn)
savegraph(fn::AbstractString, g::AbstractMetaGraph) = savemg(fn, g)
+# escaping unescaped quotation marks
+# i.e. replacing `"`` with `\"` while leaving `\"` as is
+escape_quotes(s::AbstractString) = replace(s, r"([^\\])\"" => s"\1\\\\\"")
+
+# According to the DOT language specification https://graphviz.org/doc/info/lang.html
+# we can quote everyhthing that's not an XML/HTML literal
+function quote_prop(p::AbstractString)
+ if occursin(r"<+.*>+$", p)
+ # The label is an HTML string, no additional quotes here.
+ return p
+ else
+ return "\"" * escape_quotes(p) * "\""
+ end
+end
+# if the property value is _not_ a string it cannot be XML/HTML literal, so just put it in quotes
+quote_prop(p::Any) = "\"" * escape_quotes(string(p)) * "\""
+# NOTE: down there I only quote property _values_. DOT allows quoting property _names_ too
+# I don't do that as long as names are Symbols and can't have spaces and commas and stuff.
+# That will break if someone uses a DOT keyword as a property name, as they must be quoted.
+
function savedot(io::IO, g::AbstractMetaGraph)
if is_directed(g)
@@ -27,24 +47,18 @@ function savedot(io::IO, g::AbstractMetaGraph)
end
for p in props(g)
- write(io, "$(p[1])=$(p[2]);\n")
+ write(io, "$(p[1])=$(quote_prop(p[2]));\n")
end
for v in vertices(g)
write(io, "$v")
if length(props(g, v)) > 0
write(io, " [ ")
- end
- for p in props(g, v)
- key = p[1]
- if key .=== :label && occursin(r"<+.*>+$", p[2])
- # The label is an HTML string, no additional quotes here.
- write(io, "$key=$(p[2]),")
- else
- write(io, "$key=\"$(p[2])\",")
+
+ for p in props(g, v)
+ write(io, "$(p[1])=$(quote_prop(p[2])), ")
end
- end
- if length(props(g, v)) > 0
+
write(io, "];")
end
write(io, "\n")
@@ -53,7 +67,7 @@ function savedot(io::IO, g::AbstractMetaGraph)
for e in edges(g)
write(io, "$(src(e)) $dash $(dst(e)) [ ")
for p in props(g,e)
- write(io, "$(p[1])=$(p[2]), ")
+ write(io, "$(p[1])=$(quote_prop(p[2])), ")
end
write(io, "]\n")
end
diff --git a/test/diagram_ref.dot b/test/diagram_ref.dot
index 0814c2a..1faaaf7 100644
--- a/test/diagram_ref.dot
+++ b/test/diagram_ref.dot
@@ -1,56 +1,56 @@
digraph G {
-pack=true;
-1 [ color="orange",style="filled",penwidth="2.0",fillcolor="#dddddd",name="weather",label="NOAA\nWeather",shape="record",];
-2 [ color="orange",style="filled",penwidth="2.0",fillcolor="#dddddd",name="cost",label="Vaccination\nCost",shape="record",];
-3 [ color="orange",style="filled",penwidth="2.0",fillcolor="#dddddd",name="demo",label="Census\nDemographics",shape="record",];
-4 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="fed",label="Fed Forcast",shape="record",];
-5 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="epi",label="SIR",shape="record",];
-6 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="ode",label="ODE Solver",shape="record",];
-7 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="rate",label="{Transition\nRate}",shape="record",];
-8 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="unit",label="Unit",shape="record",];
-9 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="personper",label="Person/s",shape="record",];
-10 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="person",label="Person",shape="record",];
-11 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="second",label="second (s)",shape="record",];
-12 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="dollars",label="$",shape="record",];
-13 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="inf",label="Infection\nRate",shape="record",];
-14 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="mort",label="Mortality\nRate",shape="record",];
-15 [ color="#66AA55",style="filled",penwidth="2.0",fillcolor="#dddddd",name="birth",label="Birth\nRate",shape="record",];
-16 [ color="#DD1133",style="filled",penwidth="2.0",fillcolor="#dddddd",name="twenty",label="0.2 Persons/s",shape="record",];
-17 [ color="#DD1133",style="filled",penwidth="2.0",fillcolor="#dddddd",name="thirty",label="0.3 Persons/s",shape="record",];
-18 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="ind",label="Individual\nContact\nModel",shape="record",];
-19 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="temp",label="Temperature",shape="record",];
-20 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="age",label="Age",shape="record",];
-21 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="dGDP",label="Economic Growth",shape="record",];
-22 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="cases",label="Flu\nCases",shape="record",];
-23 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="prices",label="Vacc\nPrice",shape="record",];
-24 [ color="#5DADE2",style="filled",penwidth="2.0",fillcolor="#dddddd",name="regres",label="Regression",shape="record",];
-25 [ color="#000000",style="filled",penwidth="2.0",fillcolor="#dddddd",name="html",label=<Title
Some text.>,shape="record",];
-1 -> 19 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-3 -> 15 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-3 -> 20 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-4 -> 21 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-5 -> 22 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-6 -> 5 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-7 -> 9 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-9 -> 8 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-10 -> 8 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-11 -> 8 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-12 -> 2 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-12 -> 8 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-13 -> 7 [ color=black, dir=none, penwidth=2.0, style=dashed, ]
-13 -> 18 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-14 -> 7 [ color=black, dir=none, penwidth=2.0, style=dashed, ]
-14 -> 18 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-15 -> 7 [ color=black, dir=none, penwidth=2.0, style=dashed, ]
-15 -> 18 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-16 -> 14 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-17 -> 13 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-18 -> 5 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-19 -> 13 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-20 -> 14 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-21 -> 23 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-22 -> 24 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-23 -> 24 [ color=missing, dir=none, penwidth=missing, style=missing, ]
-24 -> 2 [ color=orange, dir=none, penwidth=4.0, style=solid, ]
-25 -> 4 [ color=black, dir=none, penwidth=2.0, style=solid, ]
+pack="true";
+1 [ color="orange", style="filled", penwidth="2.0", fillcolor="#dddddd", name="weather", label="NOAA\nWeather", shape="record", ];
+2 [ color="orange", style="filled", penwidth="2.0", fillcolor="#dddddd", name="cost", label="Vaccination\nCost", shape="record", ];
+3 [ color="orange", style="filled", penwidth="2.0", fillcolor="#dddddd", name="demo", label="Census\nDemographics", shape="record", ];
+4 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="fed", label="Fed Forcast", shape="record", ];
+5 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="epi", label="SIR", shape="record", ];
+6 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="ode", label="ODE Solver", shape="record", ];
+7 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="rate", label="{Transition\nRate}", shape="record", ];
+8 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="unit", label="Unit", shape="record", ];
+9 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="personper", label="Person/s", shape="record", ];
+10 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="person", label="Person", shape="record", ];
+11 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="second", label="second (s)", shape="record", ];
+12 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="dollars", label="$", shape="record", ];
+13 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="inf", label="Infection\nRate", shape="record", ];
+14 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="mort", label="Mortality\nRate", shape="record", ];
+15 [ color="#66AA55", style="filled", penwidth="2.0", fillcolor="#dddddd", name="birth", label="Birth\nRate", shape="record", ];
+16 [ color="#DD1133", style="filled", penwidth="2.0", fillcolor="#dddddd", name="twenty", label="0.2 Persons/s", shape="record", ];
+17 [ color="#DD1133", style="filled", penwidth="2.0", fillcolor="#dddddd", name="thirty", label="0.3 Persons/s", shape="record", ];
+18 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="ind", label="Individual\n\"Contact\"\nModel", shape="record", ];
+19 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="temp", label="Temperature", shape="record", ];
+20 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="age", label="Age", shape="record", ];
+21 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="dGDP", label="Economic Growth", shape="record", ];
+22 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="cases", label="Flu\nCases", shape="record", ];
+23 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="prices", label="Vacc\nPrice", shape="record", ];
+24 [ color="#5DADE2", style="filled", penwidth="2.0", fillcolor="#dddddd", name="regres", label="Regression", shape="record", ];
+25 [ color="#000000", style="filled", penwidth="2.0", fillcolor="#dddddd", name="html", label=<Title
Some text.>, shape="record", ];
+1 -> 19 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+3 -> 15 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+3 -> 20 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+4 -> 21 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+5 -> 22 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+6 -> 5 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+7 -> 9 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+9 -> 8 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+10 -> 8 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+11 -> 8 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+12 -> 2 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+12 -> 8 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+13 -> 7 [ color="black", dir="none", penwidth="2.0", label="", style="dashed", ]
+13 -> 18 [ color="orange", dir="none", penwidth="4.0", label="one, other", style="solid", ]
+14 -> 7 [ color="black", dir="none", penwidth="2.0", label="", style="dashed", ]
+14 -> 18 [ color="orange", dir="none", penwidth="4.0", label="cause", style="solid", ]
+15 -> 7 [ color="black", dir="none", penwidth="2.0", label="", style="dashed", ]
+15 -> 18 [ color="orange", dir="none", penwidth="4.0", label="one, other, and third one", style="solid", ]
+16 -> 14 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+17 -> 13 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+18 -> 5 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+19 -> 13 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+20 -> 14 [ color="orange", dir="none", penwidth="4.0", label="", style="solid", ]
+21 -> 23 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+22 -> 24 [ color="orange", dir="none", penwidth="4.0", label="with single \" here", style="solid", ]
+23 -> 24 [ color="missing", dir="none", penwidth="missing", label="", style="missing", ]
+24 -> 2 [ color="orange", dir="none", penwidth="4.0", label="with \"quoted\" word", style="solid", ]
+25 -> 4 [ color="black", dir="none", penwidth="2.0", label="", style="solid", ]
}
diff --git a/test/dotformat.jl b/test/dotformat.jl
index fad114a..71a5b9b 100644
--- a/test/dotformat.jl
+++ b/test/dotformat.jl
@@ -3,35 +3,35 @@ using Test
using Graphs
property_set=[
- (src="thirty",dst="inf",color="missing",penwidth="missing",style="missing",),
- (src="twenty",dst="mort",color="missing",penwidth="missing",style="missing",),
- (src="rate",dst="personper",color="missing",penwidth="missing",style="missing",),
- (src="person",dst="unit",color="missing",penwidth="missing",style="missing",),
- (src="personper",dst="unit",color="missing",penwidth="missing",style="missing",),
- (src="second",dst="unit",color="missing",penwidth="missing",style="missing",),
- (src="dollars",dst="unit",color="missing",penwidth="missing",style="missing",),
- (src="dGDP",dst="prices",color="missing",penwidth="missing",style="missing",),
- (src="prices",dst="regres",color="missing",penwidth="missing",style="missing",),
- (src="fed",dst="dGDP",color="missing",penwidth="missing",style="missing",),
- (src="ode",dst="epi",color="missing",penwidth="missing",style="missing",),
- (src="dollars",dst="cost",color="missing",penwidth="missing",style="missing",),
- (src="mort",dst="rate",color="black",penwidth="2.0",style="dashed",),
- (src="inf",dst="rate",color="black",penwidth="2.0",style="dashed",),
- (src="birth",dst="rate",color="black",penwidth="2.0",style="dashed",),
- (src="mort",dst="ind",color="orange",penwidth="4.0",style="solid",),
- (src="ind",dst="epi",color="orange",penwidth="4.0",style="solid",),
- (src="ind",dst="epi",color="orange",penwidth="4.0",style="solid",),
- (src="inf",dst="ind",color="orange",penwidth="4.0",style="solid",),
- (src="birth",dst="ind",color="orange",penwidth="4.0",style="solid",),
- (src="temp",dst="inf",color="orange",penwidth="4.0",style="solid",),
- (src="age",dst="mort",color="orange",penwidth="4.0",style="solid",),
- (src="demo",dst="birth",color="orange",penwidth="4.0",style="solid",),
- (src="epi",dst="cases",color="orange",penwidth="4.0",style="solid",),
- (src="cases",dst="regres",color="orange",penwidth="4.0",style="solid",),
- (src="weather",dst="temp",color="orange",penwidth="4.0",style="solid",),
- (src="demo",dst="age",color="orange",penwidth="4.0",style="solid",),
- (src="regres",dst="cost",color="orange",penwidth="4.0",style="solid",),
- (src="html",dst="fed",color="black",penwidth="2.0",style="solid",)
+ (src="thirty",dst="inf",color="missing",penwidth="missing",style="missing",label=""),
+ (src="twenty",dst="mort",color="missing",penwidth="missing",style="missing",label="",),
+ (src="rate",dst="personper",color="missing",penwidth="missing",style="missing",label="",),
+ (src="person",dst="unit",color="missing",penwidth="missing",style="missing",label="",),
+ (src="personper",dst="unit",color="missing",penwidth="missing",style="missing",label="",),
+ (src="second",dst="unit",color="missing",penwidth="missing",style="missing",label="",),
+ (src="dollars",dst="unit",color="missing",penwidth="missing",style="missing",label="",),
+ (src="dGDP",dst="prices",color="missing",penwidth="missing",style="missing",label="",),
+ (src="prices",dst="regres",color="missing",penwidth="missing",style="missing",label="",),
+ (src="fed",dst="dGDP",color="missing",penwidth="missing",style="missing",label="",),
+ (src="ode",dst="epi",color="missing",penwidth="missing",style="missing",label="",),
+ (src="dollars",dst="cost",color="missing",penwidth="missing",style="missing",label="",),
+ (src="mort",dst="rate",color="black",penwidth="2.0",style="dashed",label="",),
+ (src="inf",dst="rate",color="black",penwidth="2.0",style="dashed",label="",),
+ (src="birth",dst="rate",color="black",penwidth="2.0",style="dashed",label="",),
+ (src="mort",dst="ind",color="orange",penwidth="4.0",style="solid",label="cause",),
+ (src="ind",dst="epi",color="orange",penwidth="4.0",style="solid",label="two words",),
+ (src="ind",dst="epi",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="inf",dst="ind",color="orange",penwidth="4.0",style="solid",label="one, other",),
+ (src="birth",dst="ind",color="orange",penwidth="4.0",style="solid",label="one, other, and third one",),
+ (src="temp",dst="inf",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="age",dst="mort",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="demo",dst="birth",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="epi",dst="cases",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="cases",dst="regres",color="orange",penwidth="4.0",style="solid",label="with single \" here",),
+ (src="weather",dst="temp",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="demo",dst="age",color="orange",penwidth="4.0",style="solid",label="",),
+ (src="regres",dst="cost",color="orange",penwidth="4.0",style="solid",label="with \"quoted\" word",),
+ (src="html",dst="fed",color="black",penwidth="2.0",style="solid",label="",)
]
# name, label, color
@@ -53,7 +53,7 @@ vprops = [
("birth","Birth\\nRate","#66AA55"),
("twenty","0.2 Persons/s","#DD1133"),
("thirty","0.3 Persons/s","#DD1133"),
- ("ind","Individual\\nContact\\nModel","#5DADE2"),
+ ("ind","Individual\\n\"Contact\"\\nModel","#5DADE2"),
("temp","Temperature","#5DADE2"),
("age","Age","#5DADE2"),
("dGDP","Economic Growth","#5DADE2"),
@@ -79,17 +79,18 @@ set_indexing_prop!(g, :name)
# add edges
for prop in property_set
src, dst = g[prop.src, :name], g[prop.dst, :name]
- add_edge!(g, src,dst)
+ add_edge!(g, src, dst)
set_prop!(g, src, dst, :color, prop.color)
set_prop!(g, src, dst, :penwidth, prop.penwidth)
set_prop!(g, src, dst, :style, prop.style)
+ set_prop!(g, src, dst, :label, prop.label)
end
# set global edge properties
for e in edges(g)
set_prop!(g, e, :dir, :none)
end
-# set global vertex properties
+# set global vertex properties
for v in vertices(g)
set_prop!(g, v, :shape, :record)
set_prop!(g, v, :style, :filled)
@@ -112,7 +113,7 @@ end
# - <...> : OK
# - both : NOK ("< or >" as bounding characters, resulting HTML in file will not be parsed correctly by dot.)
quote_regex = r"label\s*=\s*\"(?:[^\"\\]|\\.)*\"" # source: https://stackoverflow.com/questions/249791/regex-for-quoted-string-with-escaping-quotes
- html_regex = r"label\s*=\s*<.*>"
+ html_regex = r"label\s*=\s*<.*>"
invalid_quote_regex = r"label\s*=\s*\"<(?:[^\"\\]|\\.)*>\""
for line in eachline(fp)
test_val = false
@@ -126,7 +127,7 @@ end
# no worries, proper HTML surrounding brackets found.
test_val = true
end
- @test test_val
+ @test test_val
end
end
diff --git a/test/metagraphs.jl b/test/metagraphs.jl
index a6bf948..59fc799 100644
--- a/test/metagraphs.jl
+++ b/test/metagraphs.jl
@@ -1,7 +1,7 @@
using MetaGraphs
import Graphs: SimpleGraphs
import Base64:
- stringmime
+ stringmime
@testset "MetaGraphs" begin
@@ -21,7 +21,7 @@ import Base64:
mg = MetaGraph()
@test add_vertex!(mg, :color, "red") && get_prop(mg, nv(mg), :color) == "red"
@test add_vertex!(mg, Dict(:color => "red", :prop2 => "prop2")) && props(mg, nv(mg)) == Dict(:color => "red", :prop2 => "prop2")
- @test add_edge!(mg, 1, 2, :color, "blue") && get_prop(mg, 1, 2, :color) == "blue"
+ @test add_edge!(mg, 1, 2, :color, "blue") && get_prop(mg, 1, 2, :color) == "blue"
@test add_vertex!(mg) && add_edge!(mg, 1, 3, Dict(:color => "red", :prop2 => "prop2")) && props(mg, 1, 3) == Dict(:color => "red", :prop2 => "prop2")
for g in testgraphs(gx)
@@ -78,6 +78,31 @@ import Base64:
U = @inferred(weighttype(mg))
@test @inferred(nv(MetaGraph{T,U}(6))) == 6
+ # get_prop with default argument
+ # vertices
+ set_prop!(mg, nv(mg), :testprop, "exists")
+ @test get_prop(mg, nv(mg), :testprop, "nonexistent") == "exists"
+ @test get_prop(mg, nv(mg), :testprop_nonexist, "nonexistent") == "nonexistent"
+ @test get_prop(mg, nv(mg) + 100, :testprop_nonexist, "nonexistent") == "nonexistent"
+
+ # edges
+ set_prop!(mg, 1, 2, :testedgeprop, "5 meters")
+ @test get_prop(mg, 1, 2, :testedgeprop, "0 meters") == "5 meters"
+ @test get_prop(mg, 1, 2, :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, 2, 4, :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, 2, 1, :testedgeprop, "0 meters") == "5 meters"
+ @test get_prop(mg, 2, 1, :testedgeprop_nonexist, "0 meters") == "0 meters"
+
+ @test get_prop(mg, Edge(1, 2), :testedgeprop, "0 meters") == "5 meters"
+ @test get_prop(mg, Edge(1, 2), :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, Edge(2, 4), :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, Edge(2, 1), :testedgeprop, "0 meters") == "5 meters"
+ @test get_prop(mg, Edge(2, 1), :testedgeprop_nonexist, "0 meters") == "0 meters"
+
+ # graph
+ set_prop!(mg, :testgraphpprop, "linegraph")
+ @test get_prop(mg, :testgraphpprop, "circlegraph") == "linegraph"
+ @test get_prop(mg, :testgraphpprop_nonexist, "circlegraph") == "circlegraph"
end
for g in testdigraphs(dgx)
@@ -135,6 +160,32 @@ import Base64:
T = @inferred(eltype(mg))
U = @inferred(weighttype(mg))
@test @inferred(nv(MetaDiGraph{T,U}(6))) == 6
+
+ # get_prop with default argument
+ # vertices
+ set_prop!(mg, nv(mg), :testprop, "exists")
+ @test get_prop(mg, nv(mg), :testprop, "nonexistent") == "exists"
+ @test get_prop(mg, nv(mg), :testprop_nonexist, "nonexistent") == "nonexistent"
+ @test get_prop(mg, nv(mg) + 100, :testprop_nonexist, "nonexistent") == "nonexistent"
+
+ # edges
+ set_prop!(mg, 1, 2, :testedgeprop, "5 meters")
+ @test get_prop(mg, 1, 2, :testedgeprop, "0 meters") == "5 meters"
+ @test get_prop(mg, 1, 2, :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, 2, 4, :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, 2, 1, :testedgeprop, "0 meters") == "0 meters"
+ @test get_prop(mg, 2, 1, :testedgeprop_nonexist, "0 meters") == "0 meters"
+
+ @test get_prop(mg, Edge(1, 2), :testedgeprop, "0 meters") == "5 meters"
+ @test get_prop(mg, Edge(1, 2), :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, Edge(2, 4), :testedgeprop_nonexist, "0 meters") == "0 meters"
+ @test get_prop(mg, Edge(2, 1), :testedgeprop, "0 meters") == "0 meters"
+ @test get_prop(mg, Edge(2, 1), :testedgeprop_nonexist, "0 meters") == "0 meters"
+
+ # graph
+ set_prop!(mg, :testgraphpprop, "linegraph")
+ @test get_prop(mg, :testgraphpprop, "circlegraph") == "linegraph"
+ @test get_prop(mg, :testgraphpprop_nonexist, "circlegraph") == "circlegraph"
end
for gbig in [SimpleGraph(0xff), SimpleDiGraph(0xff)]
@@ -299,7 +350,7 @@ import Base64:
@test weightfield!(mg, :weight) == :weight
@test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 2, 3]
- @test set_props!(mg, 1, 2, Dict(:color => :blue, :action => "knows"))
+ @test set_props!(mg, 1, 2, Dict(:color => :blue, :action => "knows"))
@test length(props(mg, 1, 2)) == 3
@test rem_edge!(mg, 1, 2)
@test length(props(mg, 1, 2)) == 0
@@ -352,12 +403,12 @@ import Base64:
for v in vertices(mga)
set_prop!(mga, v, :name, string(v))
end
- set_indexing_prop!(mga,:name)
+ set_indexing_prop!(mga, :name)
@test get_prop(mga, 1, :name) == "1"
@test get_prop(mga, 5, :name) == "5"
@test rem_vertex!(mga, 1)
@test get_prop(mga, 1, :name) == "5"
- @test mga["5",:name] == 1
+ @test mga["5", :name] == 1
@test isempty(props(mga, 5))
# test for #22
@@ -373,19 +424,19 @@ import Base64:
# test for #72 - Multiple indicies that are not all used
let
- test_graph = x-> begin
- g=MetaGraph()
- set_indexing_prop!(g,:IndexA)
- set_indexing_prop!(g,:IndexB)
- add_vertex!(g,:IndexA,"A")
- x && add_vertex!(g,:IndexA,"B")
- x && set_indexing_prop!(g,nv(g),:IndexB,"B")
- add_vertex!(g,:IndexB,"C")
+ test_graph = x -> begin
+ g = MetaGraph()
+ set_indexing_prop!(g, :IndexA)
+ set_indexing_prop!(g, :IndexB)
+ add_vertex!(g, :IndexA, "A")
+ x && add_vertex!(g, :IndexA, "B")
+ x && set_indexing_prop!(g, nv(g), :IndexB, "B")
+ add_vertex!(g, :IndexB, "C")
g
end
- mga=test_graph(true)
- rem_vertex!(mga,2)
- @test mga==test_graph(false)
+ mga = test_graph(true)
+ rem_vertex!(mga, 2)
+ @test mga == test_graph(false)
end
mga = MetaDiGraph(path_digraph(4))
@@ -482,7 +533,7 @@ end
@test MetaGraphs.index_available(dG, 7, :name, "dgnode_8-anothername") == true
@test_throws ErrorException set_props!(G, 11, Dict(:name => "gnode_3", :other_name => "something11"))
- @test_throws ErrorException set_props!(dG,11, Dict(:name => "dgnode_3", :other_name => "something11"))
+ @test_throws ErrorException set_props!(dG, 11, Dict(:name => "dgnode_3", :other_name => "something11"))
@test_throws KeyError get_prop(G, 11, :other_name)
@test_throws KeyError get_prop(dG, 11, :other_name)