Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
compound
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
cubicweb
cubes
compound
Commits
c6fa146ef147
Commit
c6fa146ef147
authored
8 years ago
by
Denis Laxalde
Browse files
Options
Downloads
Patches
Plain Diff
Make it possible to explicitly follow some relations while cloning
For instance, following non-composite relations. Closes #15770380.
parent
d37b90ee4885
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
__init__.py
+27
-20
27 additions, 20 deletions
__init__.py
entities.py
+5
-1
5 additions, 1 deletion
entities.py
test/data/entities.py
+12
-0
12 additions, 0 deletions
test/data/entities.py
test/test_compound.py
+22
-0
22 additions, 0 deletions
test/test_compound.py
with
66 additions
and
21 deletions
__init__.py
+
27
−
20
View file @
c6fa146e
...
...
@@ -68,10 +68,10 @@
self
.
skipetypes
=
skipetypes
def
parent_relations
(
self
,
etype
):
"""
Yield
composite
relation information items walking the graph
upstream
from `etype`.
"""
Yield
graph
relation information items walking the graph
upstream
from `etype`.
These items are `(rtype, role), parents` where `parents` is a list of
possible parent entity types reachable through `(rtype, role)`
relation.
"""
...
...
@@ -73,8 +73,8 @@
These items are `(rtype, role), parents` where `parents` is a list of
possible parent entity types reachable through `(rtype, role)`
relation.
"""
return
self
.
_
composite
_relations
(
etype
,
topdown
=
False
)
return
self
.
_
graph
_relations
(
etype
,
topdown
=
False
)
def
child_relations
(
self
,
etype
):
...
...
@@ -79,8 +79,8 @@
def
child_relations
(
self
,
etype
):
"""
Yield
composite
relation information items walking the graph
downstream
from `etype`.
"""
Yield
graph
relation information items walking the graph
downstream
from `etype`.
These items are `(rtype, role), children` where `children` is a list of
possible child entity types reachable through `(rtype, role)` relation.
"""
...
...
@@ -83,6 +83,6 @@
These items are `(rtype, role), children` where `children` is a list of
possible child entity types reachable through `(rtype, role)` relation.
"""
return
self
.
_
composite
_relations
(
etype
,
topdown
=
True
)
return
self
.
_
graph
_relations
(
etype
,
topdown
=
True
)
...
...
@@ -88,3 +88,3 @@
def
_
composite
_relations
(
self
,
etype
,
topdown
):
def
_
graph
_relations
(
self
,
etype
,
topdown
,
follow_relations
=
()
):
"""
Yield `(rtype, role), etypes` values corresponding to arcs of the
...
...
@@ -90,6 +90,7 @@
"""
Yield `(rtype, role), etypes` values corresponding to arcs of the
graph of compositely-related entity types reachable from a `etype`
entity type. `etypes` is a list of possible entity types reachable
graph of entity types reachable from a `etype` by following composite
relations and relations selected by `follow_relations` parameter at
initialization. `etypes` is a list of possible entity types reachable
through `(rtype, role)` relation
"
upstream
"
(resp.
"
downstream
"
) if
`topdown` is True (resp. False).
"""
...
...
@@ -100,9 +101,9 @@
for
rschema
,
teschemas
,
role
in
eschema
.
relation_definitions
():
if
rschema
.
meta
or
rschema
in
self
.
skiprtypes
:
continue
composite
_role
=
role
if
topdown
else
neg_role
(
role
)
target
_role
=
role
if
topdown
else
neg_role
(
role
)
relation
,
children
=
(
rschema
.
type
,
role
),
[]
for
target
in
teschemas
:
if
target
in
self
.
skipetypes
:
continue
rdef
=
rschema
.
role_rdef
(
eschema
,
target
,
role
)
...
...
@@ -104,9 +105,10 @@
relation
,
children
=
(
rschema
.
type
,
role
),
[]
for
target
in
teschemas
:
if
target
in
self
.
skipetypes
:
continue
rdef
=
rschema
.
role_rdef
(
eschema
,
target
,
role
)
if
rdef
.
composite
!=
composite_role
:
if
((
rschema
.
type
,
target_role
)
not
in
follow_relations
and
rdef
.
composite
!=
target_role
):
continue
children
.
append
(
target
.
type
)
if
children
:
...
...
@@ -145,5 +147,5 @@
These items are tuples `(child, (rtype, role), parent)` where `role` is
the role of `child` entity in `rtype` relation with `parent`.
"""
return
self
.
_
composite
_related
(
entity
,
False
)
return
self
.
_
graph
_related
(
entity
,
False
)
...
...
@@ -149,8 +151,8 @@
def
child_related
(
self
,
entity
):
def
child_related
(
self
,
entity
,
follow_relations
=
()
):
"""
Yield information items on entities related to `entity` through
composite relations walking the graph downstream from `entity`.
These items are tuples `(parent, (rtype, role), child)` where `role` is
the role of `parent` entity in `rtype` relation with `child`.
"""
...
...
@@ -151,8 +153,9 @@
"""
Yield information items on entities related to `entity` through
composite relations walking the graph downstream from `entity`.
These items are tuples `(parent, (rtype, role), child)` where `role` is
the role of `parent` entity in `rtype` relation with `child`.
"""
return
self
.
_composite_related
(
entity
,
True
)
return
self
.
_graph_related
(
entity
,
True
,
follow_relations
=
follow_relations
)
...
...
@@ -158,7 +161,9 @@
def
_composite_related
(
self
,
entity
,
topdown
,
_visited
=
None
):
"""
Yield arcs of the graph of compositely-related entities reachable
from `entity`.
def
_graph_related
(
self
,
entity
,
topdown
,
follow_relations
=
(),
_visited
=
None
):
"""
Yield arcs of the graph of made from entities reachable from
`entity` through composite relations or relations specified in
`follow_relations`.
An
"
arc
"
is a tuple `(l_entity, (rtype, role), r_entity)` where
`l_entity` is a
"
parent
"
(resp.
"
child
"
) entity when `topdown` is True
...
...
@@ -168,11 +173,11 @@
"""
if
_visited
is
None
:
_visited
=
set
()
for
(
rtype
,
role
),
targettypes
in
self
.
_
composite
_relations
(
entity
.
cw_etype
,
topdown
):
for
(
rtype
,
role
),
targettypes
in
self
.
_
graph
_relations
(
entity
.
cw_etype
,
topdown
,
follow_relations
=
follow_relations
):
rset
=
entity
.
related
(
rtype
,
role
=
role
,
targettypes
=
targettypes
)
for
target
in
rset
.
entities
():
yield
entity
,
(
rtype
,
role
),
target
if
target
.
eid
in
_visited
:
continue
_visited
.
add
(
target
.
eid
)
...
...
@@ -173,8 +178,10 @@
rset
=
entity
.
related
(
rtype
,
role
=
role
,
targettypes
=
targettypes
)
for
target
in
rset
.
entities
():
yield
entity
,
(
rtype
,
role
),
target
if
target
.
eid
in
_visited
:
continue
_visited
.
add
(
target
.
eid
)
for
x
in
self
.
_composite_related
(
target
,
topdown
,
_visited
):
for
x
in
self
.
_graph_related
(
target
,
topdown
,
follow_relations
=
follow_relations
,
_visited
=
_visited
):
yield
x
This diff is collapsed.
Click to expand it.
entities.py
+
5
−
1
View file @
c6fa146e
...
...
@@ -168,6 +168,8 @@
rtype
,
role
=
None
,
'
object
'
# relation between the clone and the original.
skiprtypes
=
()
skipetypes
=
()
# non-composite relations' (rtype, role) to explicitly follow.
follow_relations
=
()
clone_relations
=
{}
# registered relation type and role of the original entity.
# hooks categories that should be activated during cloning
enabled_hook_categories
=
[
'
metadata
'
]
...
...
@@ -196,6 +198,8 @@
assert
clone
.
cw_etype
==
self
.
entity
.
cw_etype
,
\
"
clone entity type {} does not match with original
'
s {}
"
.
format
(
clone
.
cw_etype
,
self
.
entity
.
cw_etype
)
related
=
self
.
graph
.
child_related
(
self
.
entity
,
follow_relations
=
self
.
follow_relations
)
with
self
.
_cw
.
deny_all_hooks_but
(
*
self
.
enabled_hook_categories
):
clone
.
copy_relations
(
self
.
entity
.
eid
)
clones
=
{
self
.
entity
:
clone
}
...
...
@@ -199,7 +203,7 @@
with
self
.
_cw
.
deny_all_hooks_but
(
*
self
.
enabled_hook_categories
):
clone
.
copy_relations
(
self
.
entity
.
eid
)
clones
=
{
self
.
entity
:
clone
}
for
parent
,
(
rtype
,
parent_role
),
child
in
self
.
graph
.
child_related
(
self
.
entity
)
:
for
parent
,
(
rtype
,
parent_role
),
child
in
related
:
rel
=
rtype
if
parent_role
==
'
object
'
else
'
reverse_
'
+
rtype
kwargs
=
{
rel
:
clones
[
parent
]}
clone
=
clones
.
get
(
child
)
...
...
This diff is collapsed.
Click to expand it.
test/data/entities.py
+
12
−
0
View file @
c6fa146e
from
cubicweb.predicates
import
has_related_entities
from
cubes.compound.entities
import
(
IClonableAdapter
,
IContained
,
IContainer
,
structure_def
)
...
...
@@ -10,6 +12,16 @@
rtype
=
'
clone_of
'
class
AgentInGroupIClonableAdapter
(
IClonableAdapter
):
"""
IClonable for Agent member of a Group, following `member` relation for
cloning.
"""
__select__
=
(
IClonableAdapter
.
__select__
&
has_related_entities
(
'
member
'
,
role
=
'
object
'
))
rtype
=
'
clone_of
'
follow_relations
=
[(
'
member
'
,
'
object
'
)]
def
registration_callback
(
vreg
):
vreg
.
register_all
(
globals
().
values
(),
__name__
)
vreg
.
register
(
IContainer
.
build_class
(
'
Agent
'
))
...
...
This diff is collapsed.
Click to expand it.
test/test_compound.py
+
22
−
0
View file @
c6fa146e
...
...
@@ -319,6 +319,28 @@
{
'
original
'
:
bob
.
eid
,
'
clone
'
:
clone
.
eid
})
self
.
assertTrue
(
rset
)
def
test_clone_follow_relations
(
self
):
with
self
.
admin_access
.
repo_cnx
()
as
cnx
:
bob
=
cnx
.
create_entity
(
'
Agent
'
,
name
=
u
'
bob
'
)
group
=
cnx
.
create_entity
(
'
Group
'
,
member
=
bob
)
cnx
.
create_entity
(
'
OnlineAccount
'
,
reverse_account
=
bob
)
cnx
.
commit
()
with
self
.
new_access
(
u
'
georges
'
).
repo_cnx
()
as
cnx
:
bob
=
cnx
.
find
(
'
Agent
'
,
name
=
u
'
bob
'
).
one
()
clone
=
cnx
.
create_entity
(
'
Agent
'
,
name
=
u
'
bobby
'
)
adapted
=
bob
.
cw_adapt_to
(
'
IClonable
'
)
self
.
assertEqual
(
adapted
.
__class__
.
__name__
,
'
AgentInGroupIClonableAdapter
'
)
adapted
.
clone_into
(
clone
)
clone
.
cw_clear_all_caches
()
cnx
.
commit
()
rset
=
cnx
.
execute
(
'
Any G WHERE G member A, A name
"
bobby
"
, OG eid %(og)s,
'
'
NOT G identity OG
'
,
{
'
og
'
:
group
.
eid
})
self
.
assertEqual
(
len
(
rset
),
1
)
self
.
assertEqual
(
clone
.
reverse_member
[
0
].
eid
,
rset
[
0
][
0
])
def
test_clone_full
(
self
):
with
self
.
admin_access
.
repo_cnx
()
as
cnx
:
agent
=
cnx
.
create_entity
(
'
Agent
'
,
name
=
u
'
bob
'
)
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment