Skip to content

Commit 684acfe

Browse files
committed
Gets, sets, and lists for sessions, robots, and users
1 parent 623bdd1 commit 684acfe

File tree

6 files changed

+172
-70
lines changed

6 files changed

+172
-70
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ docker pull neo4j
6969
To run the image with user `neo4j` and password `test`:
7070

7171
```bash
72-
docker run --publish=7474:7474 --publish=7687:7687 --env NEO4J_AUTH=neo4j/test neo4j
72+
docker run -d --publish=7474:7474 --publish=7687:7687 --env NEO4J_AUTH=neo4j/test neo4j
7373
```
7474

7575
> **Note** If you just installed docker and having permission issues, please see [this ask Ubuntu forum](https://askubuntu.com/questions/941816/permission-denied-when-running-docker-after-installing-it-as-a-snap).

src/CloudGraphsDFG/services/CGStructure.jl

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,21 @@ export createSession, createRobot, createUser, createDfgSessionIfNotExist
77
export existsSession, existsRobot, existsUser
88
export getSession, getRobot, getUser
99
export updateSession, updateRobot, updateUser
10-
export listSessions, listRobots, listUsers
10+
export lsSessions, lsRobots, lsUsers
11+
12+
global _invalidIds = ["USER", "ROBOT", "SESSION", "VARIABLE", "FACTOR", "ENVIRONMENT", "PPE", "BIGDATA"]
13+
global _validLabelRegex = r"^[a-zA-Z]\w*$"
14+
15+
function _isValid(id::Union{Symbol, String})::Bool
16+
if typeof(id) == Symbol
17+
id = String(id)
18+
end
19+
return all(t -> t != uppercase(id), _invalidIds) && match(_validLabelRegex, id) != nothing
20+
end
1121

1222
function _isValid(abstractNode::N)::Bool where N <: AbstractCGNode
13-
invalidIds = ["USER", "ROBOT", "SESSION", "VARIABLE", "FACTOR", "ENVIRONMENT"]
14-
return all(t -> t != uppercase(String(abstractNode.id)), invalidIds)
23+
id = String(abstractNode.id)
24+
return all(t -> t != uppercase(id), _invalidIds) && match(_validLabelRegex, id) != nothing
1525
end
1626

1727
# Fastest way I can think to convert the data into a dict
@@ -26,16 +36,37 @@ end
2636

2737
#TODO: Refactor, #HACK :D (but it works!)
2838
function _convertDictToSession(dict::Dict{String, Any})::Session
29-
sessionData = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
39+
data = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
3040
session = Session(
3141
Symbol(dict["id"]),
3242
Symbol(dict["robotId"]),
3343
Symbol(dict["userId"]),
3444
dict["name"],
3545
dict["description"],
36-
sessionData)
46+
data)
3747
return session
3848
end
49+
#TODO: Refactor, #HACK :D (but it works!)
50+
function _convertDictToRobot(dict::Dict{String, Any})::Robot
51+
data = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
52+
robot = Robot(
53+
Symbol(dict["id"]),
54+
Symbol(dict["userId"]),
55+
dict["name"],
56+
dict["description"],
57+
data)
58+
return robot
59+
end
60+
#TODO: Refactor, #HACK :D (but it works!)
61+
function _convertDictToUser(dict::Dict{String, Any})::User
62+
data = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
63+
user = User(
64+
Symbol(dict["id"]),
65+
dict["name"],
66+
dict["description"],
67+
data)
68+
return user
69+
end
3970

4071
function createUser(dfg::CloudGraphsDFG, user::User)::User
4172
Symbol(dfg.userId) != user.id && error("DFG user ID must match user's ID")
@@ -105,18 +136,106 @@ function createDfgSessionIfNotExist(dfg::CloudGraphsDFG)::Session
105136
end
106137
end
107138

108-
function listSessions(dfg::CloudGraphsDFG)::Vector{Session}
139+
"""
140+
$(SIGNATURES)
141+
List all sessions for the specified DFG's robot and user.
142+
Returns nothing if it isn't found.
143+
"""
144+
function lsSessions(dfg::CloudGraphsDFG)::Vector{Session}
109145
sessionNodes = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:SESSION:$(dfg.robotId):$(dfg.userId))")
110146
return map(s -> _convertDictToSession(Neo4j.getnodeproperties(s)), sessionNodes)
111147
end
112148

113-
function getSession(dfg::CloudGraphsDFG)::Union{Nothing, Session}
114-
sessionNode = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:SESSION:$(dfg.sessionId):$(dfg.robotId):$(dfg.userId))")
149+
"""
150+
$(SIGNATURES)
151+
List all robots for the specified DFG's user.
152+
Returns nothing if it isn't found.
153+
"""
154+
function lsRobots(dfg::CloudGraphsDFG)::Vector{Robot}
155+
robotNodes = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:ROBOT:$(dfg.userId))")
156+
return map(s -> _convertDictToRobot(Neo4j.getnodeproperties(s)), robotNodes)
157+
end
158+
159+
"""
160+
$(SIGNATURES)
161+
List all users.
162+
Returns nothing if it isn't found.
163+
"""
164+
function lsUsers(dfg::CloudGraphsDFG)::Vector{User}
165+
userNodes = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:USER)")
166+
return map(s -> _convertDictToUser(Neo4j.getnodeproperties(s)), userNodes)
167+
end
168+
169+
"""
170+
$(SIGNATURES)
171+
Get a session specified by userId:robotId:sessionId.
172+
Returns nothing if it isn't found.
173+
"""
174+
function getSession(dfg::CloudGraphsDFG, userId::Symbol, robotId::Symbol, sessionId::Symbol)::Union{Session, Nothing}
175+
!_isValid(userId) && error("Can't receive session with user ID '$(userId)'.")
176+
!_isValid(robotId) && error("Can't receive session with robot ID '$(robotId)'.")
177+
!_isValid(sessionId) && error("Can't receive session with session ID '$(sessionId)'.")
178+
sessionNode = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:SESSION:$(sessionId):$(robotId):$(userId))")
115179
length(sessionNode) == 0 && return nothing
116-
length(sessionNode) > 1 && error("There look to be $(length(sessionNode)) sessions identified for $(dfg.sessionId):$(dfg.robotId):$(dfg.userId)")
180+
length(sessionNode) > 1 && error("There look to be $(length(sessionNode)) sessions identified for $(sessionId):$(robotId):$(userId)")
117181
return _convertDictToSession(Neo4j.getnodeproperties(sessionNode[1]))
118182
end
119183

184+
"""
185+
$(SIGNATURES)
186+
Get the session specified by the DFG object.
187+
Returns nothing if it isn't found.
188+
"""
189+
function getSession(dfg::CloudGraphsDFG)::Union{Nothing, Session}
190+
return getSession(dfg, Symbol(dfg.userId), Symbol(dfg.robotId), Symbol(dfg.sessionId))
191+
end
192+
193+
"""
194+
$(SIGNATURES)
195+
Get a robot specified by userId:robotId.
196+
Returns nothing if it isn't found.
197+
"""
198+
function getRobot(dfg::CloudGraphsDFG, userId::Symbol, robotId::Symbol)::Union{Robot, Nothing}
199+
!_isValid(userId) && error("Can't receive session with user ID '$(userId)'.")
200+
!_isValid(robotId) && error("Can't receive session with robot ID '$(robotId)'.")
201+
robotNode = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:ROBOT:$(robotId):$(userId))")
202+
length(robotNode) == 0 && return nothing
203+
length(robotNode) > 1 && error("There look to be $(length(robotNode)) robots identified for $(robotId):$(userId)")
204+
return _convertDictToRobot(Neo4j.getnodeproperties(robotNode[1]))
205+
end
206+
207+
"""
208+
$(SIGNATURES)
209+
Get the robot specified by the DFG object.
210+
Returns nothing if it isn't found.
211+
"""
212+
function getRobot(dfg::CloudGraphsDFG)::Union{Nothing, Robot}
213+
return getRobot(dfg, Symbol(dfg.userId), Symbol(dfg.robotId))
214+
end
215+
216+
"""
217+
$(SIGNATURES)
218+
Get a user specified by userId.
219+
Returns nothing if it isn't found.
220+
"""
221+
function getUser(dfg::CloudGraphsDFG, userId::Symbol)::Union{User, Nothing}
222+
!_isValid(userId) && error("Can't receive session with user ID '$(userId)'.")
223+
userNode = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:USER:$(userId))")
224+
length(userNode) == 0 && return nothing
225+
length(userNode) > 1 && error("There look to be $(length(userNode)) robots identified for $(userId)")
226+
return _convertDictToUser(Neo4j.getnodeproperties(userNode[1]))
227+
end
228+
229+
"""
230+
$(SIGNATURES)
231+
Get the user specified by the DFG object.
232+
Returns nothing if it isn't found.
233+
"""
234+
function getUser(dfg::CloudGraphsDFG)::Union{Nothing, User}
235+
return getUser(dfg, Symbol(dfg.userId))
236+
end
237+
238+
120239
"""
121240
$(SIGNATURES)
122241
DANGER: Clears the whole session from the database.

src/CloudGraphsDFG/services/CloudGraphsDFG.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ function isFullyConnected(dfg::CloudGraphsDFG)::Bool
571571
# Total connected nodes - thank you Neo4j for 0..* awesomeness!!
572572
query = """
573573
match (n:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId):$(varIds[1]))-[FACTORGRAPH*]-(node:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId))
574-
WHERE n:VARIABLE OR n:FACTOR OR node:VARIABLE OR node:FACTOR
574+
WHERE (n:VARIABLE OR n:FACTOR OR node:VARIABLE OR node:FACTOR) and not (node:SESSION)
575575
WITH collect(n)+collect(node) as nodelist
576576
unwind nodelist as nodes
577577
return count(distinct nodes)"""
@@ -665,11 +665,9 @@ Note: By default orphaned factors (where the subgraph does not contain all the r
665665
function getSubgraphAroundNode(dfg::CloudGraphsDFG, node::DFGNode, distance::Int64=1, includeOrphanFactors::Bool=false, addToDFG::AbstractDFG=_getDuplicatedEmptyDFG(dfg))::AbstractDFG
666666
distance < 1 && error("getSubgraphAroundNode() only works for distance > 0")
667667

668-
# Making a copy session if not specified
669-
#moved to parameter addToDFG::AbstractDFG=_getDuplicatedEmptyDFG(dfg)
670-
671668
# Thank you Neo4j for 0..* awesomeness!!
672-
neighborList = _getLabelsFromCyphonQuery(dfg.neo4jInstance, "(n:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId):$(node.label))-[FACTORGRAPH*0..$distance]-(node:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId))")
669+
neighborList = _getLabelsFromCyphonQuery(dfg.neo4jInstance,
670+
"(n:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId):$(node.label))-[FACTORGRAPH*0..$distance]-(node:$(dfg.userId):$(dfg.robotId):$(dfg.sessionId)) WHERE (n:VARIABLE OR n:FACTOR OR node:VARIABLE OR node:FACTOR) and not (node:SESSION)")
673671

674672
# Copy the section of graph we want
675673
_copyIntoGraph!(dfg, addToDFG, neighborList, includeOrphanFactors)

test/CGStructureTests.jl

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
using DistributedFactorGraphs
2-
using IncrementalInference
3-
using Test
4-
51
dfg = CloudGraphsDFG{NoSolverParams}("localhost", 7474, "neo4j", "test",
6-
"Bob", "testRobot", "testSession",
2+
"testUser", "testRobot", "testSession",
73
nothing,
84
nothing,
95
IncrementalInference.decodePackedType,
@@ -12,24 +8,38 @@ dfg = CloudGraphsDFG{NoSolverParams}("localhost", 7474, "neo4j", "test",
128

139
# Nuke the user
1410
clearUser!!(dfg)
15-
@test listSessions(dfg) == []
11+
@test lsSessions(dfg) == []
12+
@test lsRobots(dfg) == []
13+
@test !(Symbol(dfg.userId) in map(u -> u.id, lsUsers(dfg)))
1614
# Create sentinel nodes using shortcut
1715
createDfgSessionIfNotExist(dfg)
18-
@test map(s -> s.id, listSessions(dfg)) == [:testSession]
16+
@test map(s -> s.id, lsSessions(dfg)) == [Symbol(dfg.sessionId)]
17+
@test map(s -> s.id, lsRobots(dfg)) == [Symbol(dfg.robotId)]
18+
@test Symbol(dfg.userId) in map(u -> u.id, lsUsers(dfg))
1919
# Test that we can call it again.
2020
createDfgSessionIfNotExist(dfg)
2121
# And nuke it so we can try the longer functions.
2222
clearUser!!(dfg)
2323

2424
# User, robot, and session
2525
# TODO: Make easier ways to initialize these.
26-
user = User(:Bob, "Bob Zack", "Description", Dict{Symbol, String}())
27-
robot = Robot(:testRobot, user.id, "Test robot", "Description", Dict{Symbol, String}())
28-
session = Session(:testSession, robot.id, user.id, "Test Session", "Description", Dict{Symbol, String}())
26+
user = User(Symbol(dfg.userId), "Bob Zack", "Description", Dict{Symbol, String}())
27+
robot = Robot(Symbol(dfg.robotId), user.id, "Test robot", "Description", Dict{Symbol, String}())
28+
session = Session(Symbol(dfg.sessionId), robot.id, user.id, "Test Session", "Description", Dict{Symbol, String}())
2929

3030
@test createUser(dfg, user) == user
3131
@test createRobot(dfg, robot) == robot
3232
@test createSession(dfg, session) == session
33+
@test map(s -> s.id, lsSessions(dfg)) == [Symbol(dfg.sessionId)]
34+
@test map(s -> s.id, lsRobots(dfg)) == [Symbol(dfg.robotId)]
35+
@test Symbol(dfg.userId) in map(u -> u.id, lsUsers(dfg))
36+
37+
# Test errors
38+
dfgError = deepcopy(dfg)
39+
# User/robot/session ID's can't start with numbers and can't have spaces.
40+
dfgError.userId = "1testNope"
41+
user = User(Symbol(dfgError.userId), "Bob Zack", "Description", Dict{Symbol, String}())
42+
@test_throws Exception createUser(dfgError, user)
3343

3444
@test getUserData(dfg) == Dict{Symbol, String}()
3545
@test getRobotData(dfg) == Dict{Symbol, String}()
@@ -54,7 +64,7 @@ v3 = addVariable!(dfg, :c, ContinuousScalar, labels = [:LANDMARK])
5464
f1 = addFactor!(dfg, [:a; :b], LinearConditional(Normal(50.0,2.0)) )
5565
f2 = addFactor!(dfg, [:b; :c], LinearConditional(Normal(50.0,2.0)) )
5666

57-
sessions = listSessions(dfg)
67+
sessions = lsSessions(dfg)
5868
@test map(s -> s.id, sessions) == [session.id]
5969

6070
# Pull and solve this graph
@@ -83,3 +93,5 @@ f2 = addFactor!(dfgOrphaned, [:b; :c], LinearConditional(Normal(50.0,2.0)) )
8393
dfgLocal = GraphsDFG{SolverParams}(params=SolverParams())
8494
DistributedFactorGraphs.getSubgraph(dfgOrphaned, union(ls(dfgOrphaned), lsf(dfgOrphaned)), true, dfgLocal)
8595
tree, smtasks = solveTree!(dfgLocal)
96+
97+
# If this passes without errors and we solve the graph, then all good.

test/runtests.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ using Pkg
77
## To run the IIF tests, you need a local Neo4j with user/pass neo4j:test
88
# To run a Docker image
99
# Install: docker pull neo4j
10-
# Run: docker run --publish=7474:7474 --publish=7687:7687 --env NEO4J_AUTH=neo4j/test neo4j
10+
# Run: docker run -d --publish=7474:7474 --publish=7687:7687 --env NEO4J_AUTH=neo4j/test neo4j
1111
##
1212

1313
# Test each interface
@@ -43,9 +43,9 @@ end
4343
end
4444

4545

46-
# if get(ENV, "IIF_TEST", "") == "true"
46+
if get(ENV, "IIF_TEST", "") == "true"
4747

48-
# Pkg.add("IncrementalInference")
48+
Pkg.add("IncrementalInference")
4949
# TODO: Remove this once we move to v0.5.0
5050
Pkg.add(PackageSpec(name="IncrementalInference", rev="enhancement/compare_move_dfg"))
5151
@info "------------------------------------------------------------------------"
@@ -74,6 +74,11 @@ end
7474
include("iifInterfaceTests.jl")
7575
end
7676
end
77-
# else
78-
# @warn "Skipping IncrementalInference driver tests"
79-
# end
77+
78+
@testset "CGStructure Tests for CGDFG" begin
79+
# Run the CGStructure tests
80+
include("CGStructureTests.jl")
81+
end
82+
else
83+
@warn "Skipping IncrementalInference driver tests"
84+
end

test/sandbox.jl

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,15 @@ logger = SimpleLogger(stdout, Logging.Debug)
99
global_logger(logger)
1010

1111
dfg = CloudGraphsDFG{SolverParams}("localhost", 7474, "neo4j", "test",
12-
"testUser", "testRobot", "sandbox",
12+
"1testUser", "testRobot", "testSession",
1313
nothing,
1414
nothing,
1515
IncrementalInference.decodePackedType,
1616
IncrementalInference.rebuildFactorMetadata!,
1717
solverParams=SolverParams())
18-
clearRobot!!(dfg)
18+
user = User(Symbol(dfg.userId), "Bob Zack", "Description", Dict{Symbol, String}())
19+
@test_throws Exception createUser(dfg, user) == user
1920

20-
numNodes = 10
21-
#change ready and backendset for x7,x8 for improved tests on x7x8f1
22-
verts = map(n -> addVariable!(dfg, Symbol("x$n"), ContinuousScalar, labels = [:POSE]), 1:numNodes)
23-
#TODO fix this to use accessors
24-
verts[7].ready = 1
25-
# verts[7].backendset = 0
26-
verts[8].ready = 0
27-
verts[8].backendset = 1
28-
#call update to set it on cloud
29-
updateVariable!(dfg, verts[7])
30-
updateVariable!(dfg, verts[8])
31-
32-
facts = map(n -> addFactor!(dfg, [verts[n], verts[n+1]], LinearConditional(Normal(50.0,2.0))), 1:(numNodes-1))
33-
34-
# Get neighbors tests
35-
@test getNeighbors(dfg, verts[1]) == [:x1x2f1]
36-
neighbors = getNeighbors(dfg, getFactor(dfg, :x1x2f1))
37-
@test neighbors == [:x1, :x2]
38-
# Testing aliases
39-
@test getNeighbors(dfg, getFactor(dfg, :x1x2f1)) == ls(dfg, getFactor(dfg, :x1x2f1))
40-
@test getNeighbors(dfg, :x1x2f1) == ls(dfg, :x1x2f1)
41-
42-
# ready and backendset
43-
@test getNeighbors(dfg, :x5, ready=1) == Symbol[]
44-
#TODO Confirm: test failed on GraphsDFG, don't know if the order is important for isa variable.
45-
@test symdiff(getNeighbors(dfg, :x5, ready=0), [:x4x5f1,:x5x6f1]) == []
46-
@test getNeighbors(dfg, :x5, backendset=1) == Symbol[]
47-
@test symdiff(getNeighbors(dfg, :x5, backendset=0),[:x4x5f1,:x5x6f1]) == []
48-
@test getNeighbors(dfg, :x7x8f1, ready=0) == [:x8]
49-
@test getNeighbors(dfg, :x7x8f1, backendset=0) == [:x7]
50-
@test getNeighbors(dfg, :x7x8f1, ready=1) == [:x7]
51-
@test getNeighbors(dfg, :x7x8f1, backendset=1) == [:x8]
52-
@test getNeighbors(dfg, verts[1], ready=0) == [:x1x2f1]
53-
@test getNeighbors(dfg, verts[1], ready=1) == Symbol[]
54-
@test getNeighbors(dfg, verts[1], backendset=0) == [:x1x2f1]
55-
@test getNeighbors(dfg, verts[1], backendset=1) == Symbol[]
21+
isFullyConnected(dfg)
22+
hasOrphans(dfg)
23+
dfgSubgraph = getSubgraphAroundNode(dfg, getVariable(dfg, :x1), 2)

0 commit comments

Comments
 (0)